How to handle Node.js file uploads with Fastify
Tags: ecosystems, engineering
Published: 2022-12-20 13:41:39

---
title: How to handle Node.js file uploads with Fastify
published: true
date: 2022-12-08 15:15:39 UTC
tags: Ecosystems,Engineering
canonical_url: https://snyk.io/blog/node-js-file-uploads-with-fastify/
---

File uploading is a commonly needed functionality in web applications. Because of this, many web frameworks provide ways to allow server-side applications to accept files and work with them. This is the same with [Fastify](https://www.fastify.io), which is a Node.js web framework that is often regarded as a successor to [Express](https://expressjs.com). It’s fast and highly focused on providing the best developer experience with a powerful plugin architecture and the least overhead possible.

In this tutorial, you’ll use the Fastify framework in a Node.js application to set up file uploading. You’ll learn how file uploads work on a typical server and how to handle file uploads on a Fastify server application.

## File uploading basics 

If you want to upload files to an application server, a client computer needs to make a POST request to the server. In the request body, it would send data that is typically submitted through HTML forms. HTML forms provide [three methods](https://html.spec.whatwg.org/multipage/forms.html#attr-fs-enctype) of encoding data:

- `application/x-www-form-urlencoded` (default) is the default content-type or encoding for HTML forms. However, when files are submitted using this form, it must be encoded using the `multipart/form-data` method.
- `multipart/form-data` is used to represent the multiple parts or fields present in an HTML form, with the parts sent as name/value pairs in the body of the request. The name and value pair correspond to the name and value of the input fields respectively.
- `text`/`plain` is used to send data in plain unformatted text, and cannot be used to send files.

When files are sent to an application server, the purpose is usually for the server to process the files, save them, or do both. Server applications can process and save uploaded files in a number of ways including:

- Saving them on the file system of your own server. This can be in a dedicated directory on the same server your application is running on or in a different server dedicated to storing files.
- Storing them in a database.
- Uploading the files to cloud storage like Amazon S3 or Cloudinary.

This tutorial focuses on using the first method to handle file uploads. 

## Implementing file uploading with Fastify

Before you begin this tutorial, you’ll need the following:

- Node.js installed
- A code editor of your choice, like Visual Studio Code (VS Code)
- [Postman](https://www.postman.com/), and you can either sign in and use the web app, or have it installed on your computer.

In the following sections, you’ll create a Fastify project, set up a file upload plugin, create an `/upload` route that can handle multiple file uploads, and write a test case for the route.

### Create a project and set up Fastify

To begin, you need to create a project and set up Fastify. Navigate to the directory where you wish to create this project and create a folder called `fastify-file-upload`. In your terminal, you can run the following command: 

```
mkdir fastify-file-upload
```

Change your directory to this folder:

```
cd fastify-file-upload
```

Then open the folder in your preferred code editor. If you’re using VS code, you can use the following command: 

```
code .
```

Initialize your project and create a `package.json` file by running the following:

```
npm init -y
```

Then, install the dependencies you’ll be using in this project:

```
npm install fastify @fastify/multipart form-auto-content tap
```

And create two files: `app.js` and `server.js`:

```
touch app.js server.js
```

`app.js` will contain your application code. Inside it, copy and paste the following code:

```
import Fastify from 'fastify'

export const app = Fastify({
    logger: true
  })
```

import Fastify from ‘fastify’

This code imports the Fastify framework and creates an instance of an app from it. Next, put the following code into your `server.js` file:

```
import { app } from './app.js'

const start = async () => {
    try {
      await app.listen({ port: 8000 })
      console.log(`server listening on ${app.server.address().port}`)
    } catch (err) {
        app.log.error(err)
      process.exit(1)
    }
  }
  start()
```

This code will serve your application when it’s run, or it returns an error if a problem occurs while trying to serve it.

In this project, you’ll be using ES6 modules syntax, therefore, your JavaScript files need to be loaded as modules. To make sure your JavaScript files are loaded as modules, add the property `type` to your `package.json` and set the value to `module`:

```
"type": "module"
```

### Set up a file upload plugin

[Fastify-multipart](https://github.com/fastify/fastify-multipart) provides support for [multipart content-types](https://learn.microsoft.com/en-us/previous-versions/office/developer/exchange-server-2010/aa493937(v=exchg.140)) in Fastify. Since you’ve already installed the plugin when you installed your dependencies, it’s time to set it up. To do this, you need to import `multipart` from `'@fastify/multipart'` and then register it. 

Add the lines starting with `+` to the content of your `app.js` file:

```
  import Fastify from 'fastify'
+ import multipart from '@fastify/multipart'

export const app = Fastify({
    logger: true
  })

+ app.register(multipart)
```

Here you registered multipart on the Fastify app you created earlier. Now your application instance can parse multipart content-type with ease.

### File uploads

There are several ways to access the file(s) from a POST request. For example, if you know a request contains only one file, you can access the file with the following code:

```
const data = await request.file()
```

In this code, `request` is the request parameter from the handler function for your upload route where your handle function looks something like this:

```
…
app.post('/upload', async function (request, reply) {

  const data = await request.file()

  // The rest of your code
}
```

If you’re expecting multiple files, the difference will be that you would call the `files()` method instead of `file()` like so:

```
const data = await request.files()
```

In your `app.js` file, you need to write code to fully implement multiple file uploading. But first, you need to create a directory where the files uploaded on your application will be stored. Create this directory at the root of your project and name it `uploads`: 

```
mkdir uploads
```

After that, make the following modifications to the `app.js` file:

```
import Fastify from 'fastify'
import multipart from '@fastify/multipart'
+ import fs from 'fs'
+ import util from 'util'
+ import { pipeline } from 'stream'

+ const pump = util.promisify(pipeline)

export const app = Fastify({
    logger: true
  })

  app.register(multipart)

+ app.post('/upload', async function (req, reply) {

+ const parts = req.files()
+ 
+ for await (const part of parts) {

+ // upload and save the file
+ await pump(part.file, fs.createWriteStream(`./uploads/${part.filename}`))
 
+ }

+ return {message : 'files uploaded' }
+ })
```

In the modified code, you imported functions from the core Node.js modules `fs`, `util`, and `stream`. Then you created an `/upload` route and its handler function. 

Inside the handler function, you used `req.files()` to access all the files from the request. You looped through the files, and for each of the files, the code `await pump(part.file, fs.createWriteStream(`./uploads/${part.filename}`))` uploads, and is saved to the upload directory you created earlier.

Please note that the files you’re uploading are not accumulated in memory before they’re saved. Instead, the `fs.createWriteStream` function is used to create a stream on the receiving end, while the file stream coming from `part.file` is piped to the newly created stream. This ensures that the content of the file is not first buffered in memory, rather, chunks of data are being encoded directly into the file where they’re saved as they come through the stream. This is especially helpful when uploading large files. It doesn’t block the event loop, and it’s also a memory efficient way to handle files on your application.

Now it’s time to run your server and send files to it using Postman. In your `package.json` file, replace the start script with the following:

```
"start": "node server.js"
```

Then start the server with the following:

```
npm start
```

Navigate to Postman and make a POST request to `http://localhost:8000/upload`. Under the body of the request you’re about to make, select the `form-data` option, and upload two files of your choice:

![Postman interface for uploading files](https://snyk.io/wp-content/uploads/blog-fastify-upload-postman-1240x775.png)
_Postman interface for uploading files_

Here, you upload one file named `article.txt` and another named `cover.jpg` (their keys will be “article” and “coverImage”, respectively). After uploading the files, you should get a status code of `200` and the return object `{message : 'files uploaded' }`:

![Two files uploaded from Postman](https://snyk.io/wp-content/uploads/blog-fastify-upload-field-files-1240x747.png)
_Two files uploaded from Postman_

You should also see the files added to the “upload” directory you created earlier for uploads.

At this point, you’ve successfully handled multiple file uploads using the `fastify/multipart` plugin. Most forms require you to provide not only files, but files and other text fields. In order to handle forms like this, you should modify your existing code to handle both file streams and other fields. 

To do so, make the following modifications to your `app.js` file:

```
import Fastify from 'fastify'
import multipart from '@fastify/multipart'
import fs from 'fs'
import util from 'util'
import { pipeline } from 'stream'

const pump = util.promisify(pipeline)

export const app = Fastify({
    logger: true
  })

  app.register(multipart)

  app.post('/upload', async function (req, reply) {

+ const parts = req.parts()

for await (const part of parts) {

+ if (part.file) {

    // upload and save the file
    await pump(part.file, fs.createWriteStream(`./uploads/${part.filename}`))

+ } else {
+ // do something with the non-files parts
+ }
  
}

    return {message : 'files uploaded' }
})
```

Notice that this time you’re using `req.parts()` to access both file streams and fields, instead of `req.files()`. And in the body of the loop, you’re using an `if` statement to determine which part is a file, and to handle them differently from fields.

Now, it’s time to make another request. Save your file, then stop and restart your server with `npm start`. Navigate back to Postman, delete the key/value pairs from the previous request, and create new ones. On the first field, provide your full name, and then provide any two files (as you did previously). 

**Note:** You may want to delete the files that were last uploaded to your upload directory if you intend to use the same files now. 

Your request should look like this:

![One field and two files uploaded from Postman](https://snyk.io/wp-content/uploads/blog-fastify-upload-field-files-1-1240x747.png)
_One field and two files uploaded from Postman_

And if it does, your application can now handle multiple file streams and fields.

### Write test cases

In order to protect your file upload implementation, you need to write tests for it. But, how can you write test cases for file upload in Fastify? Here, you’ll be using the testing framework [tap](https://www.npmjs.com/package/tap), the package [form-auto-content](https://github.com/Eomm/form-auto-content), and  Fastify’s `.inject` [method](https://www.fastify.io/docs/latest/Guides/Testing/) to write your test cases. 

Create the file `app.test.js` at the root of your project. Then copy and paste the following code into it:

```
'use strict'

import { test } from 'tap'
import formAutoContent from 'form-auto-content'
import fs from 'fs'
import { app } from './app.js'

const myForm = formAutoContent({
    fullName: 'Amarachi Aso',
    article: fs.createReadStream(`./sample_files/file.txt`),
    coverImage: fs.createReadStream(`./sample_files/cover.jpg`)
  })

test('submits form with multiple files in it', async t => {

    const res = await app.inject({
        method: 'post',
        url: '/upload',
        ...myForm
      })
  t.equal(res.statusCode, 200, 'returns a status code of 200')
})
```

Replace the file paths `./sample_files/file.txt` and `./sample_files/cover.jpg` with file paths to any two files in your filesystem. Or you can make a copy of these files from [here](https://github.com/Shulammite-Aso/fastify-file-upload/tree/main/sample_files).

The Fastify `.inject` method allows you to inject fake HTTP requests to your application without running your code on an actual server. In the previous code, notice how you used `formAutoContent` to create a form called `myForm` and then used this form as the request body for a fake POST request inside your test case. You only have one test case, and it expects that when it submits a form with multiple files in it, the application should return a `statusCode` of `200`.

Run this test and confirm that it passes. In your `package.json` file, replace the test script with this one:

```
"test": "tap --reporter=list --watch",
```

Then run the following:

```
npm test
```

Your test should pass and the output should look like this:

![Passing test](https://snyk.io/wp-content/uploads/blog-fastify-upload-term-1240x442.png)
_Passing test_

All the code for this tutorial is available on this [GitHub repo](https://github.com/Shulammite-Aso/fastify-file-upload).

## Conclusion

In this article, you learned about the basics of file uploading and why forms containing files are only encoded using the `multipart/form-data content-type`. You also learned about the two ways that server applications can process and save files either in a dedicated directory on the same server or uploaded to a cloud storage like Amazon S3. After that, you learned how to handle uploads in Fastify, a fast and low overhead web framework for Node.js

If you’re interested in learning more, check out the documentation for the [Fastify-multipart plugin](https://github.com/fastify/fastify-multipart) and from the [Fastify guide on testing](https://www.fastify.io/docs/latest/Guides/Testing/).