An Introduction on API Routes in Next.js

Updated on Jan 13, 2024


Table of Contents


What are API routes?

API routes are server-side functions that can handle requests and responses in Next.js. They are similar to Express or Fastify handlers, but they are integrated with the Next.js framework and have some advantages, such as:

  • They are automatically optimized for performance and scalability, thanks to the serverless architecture of Next.js.
  • They can access the same environment variables and modules as the rest of the Next.js application, making it easy to share code and configuration.
  • They can leverage the built-in request helpers that Next.js provides, such as req.cookies, req.query, and req.body, which simplify the parsing of the incoming request.
  • They can use any Node.js library or framework, such as Axios, Mongoose, or Passport, to enhance their functionality and integrate with external services.

API routes are ideal for building a public API with Next.js, or for creating server-side logic that complements the client-side rendering of the Next.js pages.

How to create API routes?

To create an API route in Next.js, you need to create a file inside the pages/api folder, and export a default function that receives two parameters: req and res. The req parameter is an instance of http.IncomingMessage, which represents the incoming request. The res parameter is an instance of http.ServerResponse, which represents the outgoing response.

For example, the following API route returns a JSON response with a status code of 200:

// pages/api/hello.js

export default function handler(req, res) {
  res.status(200).json({ message: 'Hello from Next.js!' });
}

You can name the API route file as you wish, as long as it is inside the pages/api folder. The file name will determine the URL of the API route, following the same convention as the Next.js pages. For example, if you create a file named pages/api/posts/[id].js, the API route will be mapped to /api/posts/:id, where :id is a dynamic parameter that can be accessed via req.query.id.

How to use HTTP methods?

To handle different HTTP methods in an API route, you can use req.method in your handler function, and write conditional logic based on its value. For example, the following API route can handle both GET and POST requests:

// pages/api/hello.js

export default function handler(req, res) {
  if (req.method === 'GET') {
    // Handle GET request
    res.status(200).json({ message: 'Hello from Next.js!' });
  } else if (req.method === 'POST') {
    // Handle POST request
    res.status(201).json({ message: 'You posted to Next.js!' });
  } else {
    // Handle any other HTTP method
    res.status(405).json({ message: 'Method not allowed' });
  }
}

How to parse the request body and query parameters?

Next.js provides some built-in request helpers that make it easy to parse the request body and query parameters in your API routes. These are:

  • req.cookies: An object containing the cookies sent by the request. Defaults to {}.
  • req.query: An object containing the query string. Defaults to {}.
  • req.body: An object containing the body parsed by content-type, or null if no body was sent. For example, the following API route can access the query parameters and the JSON body of the request:
// pages/api/hello.js

export default function handler(req, res) {
  if (req.method === 'POST') {
    // Get the query parameters
    const { name } = req.query;

    // Get the JSON body
    const { message } = req.body;

    // Send a response
    res.status(200).json({ name, message });
  }
}

By default, Next.js will parse the request body based on the content-type header, and support the following formats:

  • application/json
  • application/x-www-form-urlencoded
  • text/plain If you want to parse other formats, such as multipart/form-data, you need to use a custom middleware or a library like multer.

You can also disable the automatic body parsing if you want to consume the body as a stream or with a custom parser. To do that, you need to export a config object from your API route file, and set the api.bodyParser property to false. For example:

// pages/api/hello.js

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

export default function handler(req, res) {
  // ...
}

How to send JSON responses?

To send a JSON response from your API route, you can use the res.json() method, which accepts any JSON-serializable object as an argument, and sets the appropriate headers for the response. For example:

// pages/api/hello.js

export default function handler(req, res) {
  // Send a JSON response
  res.json({ message: 'Hello from Next.js!' });
}

You can also use the res.status() method to set the status code of the response, before calling the res.json() method. For example:

// pages/api/hello.js

export default function handler(req, res) {
  // Set the status code to 201
  res.status(201);

  // Send a JSON response
  res.json({ message: 'You posted to Next.js!' });
}

Alternatively, you can use the res.status().json() chain, which is equivalent to the above code. For example:

// pages/api/hello.js

export default function handler(req, res) {
  // Set the status code to 201 and send a JSON response
  res.status(201).json({ message: 'You posted to Next.js!' });
}

Examples of API routes

Now that we have learned the basics of creating and using API routes in Next.js, let’s see some examples of how we can use them for various purposes, such as fetching data from external sources, performing CRUD operations on a database, and validating user input.

Fetching data from an external API

One of the common use cases for API routes is to fetch data from an external API and return it to the client. This can be useful for several reasons, such as:

  • Hiding the API key or credentials from the client-side code.
  • Avoiding CORS issues when the external API does not support cross-origin requests.
  • Performing some transformations or filtering on the data before sending it to the client.

