Upload files with NextJS + Fetch + Api routes + Typescript
Tags: react, nextjs, typescript, javascript
Published: 2022-05-18 16:07:21

You will learn, how to upload files using React, NextJs and Fetch API.

---

First of all, let’s create NextJs application by running:

```bash
yarn create next-app --typescript
```

After the installation is complete, run yarn dev to start the development server on http://localhost:3000 .

If you open the page, you will see something similar to the following:

![NextJs welcome screen](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nd0yuu2h17ytdnpb6790.png)

Now let's install the packages, which will help us to parse form data:

```bash
yarn add -D formidable @types/formidable
```

---

## Creating the form

We will create small form, where we can upload files. I will simplify the code to make it more clear.

Go to `pages/index.js` and replace all the default code with the following:

```js
import type { NextPage } from 'next'
import React from "react";

const Home: NextPage = () => {

    const [isLoading, setIsLoading] = React.useState(false);
    const inputFileRef = React.useRef<HTMLInputElement | null>(null);

    const handleOnClick = async (e: React.MouseEvent<HTMLInputElement>) => {

        /* Prevent form from submitting by default */
        e.preventDefault();

        /* If file is not selected, then show alert message */
        if (!inputFileRef.current?.files?.length) {
            alert('Please, select file you want to upload');
            return;
        }

        setIsLoading(true);

        /* Add files to FormData */
        const formData = new FormData();
        Object.values(inputFileRef.current.files).forEach(file => {
            formData.append('file', file);
        })

        /* Send request to our api route */
        const response = await fetch('/api/upload', {
            method: 'POST',
            body: formData
        });

        const body = await response.json() as { status: 'ok' | 'fail', message: string };

        alert(body.message);

        if (body.status === 'ok') {
            inputFileRef.current.value = '';
            // Do some stuff on successfully upload
        } else {
            // Do some stuff on error
        }

        setIsLoading(false);
    };

    return (
        <form>
            <div>
                <input type="file" name="myfile" ref={inputFileRef} multiple />
            </div>
            <div>
                <input type="submit" value="Upload" disabled={isLoading} onClick={handleOnClick} />
                {isLoading && ` Wait, please...`}
            </div>
        </form>
    )
}

export default Home
```

Here we are using multiple files uploading. If you want to upload only one file, remove `multiple` from `input:file`:

```js
<input type="file" name="myfile" ref={inputFileRef} />
```

---

## API Route

Let’s create an endpoint, where we will process the uploaded files.

Create the `filepages/api/upload.ts`:

```js
import type { NextApiRequest, NextApiResponse } from 'next'
import { promises as fs } from "fs";
import path from "path";
import formidable, { File } from 'formidable';

/* Don't miss that! */
export const config = {
    api: {
        bodyParser: false,
    }
};

type ProcessedFiles = Array<[string, File]>;

const handler = async (req: NextApiRequest, res: NextApiResponse) => {

    let status = 200,
        resultBody = { status: 'ok', message: 'Files were uploaded successfully' };

    /* Get files using formidable */
    const files = await new Promise<ProcessedFiles | undefined>((resolve, reject) => {
        const form = new formidable.IncomingForm();
        const files: ProcessedFiles = [];
        form.on('file', function (field, file) {
            files.push([field, file]);
        })
        form.on('end', () => resolve(files));
        form.on('error', err => reject(err));
        form.parse(req, () => {
            //
        });
    }).catch(e => {
        console.log(e);
        status = 500;
        resultBody = {
            status: 'fail', message: 'Upload error'
        }
    });

    if (files?.length) {

        /* Create directory for uploads */
        const targetPath = path.join(process.cwd(), `/uploads/`);
        try {
            await fs.access(targetPath);
        } catch (e) {
            await fs.mkdir(targetPath);
        }

        /* Move uploaded files to directory */
        for (const file of files) {
            const tempPath = file[1].filepath;
            await fs.rename(tempPath, targetPath + file[1].originalFilename);
        }
    }

    res.status(status).json(resultBody);
}

export default handler;
```

Congratulations, we’re done! You can test your form!

All files will be saved in directory `/uploads` inside your project root.

---

## Use Api route as proxy

Here I suggest you to assume, that we need to transfer our files somewhere else to another server, because we don’t want to store them and doing difficult operations on files.

Add some new packages:

```bash
yarn add -D form-data node-fetch
```

Change you `/pages/api/upload.ts to`:

```js
import type { NextApiRequest, NextApiResponse } from 'next'
import fs from "fs";
import fetch from "node-fetch";
import FormData from 'form-data';
import formidable, { File } from 'formidable';

export const config = {
    api: {
        bodyParser: false,
    }
};

type ProcessedFiles = Array<[string, File]>;

const handler = async (req: NextApiRequest, res: NextApiResponse) => {

    let status = 200,
        resultBody = { status: 'ok', message: 'Files were uploaded successfully' };

    /* Get files using formidable */
    const files = await new Promise<ProcessedFiles | undefined>((resolve, reject) => {
        const form = new formidable.IncomingForm();
        const files: ProcessedFiles = [];
        form.on('file', function (field, file) {
            files.push([field, file]);
        })
        form.on('end', () => resolve(files));
        form.on('error', err => reject(err));
        form.parse(req, () => {
            //
        });
    }).catch(e => {
        console.log(e);
        status = 500;
        resultBody = {
            status: 'fail', message: 'Upload error'
        }
    });

    if (files?.length) {

        /* Add files to FormData */
        const formData = new FormData();
        for (const file of files) {
            formData.append(file[0], fs.createReadStream(file[1].filepath));
        }

        /* Send request to another server */
        const response = await fetch('PATH_TO_ANOTHER_SERVER', {
            headers: formData.getHeaders(),
            method: 'POST',
            body: formData
        });

        // Do anything you need with response

    }

    res.status(status).json(resultBody);
}

export default handler;
```

Don’t forget to replace `PATH_TO_ANOTHER_SERVER`.

---

This is it, enjoy! Full repo https://github.com/gapon2401/upload-files-nextjs