This is `canary` version of documentation. It's still under construction and review.
ZANREAL logoNEMO

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.

_middleware.ts
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');
  }
};