next.jsjwtmiddlewareapp-router

Create middleware which rewrites headers and redirects url to API server


I'm attempting to make a middleware for making API calls to a separate server but failing to make a redirect/rewrite. Paths which starts with /api/proxy should be written without that prefix and route to the API server. I'm using NextJS v.15.1.4, NextAuth 5.0.0-beta.25 and @rescale/nemo 1.4 for combining (future) middlewares.

My middleware.ts looks like this at the moment:

import { NextResponse } from "next/server";
import { cookies } from 'next/headers';
import { createMiddleware, type MiddlewareFunctionProps } from '@rescale/nemo'
import { auth } from 'auth';

const globalMiddlewares = {
  before: async ({ request }: MiddlewareFunctionProps) => {
    if (!request.url.includes("api/auth")) {
      return;
    }
    auth()
  },
};

const middlewares = {
  "/api/proxy/*segments": [
    async ({ request, response, context, event, forward, params }: MiddlewareFunctionProps) => {

      const sessionCookieName = process.env.NODE_ENV === "development"
        ? "authjs.session-token"
        : "__Secure-next-auth.session-token";
      const cookieStore = await cookies();
      const bearerToken = cookieStore.get(sessionCookieName);

      request.headers.set("Authorization", "Bearer " + bearerToken?.value);

      const newHeaders = new Headers(request.headers);
      newHeaders.set("Authorization", "Bearer " + bearerToken?.value);

      const newResponse = NextResponse.next({
        request: {
          headers: newHeaders
        }
      });

      forward(newResponse);

    },
    async ({ request, response, context, event, forward, params }: MiddlewareFunctionProps) => {

      const path = params()?.segments?.join("/");

      return NextResponse.rewrite(new URL(path, process.env.NEXT_PUBLIC_API_BASE));

    }
  ]
}

// create middleware
export const middleware = createMiddleware(middlewares, globalMiddlewares);

export const config = {
  matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"],
}
```

Solution

  • Your forward(newResponse) is misplaced, you’re forwarding in the middle of a chain where you likely expect to modify the request path first. Instead, chain your logic only after addressing header manipulations and rewriting the path. Make sure the /api/proxy prefix is ​​removed and the rest of the URL is correctly added to the API server. You are currently trying to set headers for a proxy API request using request.headers. This may be error-prone depending on how your middleware processes requests in Next.js. You can construct a fetch request to the API server directly and handle the response. Below code adjusted

    import { NextResponse } from "next/server";
    import { cookies } from "next/headers";
    import { createMiddleware, type MiddlewareFunctionProps } from "@rescale/nemo";
    import { auth } from "auth";
    
    const globalMiddlewares = {
      before: async ({ request }: MiddlewareFunctionProps) => {
        if (!request.url.includes("api/auth")) {
          return;
        }
        auth();
      },
    };
    
    const middlewares = {
      "/api/proxy/*segments": [
        // Middleware function for adding Authorization header
        async ({
          request,
          response,
          context,
          event,
          forward,
          params,
        }: MiddlewareFunctionProps) => {
          const sessionCookieName =
            process.env.NODE_ENV === "development"
              ? "authjs.session-token"
              : "__Secure-next-auth.session-token";
    
          const cookieStore = cookies();
          const bearerToken = cookieStore.get(sessionCookieName);
    
          if (bearerToken?.value) {
            request.headers.set("Authorization", `Bearer ${bearerToken.value}`);
          }
    
          // Allow the next middleware in the chain to handle the rewrite
          return forward();
        },
    
        // Middleware function for rewriting the path to the external API server
        async ({
          request,
          response,
          context,
          event,
          forward,
          params,
        }: MiddlewareFunctionProps) => {
          const path = params()?.segments?.join("/"); // Extract segments after `/api/proxy`
          return NextResponse.rewrite(
            new URL(path ?? "", process.env.NEXT_PUBLIC_API_BASE)
          );
        },
      ],
    };
    
    export const middleware = createMiddleware(middlewares, globalMiddlewares);
    
    export const config = {
      matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"],
    };
    

    EDIT

    Instead of expecting RTK Query to resolve or handle requests after rewrite(), you can make the middleware act as a proxy server. Modify the middleware to directly handle the request and forward it with fetch. Intercept the request in the middleware, rewrite, and fetch the response from the API server.

    export const middleware = async (req: Request) => {
    
      const url = new URL(req.url);
      if (!url.pathname.startsWith("/api/proxy")) return NextResponse.next();
    
      const targetPath = url.pathname.replace("/api/proxy/", "");
      const targetURL = `${process.env.NEXT_PUBLIC_API_BASE}/${targetPath}`;
    
      const sessionCookieName =
        process.env.NODE_ENV === "development"
          ? "authjs.session-token"
          : "__Secure-next-auth.session-token";
      const cookieStore = cookies();
      const bearerToken = cookieStore.get(sessionCookieName);
    
      const headers = new Headers(req.headers);
      if (bearerToken?.value) {
        headers.set("Authorization", `Bearer ${bearerToken.value}`);
      }
    
      // Forward the request to the API server
      const response = await fetch(targetURL, {
        method: req.method,
        headers,
        body: req.body,
      });
    
      // Return the proxied response back to the client
      const proxiedResponse = new NextResponse(response.body, {
        status: response.status,
        headers: response.headers,
      });
    
      return proxiedResponse;
    };
    
    export const config = {
      matcher: "/api/proxy/:path*", 
    };