Skip to content

Docs: improvde documentation on how to write multiple middlewares in a proper manner #59888

@mastoj

Description

@mastoj

What is the improvement or update you wish to see?

Often when using middleware you might need to use more than one and then chain them, and from the documentation it is not clear how to do it properly. I tried first implementing a chain function like this:

export type MiddlewareFactory = (next: NextMiddleware) => NextMiddleware;
export const chain = (
  middlewareFactories: MiddlewareFactory[],
  index = 0,
): NextMiddleware => {
  const current = middlewareFactories[index];
  if (!current) {
    return () => NextResponse.next();
  }
  const next = chain(middlewareFactories, index + 1);
  return current(next);
};

This was then called in the middleware like such:

export default chain([withSession, withAuth, withInternationalization]);

This was suboptimal in cases where I wanted to use a rewrite or redirect in one middleware and then set some cookies after. I also struggled to set cookies in two different middlewares using a chaining function like this. So ended up created a version that used NextResponse and a context to pass some information between middlewares. So my updated middleware chain function look like this:

export type MiddlewareContext = { [key: string]: unknown };
export type MiddlewareResponse = {
  response: NextResponse<unknown> | undefined;
  context: MiddlewareContext;
};

export type Middleware = (
  request: NextRequest,
  response: NextResponse<unknown> | undefined,
  context: MiddlewareContext,
) => Promise<MiddlewareResponse>;

export const chain =
  (middlewares: Middleware[]): NextMiddleware =>
  async (request: NextRequest, event: NextFetchEvent) => {
    const { response, context } = await middlewares.reduce(
      async (prev: Promise<MiddlewareResponse>, current) => {
        const { response, context } = await prev;
        return current(request, response, context);
      },
      new Promise<MiddlewareResponse>((resolve) =>
        resolve({ context: {}, response: undefined } as MiddlewareResponse),
      ),
    );
    if (response === undefined) {
      return NextResponse.next();
    }
    return response;
  };

This works as intended I think, but it doesn't feel right and some pointers in the documentation would definitely help.

Is there any context that might help us understand?

I think I wrote all that is needed above.

Does the docs page already exist? Please link to it.

https://nextjs.org/docs/app/building-your-application/routing/middleware

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions