npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

next-api-middleware

v3.0.0

Published

Middleware solution for Next.js API routes

Downloads

19,723

Readme

Introduction

⚠️ This library was written to support API routes that use the Next.js Pages Router. It has not been tested with the App Router.

Next.js API routes are a ridiculously fun and simple way to add backend functionality to a React app. However, when it comes time to add middleware, there is no easy way to implement it.

The official Next.js docs recommend writing functions inside your API route handler. This is a huge step backward compared to the clean APIs provided by Express.js or Koa.js.

This library attempts to provide minimal, clean, composable middleware patterns that are both productive and pleasant to use.

Table of Contents

Quick Start

import { label, Middleware } from "next-api-middleware";
import * as Sentry from "@sentry/nextjs";
import nanoid from "nanoid";

// 1 – Create middleware functions

const captureErrors: Middleware = async (req, res, next) => {
  try {
    // Catch any errors that are thrown in remaining
    // middleware and the API route handler
    await next();
  } catch (err) {
    const eventId = Sentry.captureException(err);

    res.status(500);
    res.json({ error: err });
  }
};

const addRequestId: Middleware = async (req, res, next) => {
  // Let remaining middleware and API route execute
  await next();

  // Apply header
  res.setHeader("X-Response-ID", nanoid());
};

// 2 – Use `label` to assemble all middleware

const withMiddleware = label(
  {
    addRequestId,
    sentry: captureErrors, // <-- Optionally alias middleware
  },
  ["sentry"] // <-- Provide a list of middleware to call automatically
);

// 3 – Define your API route handler

const apiRouteHandler = async (req, res) => {
  res.status(200);
  res.send("Hello world!");
};

// 4 – Choose middleware to invoke for this API route

export default withMiddleware("addRequestId")(apiRouteHandler);

How It Works

My mental model for how this library handles middleware functions is that of a "winding and unwinding stack."

Let's imagine you've used label to add two middleware functions to an API route.

When a request comes in, this is a rough impression of how that request makes its way through all middleware functions, the API route handler itself, and then back up through the middleware.

              |-----------------|-----------------|--------------------|
              |  Middleware #1  |  Middleware #2  | API Route Handler  |
              |-----------------|-----------------|--------------------|
              |                 |                 |                    |
Request ------|----> Setup -----|----> Setup -----|-->------|          |
              |                 |                 |         |          |
              |-----------------|-----------------|         V          |
              |                 |                 |                    |
              |   await next()  |   await next()  |     API stuff      |
              |                 |                 |                    |
              |-----------------|-----------------|         |          |
              |                 |                 |         |          |
Response <----|--- Teardown <---|--- Teardown <---|---------|          |
              |                 |                 |                    |
              |-----------------|-----------------|--------------------|

While this is a crummy ASCII diagram, I think it gives the right impression. The request winds its way though each middleware function in succession, hits the API route handler, and then proceeds to "unwind" its way through the stack.

Every middleware function has the opportunity to go through three phases:

  1. Setup
  2. Waiting
  3. Teardown

The "Setup" phase covers everything that happens before await next(). The "Waiting" phase is really just await next(). The "Teardown" phase is the remaining code within a middleware function after await next().

It is worth noting that although these phases are available to all middleware functions, you don't need to take advantage of them all.

For example, in error catching middleware you might simply wrap await next() in a try / catch block. On the other hand, you might have request timing middleware that captures a start time during the setup phase, waits, and then captures a finish time in the teardown phase.

APIs

label

This is the primary utility for creating reusuable collections of middleware for use throughout many Next.js API routes.

const withMiddleware = label(middleware, defaults);

Parameters

  • middleware: an object containing middleware functions or arrays of middleware
  • defaults: (optional) an array of middleware keys that will be invoked automatically

Return Value

label returns a function (conventionally referred to as withMiddleware) that uses currying to accept a list of middleware names to be invoked, followed by a Next.js API handler function.

Typically, withMiddleware will be imported in API route files and used at the default export statement:

import { withMiddleware } from "../helpers/my-middleware";

const apiRouteHandler = async (req, res) => {
  ...
}

export default withMiddleware("foo", "bar", "baz")(apiRouteHandler);

Though label could contain many middleware functions, the actual middleware invoked by an API route is determined by the names passed in to withMiddleware.

Examples

Basic Use
const logErrors = async (req, res, next) => {
  try {
    await next();
  } catch (error) {
    console.error(error);
    res.status(500);
    res.json({ error });
  }
};

const withMiddleware = label({
  logErrors,
});

// export default withMiddleware("logErrors")(apiRouteHandler);
Aliases
const withMiddleware = label({
  error: logErrors,
});

// export default withMiddleware("error")(apiRouteHandler);
Groups
import { foo, bar, baz } from "./my-middleware";

const withMiddleware = label({
  error: logErrors,
  myGroup: [foo, bar, baz],
});

// export default withMiddleware("error", "myGroup")(apiRouteHandler);
Defaults
const withMiddleware = label(
  {
    error: logErrors,
    myGroup: [foo, bar, baz],
  },
  ["error"]
);

// export default withMiddleware("myGroup")(apiRouteHandler);

use

This utility accepts middleware functions directly and executes them all in order. It is a simpler alternative to label that can be useful for handling one-off middleware functions.

const withInlineMiddleware = use(...middleware);

Parameters

  • middleware: a list of middleware functions and/or arrays of middleware functions

Return Value

use returns a function that accepts a Next.js API route handler.

Examples

CORS
import { use } from "next-api-middleware";
import cors from "cors";

const apiRouteThatOnlyNeedsCORS = async (req, res) => {
  ...
}

export default use(cors())(apiRouteThatOnlyNeedsCORS);

Usage Guide

See EXAMPLES.md for more detailed examples of label and use.

Advanced

Middleware Factories

Since use and label accept values that evaluate to middleware functions, this provides the opportunity to create custom middleware factories.

Here's an example of a factory that generates a middleware function to only allow requests with a given HTTP method:

import { Middleware } from "next-api-middleware";

const httpMethod = (
  allowedHttpMethod: "GET" | "POST" | "PATCH"
): Middleware => {
  return async function (req, res, next) {
    if (req.method === allowedHttpMethod || req.method == "OPTIONS") {
      await next();
    } else {
      res.status(404);
      res.end();
    }
  };
};

export const postRequestsOnlyMiddleware = httpMethod("POST");

Middleware Signature

Middleware is inspired by the asyncronous middleware style popularized by Koa.js.

type Middleware<Request = NextApiRequest, Response = NextApiResponse> = (
  req: Request,
  res: Response,
  next: () => Promise<void>
) => Promise<void>;

Alternatives