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

nextjs-middleware-chain

v0.0.8

Published

Next.js middleware library for API and SSR routes.

Downloads

178

Readme

Next.js Middleware Chain

Next.js Middleware Chain (NMC) is a lightweight, zero-dependency next.js middleware solution. It is built on the idea that middleware functions are more flexible when they can be chained as needed during development.

NMC aims to provide a simple framework for writing methods that can be used with either Next.js API routes or SSR (getServerSideProps) data fetching.

This library is still in an early stage of development. It has been tested it in a couple projects and should be stable, however there may be bugs or scenarios that have not been realized or planned for.

If you encounter a bug or problem, please open an issue.

If you have feedback or questions, please start a discussion.

Please be civil and abide by the code of conduct.

Why

Next.js does a lot of great things. Middleware is not one of them. If you want to implement some functionality for all API routes (logging at start & finish, for instance) you have to either explicitly add it to all your routes or write your own reusable solution.

This gets doubly complex if you want to re-use the same functionality across API Routes and SSR data fetching methods, since the function APIs and behaviors differ in small but important ways.

It would be a lot easier to just write the middleware functions you want and then compose them however you need when building out new pages and routes.

That's the goal.

Installation

npm install nextjs-middleware-chain

Setup & Usage

This package exports a single named function createMiddleware which takes two arguments, an array of middleware functions and an object of options to set globally for all middlware instances.

createMiddleware returns a factory function which, when called, returns an instance of a Middleware class which is used to run any subsequently chained functions.

This README will go into more detail for each part, but some initial context the setup & usage of NCM looks like:

// Setup in a `middleware.js` file...
import { createMiddleware } from 'nextjs-middleware-chain'

const mwFunction = (req, res, next) => {
  console.log('Running function!')

  return next()
}

const middlewareFunctionsArray = [ mwFunction ]

const optionsObject = { useChainOrder: true }

const mwFactory = createMiddleware(
  middlewareFunctionsArray,
  optionsObject
)

export default mwFactory

// Usage in an API or SSR route...
import mwFactory from './middleware'

/* ... api or ssr routeFunction(...) {...} ... */

export default mwFactory().mwFunction().finish(
  routeFunction,        // required
  'Friendly Route Name' // optional
)

This repo also includes a Next.js sample "client" project which you can reference & run to test out the library. You can reference the setup & syntax for NMC in these files:

Middleware Functions Array

Any middleware functions you create will be made available to NMC by adding them to this array, and passing this array as the first argument to createMiddleware.

Options object

The default options will apply globally unless they are overwritten. You can overwrite options:

  • globally: by passing an object as the second argument to the createMiddleware function.
  • per-route: by passing an object as the only argument to the middleware factory responsible for creating an instance of the middleware.
    • mwFactory(optionsObject).mwFunction().finish(...)

Property | Type | Default | Purpose --- | --- | --- | --- useChainOrder | bool | true | Run middleware functions in the order they are used in the chain. When false this will always run the middleware function in the same order - the order they were added to the functions array passed to createMiddleware. useAsyncMiddleware | bool | true | Run middleware functions asynchronously. When false middleware functions will run synchronously. reqPropName | string | nmc | The name of the decoration property added to the req object for all routes. You can rename this if it causes a collision or of you don't like these letters. onMiddlewareStart | function | (req) => {} | Run before starting the middleware chain. Receives the decorated req object as an argument. onMiddlewareComplete | function | (req) => {} | Run after all middleware is complete (or when next() is called with 'end' or 'route') and before the Next.js route is run. Receives the decorated req object as an argument. onRouteComplete | function | (req) => {} | Runs after the Next.js route has been run (or skipped). Receives the decorated req object as an argument.

Writing middleware functions

All middleware functions will receive 3 arguments:

  • req
  • res
  • next

req and res are provided by the Next.js route. They will be passed by reference to all middleware functions, so can be mutated throughout the chain. Be aware than if you are using a chain function in both API and SSR contexts you may not have access to certain Next.js-provided decorations, which are only added for API routes:

next is a custom function for NMC. Each middleware function must return next() in order to continue running the middleware.

A basic middleware function looks like:

const middlewareChainFn = (req, res, next) => {
  // Do things here...
  
  return next()
}

Short-circuiting the middleware runner

You can break the chain in one of three ways.

To skip any remaining middleware functions and execute the Next.js route:

  • return next('route')

To skip any remaining middleware function and ignore the Next.js route:

  • From API routes: End the response via the res object, and return nothing.
    • res.status(###).json({ message: '...' })
  • From SSR routes: Return with the keyword 'end' and a payload object.
const isAuthorized = (req, res, next) => {
  // Some authorization function...
  const authorized = getAuthorization(req)

  if (req.nmc.type === 'api' && !authorized) {
    // End the request if it is an API route
    res.status(401).json({error: 'unauthorized', message: '...'})
  } else if (req.nmc.type === 'ssr' && !authorized) {
    // End the request if it is an SSR route
    return next('end', {
      redirect: {
        destination: '/',
        permanent: false,
      }
    })
  } else {
    // Skip remaining middleware and continue with the Next.js route.
    return next('route')
  }
}

Request Decoration

NMC will add a custom property, nmc, (the prop name is configurable in options) to the req received by each middleware function. This object will contain:

Property | Type | Value --- | --- | --- id | string | a random guid unique to each middleware instance name | string | finalFuncName if provided, otherwise finalFunc.name, otherwise '' type | string | api or ssr - the type of route the middleware is being run from* context | object | The Next.js context object provided to SSR routes. Will be {} for API routes.

* This determination is made based on the arguments the middleware runner receives from Next.js, which will either be (req, res) => {...} for API routes, or (context, undefined) => {...} for SSR routes.

Middleware Patterns

One Middleware to run Multiple Middlewares

You can run middleware functions yourself, if desired, but you will have to handle the returns from each as needed for your use-case:

const common = async (req, res, next) => {
  const aReturn = fnA(req, res, next)
  const bReturn = await fnB(req, res, next)
  const authReturn = unauthorized(req, res, next)

  if (authReturn) {
    return authReturn
  }
}

Prebuilt Middleware Chain

You can create pre-built chains of middleware if certain functions are commonly used together, or should always be run in a specific order:

// Creating a pre-built chain in middleware.js
const mwFactory = createMiddleware(
  middlewareFunctionsArray,
  optionsObject
)

const preBuiltChain = mwFactory().fnA().fnB().fnC()

export { preBuiltChain }
export default mwFactory

// Using a pre-built chain
import { preBuiltChain } from 'nextjs-middleware-chain'

/* ... api or ssr routeFunction(...) {...} ... */

export default preBuiltChain.otherMiddleware().finish(routeFunction)

Note that when you import & use the preBuiltChain it is already an instance of Middleware and so should be accessed as an object, not run as a method.

Planned Work

  • Add automatic versioning & releases
  • Add additional unit tests
  • Move to TypeScript