Michael Nguyen
Michael Nguyen

Reputation: 23

(Next 12.2) Chaining Middleware

I am looking to chain middleware in a nextJS project, on the latest NextJS version with the stable Middleware API. Here's an example snippet of my middleware.ts file.

Both of these will return a NextResponse object, how can I go about chaining them so the cookies are added first then passed along to find redirects?

I suppose I'll have to extend the API of the second middleware to take in the response and use it's contents or pass it along. Does anyone have a good example for how to go about this?

export const middleware = (req: NextRequest): NextResponse | undefined => {
  addCookieMiddleware(req);
  findRedirectsMiddleware(req);

// How to return a singular NextResponse object with the cookie attached and the correct URL to redirect?
}

Upvotes: 2

Views: 4228

Answers (1)

Collin Thomas
Collin Thomas

Reputation: 1700

I'm trying to chain as well.

The docs say you can chain https://nextjs.org/docs/api-reference/next/server#static-methods

next() - Returns a NextResponse that will continue the middleware chain

Here is the source code for v12.2.2 (current at the time of writing) https://github.com/vercel/next.js/blob/689626c6a4d509fe04aaf82c9971a5928669bcf4/packages/next/server/web/spec-extension/response.ts#L64

static next(init?: ResponseInit) {
  const headers = new Headers(init?.headers)
  headers.set('x-middleware-next', '1')
  return new NextResponse(null, { ...init, headers })
}

If it does chain, I think it has to do something with the x-middleware-next header it is setting. I looked through the source code and I still could not determine how to chain.

The other interesting thing is next() returns a new NextResponse ,but, next() is a static method.

Since next() is static, you cannot call twice.

This does not work, response will be undefined.

const response = NextResponse.next()
response.next({
  headers: {
    'a-test': Math.random().toString(),
  },
})

What I'm doing for now:

You set cookies by calling next() and using the public cookie property and use Map operations.

const response = NextResponse.next()
response.cookies.set('a-test', Math.random().toString())

You set headers by calling next() and passing in an object with a headers object within.

const response = NextResponse
response.next({
  headers: {
    'a-test': Math.random().toString(),
  },
})

I first thought was to use const response = new NextResponse but that did not work, response would always be undefined. You have to use const response = NextResponse

Since you can only call next() once, the best thing I could do was call the headers function first where calling next() is required, and then calling the function that sets the cookies.

// middleware.js
import { NextResponse } from 'next/server'

const setHeaders = (response) => {
  return response.next({
    headers: {
      'a-test': Math.random().toString(),
    },
  }
}

const setCookies = (response) => {
  response.cookies.set('a-test', Math.random().toString())
  return response
}

export async function middleware(request) {
  // Limitation in config matcher, requires me to exclude here
  if (request.nextUrl.pathname.startsWith('/_next')) {
    return NextResponse.next()
  }

  let response = NextResponse
  response = setHeaders(response) 
  response = setCookies(response)
  return response
}

Hope this helps!

Upvotes: 2

Related Questions