Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork7
Middleware for API routes using the Next.js Pages Router
License
htunnicliff/next-api-middleware
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
⚠️ This library was written to support API routes that use the Next.jsPages Router. It has not been tested with theApp 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 functionsinside 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*asSentryfrom"@sentry/nextjs";importnanoidfrom"nanoid";// 1 – Create middleware functionsconstcaptureErrors:Middleware=async(req,res,next)=>{try{// Catch any errors that are thrown in remaining// middleware and the API route handlerawaitnext();}catch(err){consteventId=Sentry.captureException(err);res.status(500);res.json({error:err});}};constaddRequestId:Middleware=async(req,res,next)=>{// Let remaining middleware and API route executeawaitnext();// Apply headerres.setHeader("X-Response-ID",nanoid());};// 2 – Use `label` to assemble all middlewareconstwithMiddleware=label({ addRequestId,sentry:captureErrors,// <-- Optionally alias middleware},["sentry"]// <-- Provide a list of middleware to call automatically);// 3 – Define your API route handlerconstapiRouteHandler=async(req,res)=>{res.status(200);res.send("Hello world!");};// 4 – Choose middleware to invoke for this API routeexportdefaultwithMiddleware("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 usedlabel 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:
- Setup
- Waiting
- Teardown
The "Setup" phase covers everything that happens beforeawait next(). The "Waiting" phase is really justawait next(). The "Teardown" phase is the remaining code within a middleware function afterawait 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 wrapawait next() in atry / 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.
This is the primary utility for creating reusuable collections of middleware for use throughout many Next.js API routes.
constwithMiddleware=label(middleware,defaults);
middleware: an object containing middleware functions or arrays of middlewaredefaults: (optional) an array ofmiddlewarekeys that will be invoked automatically
label returns a function (conventionally referred to aswithMiddleware) 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";constapiRouteHandler=async(req,res)=>{ ...}exportdefaultwithMiddleware("foo","bar","baz")(apiRouteHandler);
Thoughlabel could contain many middleware functions, the actual middleware invoked by an API route is determined by the names passed in towithMiddleware.
constlogErrors=async(req,res,next)=>{try{awaitnext();}catch(error){console.error(error);res.status(500);res.json({ error});}};constwithMiddleware=label({ logErrors,});// export default withMiddleware("logErrors")(apiRouteHandler);
constwithMiddleware=label({error:logErrors,});// export default withMiddleware("error")(apiRouteHandler);
import{foo,bar,baz}from"./my-middleware";constwithMiddleware=label({error:logErrors,myGroup:[foo,bar,baz],});// export default withMiddleware("error", "myGroup")(apiRouteHandler);
constwithMiddleware=label({error:logErrors,myGroup:[foo,bar,baz],},["error"]);// export default withMiddleware("myGroup")(apiRouteHandler);
This utility accepts middleware functions directly and executes them all in order. It is a simpler alternative tolabel that can be useful for handling one-off middleware functions.
constwithInlineMiddleware=use(...middleware);
middleware: a list of middleware functions and/or arrays of middleware functions
use returns a function that accepts a Next.js API route handler.
import{use}from"next-api-middleware";importcorsfrom"cors";constapiRouteThatOnlyNeedsCORS=async(req,res)=>{ ...}exportdefaultuse(cors())(apiRouteThatOnlyNeedsCORS);
SeeEXAMPLES.md for more detailed examples oflabel anduse.
Sinceuse andlabel 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";consthttpMethod=(allowedHttpMethod:"GET"|"POST"|"PATCH"):Middleware=>{returnasyncfunction(req,res,next){if(req.method===allowedHttpMethod||req.method=="OPTIONS"){awaitnext();}else{res.status(404);res.end();}};};exportconstpostRequestsOnlyMiddleware=httpMethod("POST");
Middleware is inspired by the asyncronous middleware style popularized by Koa.js.
typeMiddleware<Request=NextApiRequest,Response=NextApiResponse>=(req:Request,res:Response,next:()=>Promise<void>)=>Promise<void>;
About
Middleware for API routes using the Next.js Pages Router
Topics
Resources
License
Code of conduct
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Sponsor this project
Uh oh!
There was an error while loading.Please reload this page.
Packages0
Uh oh!
There was an error while loading.Please reload this page.
Contributors3
Uh oh!
There was an error while loading.Please reload this page.