How to Implement Download Files in NextJS using an API Route

Jasser Mark Arioste

Jasser Mark Arioste

How to Implement Download Files in NextJS using an API Route

Hello, hustlers! In this tutorial, you'll learn how to send files from an API route in NextJS 13 /appย and /api folders.ย 

Introduction #

It's common practice to store files to a third-party service like AWS S3, Digital Ocean Spaces, or Azure blob storage but we don't want the client to access the URL from those services directly. It's a best practice to create a proxy like an API route in your backend server.ย 

The diagram below illustrates the process described above:

NextJS Typical Download File Architecture.

Tutorial Objectives #

In this tutorial, you'll learn the following:

  1. Create an API route to send files using theย /app router of NextJS 13.
  2. Create an API route to send files using the /pages directory of NextJS 12.

This way, you'll have two options depending on what version of NextJS you are using. The implementation is different in both options but I prefer using the /app directory since it's more straightforward and I find the code more readable.

Option 1: Using the /app router #

First, let's create the route by creating the file /app/download/[filename]/route.ts. Your directory should look like this:

๐Ÿ“ app
  ๐Ÿ“ download
    ๐Ÿ“ [filename]

The route.ts file is a special file for route handlers in NextJS that indicates that a path is an API route. Here we can access the API through the following route for example: /api/download/dummy.pdf.

Here's how to implement the actual download:

type GetParams = {
  params: {
    filename: string;

// export an async GET function. This is a convention in NextJS
export async function GET(req: Request, { params }: GetParams) {
  // filename for the file that the user is trying to download
  const filename = params.filename;

  // external file URL
  const DUMMY_URL =

  // use fetch to get a response
  const response = await fetch(DUMMY_URL);

  // return a new response but use 'content-disposition' to suggest saving the file to the user's computer
  return new Response(response.body, {
    headers: {
      ...response.headers, // copy the previous headers
      "content-disposition": `attachment; filename="${filename}"`,

Now, once we go to /api/download/dummy.pdf, you should see the file being downloaded by the browser:

File downloaded through NextJS app route handlers.

Option 2: Using the /pages/api directory #

Next, let's implement file download using the /pages directory. If you're using Next 13 but haven't adapted to the new /app directory, then you can use this method. Otherwise, I recommend the previous method since it's much easier to implement.

I find that using axios package is easier in this scenario so let's install it first:

yarn add axios

First, create the file /pages/api/_download/[filename].ts. I used _download to avoid route conflict with the /app folder.

import axios from "axios";
import { NextApiRequest, NextApiResponse } from "next";
import { Readable } from "stream";

async function handler(req: NextApiRequest, res: NextApiResponse) {
  // get the filename for the file that the user is trying to download
  const filename = req.query.filename;

  // external file URL
  const DUMMY_URL =

  // use axios to get a Readable stream response
  const { data } = await axios.get<Readable>(DUMMY_URL, {
    responseType: "stream",

  res.setHeader("content-disposition", `attachment; filename="${filename}"`);

  // pipe the data to the res object

export default handler;


Here, the code is pretty similar to the first option, but instead of returning a Response object, we use .pipe to copy all the data from the stream to the res object.

Full Code #

You can access the full code at my GitHub repo: jmarioste/next-js-download-file

Conclusion #

You learned how to create download APIs using NextJS /app router and /pages/api routes.

If you like this tutorial, please leave a like or share this article. For future tutorials like this, please subscribe to our newsletter or follow me on GitHub.

Resources #

Credits:ย Image by Vero_Fasching from Pixabay

Share this post!

Related Posts


This content may contain links to products, software and services. Please assume all such links are affiliate links which may result in my earning commissions and fees.
As an Amazon Associate, I earn from qualifying purchases. This means that whenever you buy a product on Amazon from a link on our site, we receive a small percentage of its price at no extra cost to you. This helps us continue to provide valuable content and reviews to you. Thank you for your support!


Donate to ReactHustle