API Routes and middleware in NextJS

API Routes let you create an API endpoint inside a Next.js app. An endpoint that shall be available at localhost:3000/api/hello must have a file pages/api/hello.js with the following content:

// req = HTTP incoming message, res = HTTP server response
export default function handler(req, res) {
  res.status(200).json({ text: 'Hello' });
  // res.json(json) - Sends a JSON response.
  // res.send(body) - Sends the HTTP response
  // res.redirect([status,] path) - Redirects to a specified path or URL
}

To handle different HTTP methods:

export default function handler(req, res) {
  if (req.method === 'POST') {
    // Process a POST request
  } else {
    // Handle any other HTTP method
  }
}

Dynamic API routes

API routes support dynamic routes, and follow the same file naming rules used for pages. For example, the API route pages/api/post/[pid].js has the following code:

export default function handler(req, res) {
  const { pid } = req.query
  res.end(`Post: ${pid}`)
}

Catch-all API routes

pages/api/post/[...slug].js matches /api/post/a, but also /api/post/a/b, /api/post/a/b/c and so on, but it does not match /api/post. For that, you would either need pages/api/post/[[...slug]].js (“optional catch all”), /api/posts.js or /api/posts/index.js.

Do’s and dont’s

You should not fetch an API Route from getStaticProps or getStaticPaths. Instead, write your server-side code directly in getStaticProps or getStaticPaths (or call a helper function).

A good use case for API Routes is handling form input. For example, you can create a form on your page and have it send a POST request to your API Route. You can then write code to directly save it to your database. The API Route code will not be part of your client bundle, so you can safely write server-side code. API Routes can be dynamic, just like regular pages.

API Middleware

Use them to read and/or further modify incoming requests, such as req.cookies, req.query or req.body.

Every API route can export a config object to change the default configs:

export const config = {
  api: {
    bodyParser: {
      sizeLimit: '1mb',
    },
  },
}

bodyParser Enables body parsing, you can disable it if you want to consume it as a Stream:

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

Enabling CORS

npm i cors
# or
yarn add cors
import Cors from 'cors'

// Initializing the cors middleware
const cors = Cors({
  methods: ['GET', 'HEAD'],
})

// Helper method to wait for a middleware to execute before continuing
// And to throw an error when an error happens in a middleware
function runMiddleware(req, res, fn) {
  return new Promise((resolve, reject) => {
    fn(req, res, (result) => {
      if (result instanceof Error) {
        return reject(result)
      }

      return resolve(result)
    })
  })
}

async function handler(req, res) {
  // Run the middleware
  await runMiddleware(req, res, cors)

  // Rest of the API logic
  res.json({ message: 'Hello Everyone!' })
}

export default handler

TypeScript and API Routes

You can read how to extend req and res objects with TypeScript on the offical doc page.

About Author

Mathias Bothe To my job profile

I am Mathias, born 39 years ago in Heidelberg, Germany. Today I am living in Munich and Stockholm. I am a passionate IT freelancer with more than 15 years experience in programming, especially in developing web based applications for companies that range from small startups to the big players out there. I am founder of bosy.com, creator of the security service platform BosyProtect© and initiator of several other software projects.