In this article, we would see how Fastify handles file upload as we build an image gallery using Fastify and React. We would work with Cloudinary and Fastify-multer a Fastify alternative to the express middleware multer.
Introduction
Fastify is a web development framework inspired by Hapi and Express but it promises faster performance with low overhead.
Fastify excels when it comes to building a fast HTTP server; performing nearly twice as fast as Express. It claims to be the fastest Nodejs frameworks and benchmark back up their claim.
Fastify is built to be a very modular system and it is fully extensible with hooks, plugins and, decorators.
Fastify uses a plugin-based architecture similar to that of Hapi and provides the register API for working with them. Â
Also, the Fastify plugin model is based on the reentrant lock and graph-based. And plugins in Fastify could be a set of routes, server decorators, etc.
In addition, Fastify supports middleware but starting from  v3.0.0, middleware is not supported out of the box. So external plugins such as fastify-express or middie are used to add middleware support.
Fastify provided alternatives to some express middlewares and uses Reusify to squeeze a 10% increase in performance when handling middleware.
In this article, we would see how Fastify handles file upload as we build an image gallery using Fastify and React. Â We would work with cloudinary and Fastify-multer a Fastify alternative to the express middleware multer.
Let’s get started with the prerequisite in the next section.
Prerequisite
To follow along in this article, here are a few prerequisites to note:
Since we want to set up cloudinary as we build our server it is important we set its environment variables now. Create a .env file in the root directory and add the following codes:
PORT=5000
MONGODB_URI=mongodb://localhost:27017/gallery
CLOUD_NAME=drqxiuhty // change this to your cloudinary cloud name
CLOUD_KEY=984987678826161 // change this to your cloudinary cloud key
CLOUD_SECRET=qEYQI7ERQpOIUJl765TTHb654KA // change this to your cloudinary secret
I have specified some values you need to change to the values of your Cloudinary account. The values given here would not work if used. You can get these values from your Cloudinary dashboard.
In the root directory, create a src directory with an index.js file containing the following code:
In the code above we have configured Cloudinary and created a Cloudinary image storage for multer.
Multer is an express middleware for handling multipart/form-data; it would not process any form which is not multipart and it is primarily used for uploading files.
Also, by default, Fastify does not parse multipart form data; it requires an external plugin like fasitfy-multipart or multer to add multipart support.
In this article, we are using fastify-multer which is a port of express multer and multer storage cloudinary — used to configure our Multer storage to Cloudinary.
Finally, we decorate Fastify with our image parser; this exposes the parser across our application. We will talk more about using the parser when we build our routes.
Create the status route and test the server:
In the src directory, create a routes directory and a status.js file inside.
Now we can test the server by running the codes below:
# Start server
npm start
#Test server
curl http://127.0.0.1:5000/status
# Server returns
{"date":"2021-04-24T21:25:28.860Z","status":"server is working"}
Our server is working so we will proceed to building our APIs in the next section.
Building our image gallery APIs
Create Models
In the src directory create a config directory with an index.js file and a models, directory. Inside the models directory, create a gallery.js file with the following codes:
The code above connects our app to MongodDB using mongoose and logs the connection status to the console and decorates Fastify with our model.
Create Upload Route Using Multer and Cloudinary
To create our endpoints, create a gallery.js file in the routes directory with the following codes:
const fp = require('fastify-plugin');
const gallery = async (server, opts) => {
server.route({
method: 'GET',
url: '/gallery',
handler: async (request, reply) => {
// request.file is the `avatar` file
// request.body will hold the text fields, if there were any
const { Gallery } = server.db.models;
const data = await Gallery.find({});
reply.code(200).send({ message: 'SUCCESS', data });
}
});
server.route({
method: 'POST',
url: '/gallery',
preHandler: server.multer.parser.single('upload'),
handler: async (request, reply) => {
// request.file is the `avatar` file
// request.body will hold the text fields, if there were any
const { Gallery } = server.db.models;
const image = new Gallery({
filename: request.file.filename,
originalname: request.file.originalname,
url: request.file.path
});
const data = await image.save();
reply.code(200).send({ message: 'SUCCESS', data });
}
});
};
module.exports = fp(gallery);
Our routes above are built using full declaration, and in the route option object, the handler function handles the request. In the post endpoint, we use the prehandler hook to handle the file upload using multer.
Register plugins and test server
To register our plugins add the following code below the // Register plugins below: in the index.js file as seen below:
Note we need to import our mongoose configuration. Add the following codes to the list of imported modules in the index.js file:
...
const multer = require('fastify-multer');
const db = require('./config/index');
const Port = process.env.PORT;
...
Not we can test the sever by running:
# start mongodb
mongod
# start application server
npm start
We get:
{"level":30,"time":1619300720736,"pid":15200,"hostname":"eagles-pcname","actor":"MongoDB","msg":"connected"}
{"level":30,"time":1619300720755,"pid":15200,"hostname":"eagles-pcname","msg":"Server listening at http://127.0.0.1:5000"}
The Gallery.js component above fetches data from our backend — http://127.0.0.1:5000/gallery once our component mounts. Then it modifies this data to an array of objects containing two keys namely: original and thumbnail. This data structure is required by the react-image-gallery library we are using in this project.
Lastly, it displays the image gallery if there are images and an error message — using the ErrorMessage component if there are no images.
Next, create the ErrorMessage  component by creating an Error.js file with the code  below:
Before we can successfully query our server we need to enable cors. To do  this we will use fastify-cors. Register the fastify-cors package as a plugin in our server — index.js:
...
const cors = require('fastify-cors');
const cloudinary = require('cloudinary').v2;
...
This configures the Access-Control-Allow-Origin CORS header to allow requests from localhost:3000.
We can start our React server now and our final app looks like this — note your uploaded images would be displayed instead:
fastify gallery
Conclusion
It is a real joy to work with Fastify. The developer experience is great and the plugin model is really cool. One great thing about Fastify is that it allows us to use express middlewares modules — which is a huge collection of matured modules.
In this article, we handled our image upload with fastify-multer which is a port of express multer middleware. Another alternative to multer is fastify multipart; a plugin to parse the multipart content-type.