Functions
Middleware functions standardization, explanation and usage
NEMO uses Next.js native middleware function schema to provide you with a familiar way of writing middleware functions and enabling you to use 3rd party packages without any issues.
Function schema
NEMO middleware function API is fully compatible with Next.js native API. It's just extending the event prop with some additional props and functions.
import { } from "@rescale/nemo";
const : = async (, ) => {
// function body
};Info
You can check detailed types by hovering on function parts and props.
Explanation
Prop: request
Type: NextRequest
That's a user middleware's request passed to middleware and can be later updated by functions in chain.
This props cookies will only deffer from the original user's request if you've forwarded any response it in the chain.
Header forwarding
You can forward headers to subsequent middlewares and to your page components using NextResponse.next() with the request option:
const middleware: NextMiddleware = async (request) => {
const newHeaders = new Headers(request.headers);
newHeaders.set('x-custom-header', 'value');
return NextResponse.next({
request: {
headers: newHeaders,
},
});
};How it works:
- Headers set via
NextResponse.next({ request: { headers } })are forwarded to subsequent middlewares in the chain - These headers are automatically available in your page components via Next.js
headers()function - Headers are preserved through the entire middleware chain until they reach your page
Example with multiple middlewares:
const addLocale: NextMiddleware = async (request) => {
const locale = detectLocale(request);
const newHeaders = new Headers(request.headers);
newHeaders.set('x-locale', locale);
return NextResponse.next({
request: {
headers: newHeaders,
},
});
};
const addUser: NextMiddleware = async (request) => {
// The x-locale header from previous middleware is available here
const locale = request.headers.get('x-locale');
const user = await getUser(request);
const newHeaders = new Headers(request.headers);
newHeaders.set('x-user-id', user.id);
return NextResponse.next({
request: {
headers: newHeaders,
},
});
};
// In your page.tsx
export default async function Page() {
const headersList = await headers();
const locale = headersList.get('x-locale'); // Available!
const userId = headersList.get('x-user-id'); // Available!
return <div>Locale: {locale}, User: {userId}</div>;
}Important: Headers forwarded via NextResponse.next({ request: { headers } }) are different from response headers. They are specifically for forwarding data through the middleware chain to your page components. Use NextResponse.next({ headers: { ... } }) for response headers that are sent to the client.
Prop: event
Type: NemoEvent extends NextFetchEvent
This property contains event object for serverless functions execution extended via nemo prop which contains chain execution storage.
Storage
Learn more about storage in middleware
Logger methods
The event object includes built-in logging capabilities that use the same logger as NEMO internally:
// Debug logging (only displayed when debug is enabled in config)
event.log("Processing user request", userId);
// Error logging (always displayed)
event.error("Failed to process request", error);
// Warning logging (always displayed)
event.warn("Deprecated feature used", featureName);These logger methods maintain the "[NEMO]" prefix for consistent logging throughout your middleware chain.
Skip remaining middlewares
You can skip the remaining middlewares in the current chain section (before/main/after) without returning a terminating response:
const middleware: NextMiddleware = async (request, event) => {
// Do some processing
if (someCondition) {
event.skip(); // Skip remaining middlewares in this chain section
// You can still return a response if needed
return NextResponse.next();
}
// This code will execute if skip() was not called
};Skip with options:
You can also skip the after chain by passing the skipAfter option:
const middleware: NextMiddleware = async (request, event) => {
if (someCondition) {
// Skip remaining middlewares in current chain AND skip after chain
event.skip({ skipAfter: true });
return NextResponse.next();
}
};Chain sections: The skip functionality works per chain section. If you call event.skip() in the main chain, it will only skip remaining middlewares in the main chain, but after chain middlewares will still execute. This allows you to have cleanup logic in the after chain that always runs.
If you call event.skip({ skipAfter: true }), it will skip both the remaining middlewares in the current chain section AND all middlewares in the after chain.
Use cases:
- Early exit when a condition is met without redirecting
- Conditional middleware execution based on request properties
- Performance optimization by skipping unnecessary middleware processing
- Skip cleanup logic in after chain when not needed
Examples:
// Skip only current chain section
const authCheck: NextMiddleware = async (request, event) => {
const token = request.cookies.get('auth-token');
if (!token) {
// Skip remaining auth middlewares, but allow after chain to run
event.skip();
return NextResponse.next();
}
// Continue with authentication logic
};
// Skip current chain and after chain
const cacheCheck: NextMiddleware = async (request, event) => {
const cached = await checkCache(request);
if (cached) {
// Skip remaining middlewares AND after chain (no cleanup needed)
event.skip({ skipAfter: true });
return NextResponse.next({
headers: { 'x-cached': 'true' },
});
}
};
const adminCheck: NextMiddleware = async (request, event) => {
// This will not execute if skip() was called in authCheck
const user = storage.get('user');
if (!user?.isAdmin) {
return NextResponse.redirect('/unauthorized');
}
};