Minimal example of file upload using Next.js app router

Minimal example of file upload using Next.js app router

Some old methods of accessing uploaded file contents inside a Next.js route handler don't work anymore or at least not in a way many tutorials explain it. E.g. many outdated online examples use page router which has route handlers that receive different types of arguments.

Following is a minimal proof of concept that you can extend upon to provide better UX and save or process the file, add error handling etc. The example is intentionally bare bones and not using any libraries to get down to the gist of things. It sets up a minimal example that accepts and reads a file.

HTML side

<input type="file" />
<script>
    document
      .querySelector('input[type="file"]')
      .addEventListener('change', function(event) {
          const file = event.target.files[0];
          const formData = new FormData();
          formData.append('file', file);
          fetch(
            '/api/upload-route',
            { method: 'POST', body: formData }
          );
      })
</script>

This creates a raw HTML input field with "onchange" event handler attached to it. When a file is selected, it automatically appends its content to FormData object as a file field and makes a POST request using it for the body of the request. By using FormData, we produce a multipart/form-data encoded POST request. Simply put, we are sending a specific type of message to the server that can have multiple parts(i.e. text fields, files, etc.) and they are separated by a specified boundary.

Next.js app route handler side

const readFile = (req: Request): Promise<string> =>
  new Promise(async (resolve, reject) => {
    const data = await req.formData();
    const file = data.get('file') as File | null;

    if (!file) {
      return reject('No file sent!');
    }

    const buffer = Buffer.from(await file.arrayBuffer());
    const content = buffer.toString('utf8');

    resolve(content);
  });

export async function POST(req: Request) {
  const content = await readFile(req);
  const responseBody = JSON.stringify({ content })
  return new Response(responseBody);
}

On the Next.js side, we reverse the process. We call req.formData() to get the FormData sent from the client side. Read a specific field named file by using data.get('file') and then load the binary file content into a new Buffer and decode the content as a utf8 string.
POST handler function just uses decoded file contents to loop them back to the client inside a response to this API call.

This example assumes you have a textual file being uploaded and you need to access its contents inside an API route handler function. In case the file is binary, you'd probably want to skip the "read" part and just save or transition the contents to some other place.

Thank you for reading and Godspeed!