For example, the following API route fetches data from the Star Wars API and returns a list of characters:

// pages/api/characters.js

import axios from 'axios';

export default async function handler(req, res) {
  // Get the data from the external API
  const response = await axios.get('https://swapi.dev/api/people/');

  // Extract the results array
  const characters = response.data.results;

  // Send a JSON response
  res.json(characters);
}

This API route can be accessed by sending a GET request to /api/characters, and the response will be an array of objects, each representing a Star Wars character. By using this API route, we can fetch data from the Star Wars API without exposing our API key or dealing with CORS issues. We can also modify the data before sending it to the client, for example by filtering, sorting, or adding some custom properties.

Performing CRUD operations on a database

Another common use case for API routes is to perform CRUD (Create, Read, Update, Delete) operations on a database. This can be useful for building a RESTful API with Next.js, or for creating server-side logic for your Next.js pages.

To connect to a database from an API route, you can use any Node.js library or framework that supports your database of choice, such as Mongoose for MongoDB, Prisma for PostgreSQL, MySQL, or SQLite, or Knex for various SQL databases.

For example, the following API route uses Mongoose to connect to a MongoDB database and perform CRUD operations on a collection of posts:

// pages/api/posts.js

import mongoose from 'mongoose';
import Post from '../../models/Post';

// Connect to the database
mongoose.connect(process.env.MONGODB_URI, {
  useNewUrlParser: true,
  useUnifiedTopology: true,
});

export default async function handler(req, res) {
  // Get the HTTP method
  const method = req.method;

  // Handle different methods
  switch (method) {
    case 'GET':
      // Get all posts
      try {
        const posts = await Post.find({});
        res.status(200).json(posts);
      } catch (error) {
        res.status(500).json({ message: error.message });
      }
      break;
    case 'POST':
      // Create a new post
      try {
        const post = new Post(req.body);
        await post.save();
        res.status(201).json(post);
      } catch (error) {
        res.status(400).json({ message: error.message });
      }
      break;
    case 'PUT':
      // Update an existing post
      try {
        const { id, ...data } = req.body;
        const post = await Post.findByIdAndUpdate(id, data, { new: true });
        res.status(200).json(post);
      } catch (error) {
        res.status(404).json({ message: error.message });
      }
      break;
    case 'DELETE':
      // Delete an existing post
      try {
        const { id } = req.body;
        await Post.findByIdAndDelete(id);
        res.status(200).json({ message: 'Post deleted' });
      } catch (error) {
        res.status(404).json({ message: error.message });
      }
      break;
    default:
      // Handle any other HTTP method
      res.status(405).json({ message: 'Method not allowed' });
  }
}

By using this API route, we can perform CRUD operations on a database without exposing our database credentials or writing complex queries. We can also validate the request body and send appropriate responses based on the result of the operation.

Validating user input

A final example of using API routes is to validate user input and provide feedback or suggestions. This can be useful for creating interactive forms, chatbots, or games with Next.js.

To validate user input from an API route, you can use any Node.js library or framework that supports validation, such as Joi, Yup, or Validator.

For example, the following API route uses Joi to validate a user’s email and password, and returns a JSON response with the validation result:

// pages/api/validate.js

import Joi from 'joi';

// Define the validation schema
const schema = Joi.object({
  email: Joi.string().email().required(),
  password: Joi.string().min(8).max(32).required(),
});

export default async function handler(req, res) {
  // Get the user input
  const { email, password } = req.body;

  // Validate the input
  try {
    await schema.validateAsync({ email, password });
    res.status(200).json({ message: 'Input is valid' });
  } catch (error) {
    res.status(400).json({ message: error.message });
  }
}

By using this API route, we can validate user input and provide feedback or suggestions to the user, without reloading the page or writing complex client-side logic. We can also customize the validation schema and the response message according to our needs.

Conclusion

In this blog post, we have learned how to create API routes in Next.js, how to use different HTTP methods, how to parse the request body and query parameters, and how to send JSON responses. We have also seen some examples of using API routes for various purposes, such as fetching data from external sources, performing CRUD operations on a database, and validating user input.

API routes are a powerful feature of Next.js that allow us to build a public API with Next.js, or to create server-side logic that complements the client-side rendering of the Next.js pages. They are automatically optimized for performance and scalability, thanks to the serverless architecture of Next.js. They can also access the same environment variables and modules as the rest of the Next.js application, making it easy to share code and configuration. They can also use any Node.js library or framework to enhance their functionality and integrate with external services.

How did you like the article?

like

Thanks for reading! 🙏