Part 3: API Routes in Next.js

Part 3: API Routes in Next.js

Everything you need to know about Next.js

Overview

Welcome on board! In this series of articles, you'll learn everything you need to know about Next.js through project-based learning so you can get started with Next.js. If you have no idea what Next.js is, I highly recommend you to check out my newly released post here. Before we dive in, here is a sneak peek of what we're going to build:

2021-11-06 11-52-11.gif

If you've missed Part 2, I highly recommend you to check out Part 3 first before proceeding.

What is API routes?

API routes provide a seamless solution to build your API with Next.js. Next.js is literally a full-stack framework, it has a server, not just for development, for rendering components on your server, but also for an API! By definition, you can just ship an API next to your app with no extra configurations required.

All you have to do is navigate to your pages directory then api directory. Next.js has a simple API written for you which is the hello.js file.

/pages
  /api
    /hello.js

For example, the following API route localhost:3000/api/hello returns a JSON response with a status code of 200:

Untitled.png

Boom! You've just experienced the power of API in Next.js.

Setup API Handlers

Before starting to write an API, we would need to create a nested folder name file inside api directory:

/pages
  /api
    /file
    /hello.js

file.png

Inside file directory, you'll need to create 2 files name [id].js and index.js.

/pages
  /api
    /file
      /[id].js
      /index.js
    /hello.js

id.png

[id].js -> Represent the dynamic route. The id is the name of the parameter. You can name it whatever you want. Those brackets ([ ]) are not a typo, that's the syntax for creating a dynamic route using file name conventions in the file directory.

index.js -> We're telling Next.js we want this component to be the index route for this path. In our case, navigating to http://localhost:3000/api/file will render the data that we created in our mock database.

Writing API Handlers

A handler in Next.js should look like this:

export default (req, res) => {
  res.statusCode = 200
  res.setHeader('Content-Type', 'application/json')
  res.end(JSON.stringify({ message: 'hello' }))
}

But first, we'll need some way to use connect-based middleware, which would make building out these handlers much simpler.

In your terminal, run:

npm install next-connect
// or
yarn add next-connect

This command will install next-connect which is a library that enables developers to write minimal router and middleware layer for Next.js. Your package.json should look like this at this point:

{
  "name": "file-app",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "@emotion/react": "^11.5.0",
    "@emotion/styled": "^11.3.0",
    "@mui/material": "^5.0.6",
    "next": "12.0.2",
    "next-connect": "^0.11.0",  // Added next-connect
    "react": "17.0.2",
    "react-dom": "17.0.2"
  },
  "devDependencies": {
    "eslint": "7.32.0",
    "eslint-config-next": "12.0.2"
  }
}

It's time to create some API routes for our app. We'll need this API to do some basic CRUD (Create, Read, Update and Delete) operations.

Create file => POST /api/file
Read one file => GET /api/file/:id
Read all files => GET /api/file/
Update file => PATCH /api/file/:id
Delete file => DELETE /api/file/:id

In index.js, you'll need to create the handler like so:

// /pages/api/file/index.js

import nc from "next-connect";
import file_db from "../../../src/data/mock-db";

const handler = nc()
  .get((req, res) => {
    res.json({ data: file_db });
  })
  .post((req, res) => {
    const id = Date.now();
    const file = { ...req.body, id };

    file_db.push(file);
    res.json({ data: file });
  });

export default handler;

In [id].js, you'll then need to create another handler like so:

// /pages/api/file/[id].js

import nc from "next-connect";
import file_db from "../../../src/data/mock-db";

const getFile = (id) => file_db.find((n) => n.id === parseInt(id));

const handler = nc()
  .get((req, res) => {
    const file = getFile(req.query.id);

    if (!file) {
      res.status(404);
      res.end();
      return;
    }

    res.json({ data: file });
  })
  .patch((req, res) => {
    const file = getFile(req.query.id);

    if (!file) {
      res.status(404);
      res.end();
      return;
    }

    const i = file_db.findIndex((n) => n.id === parseInt.parse(req.query.id));
    const updated = { ...file, ...req.body };

    file_db[i] = updated;
    res.json({ data: updated });
  })
  .delete((req, res) => {
    const file = getFile(req.query.id);

    if (!file) {
      res.status(404);
      res.end();
      return;
    }
    const i = file_db.findIndex((n) => n.id === parseInt(req.query.id));

    file_db.splice(i, 1);

    res.json({ data: req.query.id });
  });

export default handler;

To test out if your API handler works, you'll need to navigate to this route http://localhost:3000/api/file:

localhost_3000_api_file (1).png

If the API handler you built is successful, you should be able to retrieve data from the mock database as shown in the picture.

Conclusion

We've successfully created an API for our app! The final article in this series I will be writing is about data fetching in Next.js. If you're interested in following up with this tutorial which I will be releasing soon, do consider following me on Hashnode. Happy learning!


Reference

Source code for this project

Learn Next.js

Next.js API Routes

next-connect