Skip to content

Middleware

Middleware lets you run logic before a route is loaded — useful for auth checks, redirects, or custom responses.

Only one middleware can be registered. It receives the request context and the matched route, and can either allow the request to continue (return nothing) or short-circuit with a redirect or custom response.

Usage

Via createServer config

ts
import { createServer } from 'lovable-ssr/server';

createServer({
  root,
  entryPath: 'src/ssr/entry-server.tsx',
  middleware: async (ctx) => {
    if (ctx.pathname.startsWith('/dashboard') && !ctx.request.cookies.token) {
      return { redirect: '/login' };
    }
  },
});

Via global registerMiddleware

ts
import { registerMiddleware } from 'lovable-ssr';

registerMiddleware(async (ctx) => {
  if (ctx.pathname.startsWith('/admin') && !ctx.request.cookies.session) {
    return { status: 401, body: 'Unauthorized' };
  }
});

If both a config middleware and a global middleware are registered, the config middleware takes priority.

MiddlewareContext

The middleware function receives a MiddlewareContext with:

FieldTypeDescription
requestRequestContextParsed request: cookies, cookiesRaw, headers, method, url.
routeRouteConfig | undefinedThe matched route config, or undefined if no route matched.
pathnamestringThe URL pathname (e.g. /dashboard/settings).
params{ routeParams, searchParams }Extracted route and search params.

MiddlewareResponse

Return an object to short-circuit the request. Return void (or nothing) to continue normally.

FieldTypeDescription
redirectstring?URL to redirect to.
statusnumber?HTTP status code (defaults to 302 for redirects, 200 otherwise).
headersRecord<string, string>?Extra response headers.
bodystring?Response body (used when not redirecting).

Examples

Auth guard

ts
middleware: async (ctx) => {
  const protectedPaths = ['/dashboard', '/settings', '/profile'];
  const isProtected = protectedPaths.some((p) => ctx.pathname.startsWith(p));

  if (isProtected && !ctx.request.cookies.token) {
    return { redirect: '/login' };
  }
}

Maintenance mode

ts
middleware: async (ctx) => {
  if (process.env.MAINTENANCE === 'true') {
    return {
      status: 503,
      headers: { 'Retry-After': '3600' },
      body: '<h1>Under maintenance</h1>',
    };
  }
}