Middleware Chaining utility
Is your feature request related to a problem? Please describe.
As we add more packages that use middlewares to rewrite urls and add headers, it becomes harder to chain them together. It would be nice idea if the project had some default way to chain middlewares together.
Describe the solution you'd like This is the code I am using in my own project to Chain Middlewares:
import "server-only";
import { NextRequest, NextResponse } from "next/server";
type Middleware = (
request: NextRequest
) => NextResponse | Response | Promise<Response>;
export async function chainMiddleware(
middlewares: Middleware[],
request: NextRequest
) {
let response: ReturnType<Middleware> = new NextResponse(
request.body,
request
);
for (const middleware of middlewares) {
const awaitedResponse = await response;
const url = new URL("", request.url);
const newRequest = new NextRequest(url, awaitedResponse);
response = middleware(newRequest);
}
return response;
}
And used in the code:
const middlewares = [securityHeaders, I18nMiddleware];
const Middlewares = (request: NextRequest) => {
return chainMiddleware(middlewares, request);
};
export default authMiddleware(async (auth, request) => {
return Middlewares(request)
}
This is my implementation, it seems to work as intended in my project, but I may be wrong. I would appreciate some input to see if this is useful or not for the project.
This is very important as we start to make each package self-maintaining i.e. #332
My previous implementation was flawed, as I was not forwarding the headers to the next requests.
I settled for now on this implementation:
import "server-only";
import { type NextRequest, NextResponse } from "next/server";
type Middleware = (
request: NextRequest
) => NextResponse | Response | Promise<Response>;
export async function chainMiddleware(
middlewares: Middleware[],
request: NextRequest
) {
if (middlewares.length === 0) {
return undefined;
}
let response: ReturnType<Middleware> = new NextResponse();
const mergedHeaders = new Headers();
for (const middleware of middlewares) {
const currResponse = await middleware(request);
currResponse.headers.forEach((val, key) => mergedHeaders.set(key, val));
response = new NextResponse(null, {
headers: mergedHeaders,
status: currResponse.status,
statusText: currResponse.statusText,
});
}
return response;
}
I think this is good enough for now, but if you do url rewrites I have noticed some inconsistent behavior with next-international. I am most likely doing something wrong here. Eventually when each package is self-maintaining, it would be a good idea to revisit this issue
@pedrocarlo @haydenbleasel
I was also about to make this suggestion, but using the already open source library available for this provided by rescale.build (https://nemo.rescale.build) z4nr34l/nemo
Supporting Dynamic segments Functions Chaining Shared context
Simplifying and improving overall management of middlewares.
This would be an awesome addition to this incredible project. and would help with some cleanup regarding the middlewares and allow the users to easily extend further.
Thanks,
Best Regards
@FlurryNight just read the docs. The package is super awesome. It seems to be a good replacement for what I did. Will give it a try on my project
@FlurryNight just read the docs. The package is super awesome. It seems to be a good replacement for what I did. Will give it a try on my project
Hey, Awesome!.
Let me know your thoughts after!
Best Regards
@pedrocarlo @FlurryNight this is great! now that v3 is merged, I’d love to see this implemented to handle middlewares from various packages.
would either of you be up for creating a PR?
I believe that z4nr34l/nemo is going to be releasing a new version soon, so I think it's more prudent to wait for it. It should fix some next js middleware typing issue that was annoying to work around without patching the package
Hey folks, I believe Nemo v2 is out! We should look into this 👍
:rocket: Issue was released in v5.0.7 :rocket: