Security News
Weekly Downloads Now Available in npm Package Search Results
Socket's package search now displays weekly downloads for npm packages, helping developers quickly assess popularity and make more informed decisions.
next-api-middleware
Advanced tools
⚠️ 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.
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);
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:
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.
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);
middleware
: an object containing middleware functions or arrays of middlewaredefaults
: (optional) an array of middleware
keys that will be invoked automaticallylabel
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
.
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);
const withMiddleware = label({
error: logErrors,
});
// export default withMiddleware("error")(apiRouteHandler);
import { foo, bar, baz } from "./my-middleware";
const withMiddleware = label({
error: logErrors,
myGroup: [foo, bar, baz],
});
// export default withMiddleware("error", "myGroup")(apiRouteHandler);
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);
middleware
: a list of middleware functions and/or arrays of middleware functionsuse
returns a function that accepts a Next.js API route handler.
import { use } from "next-api-middleware";
import cors from "cors";
const apiRouteThatOnlyNeedsCORS = async (req, res) => {
...
}
export default use(cors())(apiRouteThatOnlyNeedsCORS);
See EXAMPLES.md for more detailed examples of label
and use
.
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
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>;
FAQs
Middleware solution for Next.js API routes
The npm package next-api-middleware receives a total of 4,710 weekly downloads. As such, next-api-middleware popularity was classified as popular.
We found that next-api-middleware demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Socket's package search now displays weekly downloads for npm packages, helping developers quickly assess popularity and make more informed decisions.
Security News
A Stanford study reveals 9.5% of engineers contribute almost nothing, costing tech $90B annually, with remote work fueling the rise of "ghost engineers."
Research
Security News
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.