Middleware

Using Vercube BaseMiddleware inside a Nitro application

Vercube middleware classes placed in the middleware/ directory are automatically detected and excluded from Nitro's native middleware handling. This prevents Nitro from trying to auto-register them as H3 middleware, which would cause errors since they are not standard Nitro event handlers.

How It Works

At build time the module scans the middleware/ directory (hardcoded in Nitro) and looks for classes that extend BaseMiddleware from @vercube/core. Any matching file is added to Nitro's ignore list so Nitro's own file scanner skips it.

The middleware classes are not bound to the DI container automatically - Vercube handles middleware registration differently and does not require it.

Creating a Middleware

Extend BaseMiddleware and place the file inside your middleware/ directory:

src/middleware/AuthMiddleware.ts
import { BaseMiddleware, UnauthorizedError } from '@vercube/core';

export class AuthMiddleware extends BaseMiddleware {
  public async onRequest(request: Request): Promise<void> {
    const token = request.headers.get('authorization');

    if (!token) {
      throw new UnauthorizedError('Missing authorization header');
    }
  }
}
src/middleware/LogMiddleware.ts
import { BaseMiddleware } from '@vercube/core';

export class LogMiddleware extends BaseMiddleware {
  public async onRequest(request: Request): Promise<void> {
    console.log(`[${new Date().toISOString()}] ${request.method} ${request.url}`);
  }

  public async onResponse(request: Request, response: Response): Promise<void> {
    console.log(`[${new Date().toISOString()}] ${response.status}`);
  }
}

Applying Middleware to a Controller

Apply middleware via the @Middleware decorator on a controller class or individual method:

src/routes/UserController.ts
import { Controller, Get, Middleware } from '@vercube/core';
import { AuthMiddleware } from '../middleware/AuthMiddleware';
import { LogMiddleware } from '../middleware/LogMiddleware';

@Controller('/api/users')
@Middleware(LogMiddleware)
export class UserController {

  @Get('/')
  @Middleware(AuthMiddleware)
  list() {
    return [{ id: '1', name: 'Alice' }];
  }

  @Get('/public')
  public() {
    return [{ id: '2', name: 'Bob' }];
  }
}

Global Middleware

Middleware classes in the middleware/ directory are not registered as global middleware automatically. They are only applied where you explicitly use the @Middleware decorator.

If you want a middleware to run on every request, register it in GlobalMiddlewareRegistry via a setupFile. See Global Middlewares in the core docs for the full API.

src/container.ts
import type { App } from '@vercube/core';
import { GlobalMiddlewareRegistry } from '@vercube/core';
import { LogMiddleware } from './middleware/LogMiddleware';

export default async (app: App) => {
  const registry = app.container.get(GlobalMiddlewareRegistry);
  registry.registerGlobalMiddleware(LogMiddleware, { priority: 1 });
};
nitro.config.ts
import { vercubeNitro } from '@vercube/nitro';
import { defineConfig } from 'nitro';

export default defineConfig({
  modules: [
    vercubeNitro({
      setupFile: './src/container.ts',
    }),
  ],
});

BaseMiddleware API

MethodArgumentsDescription
onRequestrequest: Request, response: Response, args: MiddlewareOptionsCalled before the controller method. Throw an error to abort the request.
onResponserequest: Request, response: Response, payload: TCalled after the controller method returns. Can modify the response.

Both methods are optional - implement only the ones you need.

Middleware can abort a request by throwing an HttpError (e.g. BadRequestError, ForbiddenError). Returning a value from onRequest or onResponse is not supported - use the response argument to modify the response object instead.
Previous

Overview

Run Vercube as a Vite plugin using the Environment API

Next