How to Implement Download Files in NextJS using an API Route
Jasser Mark Arioste
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:
Tutorial Objectives #
In this tutorial, you'll learn the following:
- Create an API route to send files using the
/app
router of NextJS 13. - 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] route.ts
123
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 = "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"; // 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}"`, }, }); }
1234567891011121314151617181920212223242526
Now, once we go to /api/download/dummy.pdf
, you should see the file being downloaded by the browser:
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
1
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 = "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"; // 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 data.pipe(res); } export default handler;
123456789101112131415161718192021222324
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