HTTP Module
The @opra/nestjs-http package integrates the OPRA HTTP adapter into a NestJS application. It converts your OPRA controllers into standard NestJS controllers, wires up the request/response lifecycle through NestJS middleware, and handles error serialization through a built-in exception filter — all transparently.
Installation
npm install @opra/nestjs-http @opra/http @opra/common
Setup
Import OpraHttpModule in your root application module:
import { Module } from '@nestjs/common';
import { OpraHttpModule } from '@opra/nestjs-http';
import { CustomersController } from './customers/customers.controller.js';
import { CustomersService } from './customers/customers.service.js';
import * as models from './models/models.js';
@Module({
imports: [
OpraHttpModule.forRoot({
imports: [DatabaseModule],
providers: [CustomersService],
exports: [CustomersService],
name: 'CustomerApi',
info: { title: 'Customer API', version: '1.0' },
references: {
shared: () => SharedModels.initApiDocument(),
},
types: [...Object.values(models)],
controllers: [CustomersController],
basePath: '/api',
schemaIsPublic: true,
}),
],
})
export class AppModule {}
Options
| Option | Type | Description |
|---|---|---|
name | string | API name. |
info | object | Document metadata — title, version, description. |
types | any[] | Data types to register (decorated classes, EnumType results). |
controllers | Type[] | OPRA controller classes. Must be provided statically. |
references | Record<string, ReferenceThunk> | Namespaced references to other ApiDocument instances or async thunks. |
basePath | string | URL prefix for all OPRA routes. Defaults to '/'. |
schemaIsPublic | boolean | Expose the GET /$schema endpoint without authentication. |
scope | string | Validation scope applied to every request and response. |
interceptors | (InterceptorFunction | IHttpInterceptor | Type<IHttpInterceptor>)[] | Interceptor chain executed on every request. Supports class-based interceptors for NestJS DI. |
imports | any[] | NestJS modules to import into the OPRA module context. |
providers | Provider[] | NestJS providers available for injection inside controllers. |
exports | any[] | Providers to export from the OPRA module to the rest of the application. |
global | boolean | Register the module as a NestJS global module. |
token | any | Custom injection token for the module. |
Async configuration
Use forRootAsync() when info, types, references, or other options depend on injected services. Note that controllers must always be provided statically — this is a NestJS limitation.
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { OpraHttpModule } from '@opra/nestjs-http';
import * as models from './models/models.js';
@Module({
imports: [
ConfigModule.forRoot(),
OpraHttpModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
controllers: [CustomersController],
useFactory: (config: ConfigService) => ({
name: 'CustomerApi',
info: {
title: 'Customer API',
version: config.get('API_VERSION'),
},
types: [...Object.values(models)],
}),
}),
],
})
export class AppModule {}
How it works
OpraHttpModule registers an OpraMiddleware that runs on every request. The middleware creates an HttpContext and attaches it to the Express request object before NestJS routes the request to a controller. This means full OPRA request decoding — parameters, headers, body — happens transparently before your handler is called.
OPRA controllers are converted to NestJS controllers automatically. Each operation method is mapped to the corresponding HTTP verb and path via NestJS decorators (@Get, @Post, etc.), so NestJS routing, guards, and pipes all work as expected.
An OpraExceptionFilter is registered globally to catch any errors thrown from OPRA handlers and serialize them into the standard OPRA error format before sending the response.
Dependency injection in controllers
Because OPRA controllers are registered as NestJS providers, you can inject services directly into them:
import { HttpController, HttpOperation, HttpContext } from '@opra/http';
import { Injectable } from '@nestjs/common';
import { CustomersService } from './customers.service.js';
@Injectable()
@HttpController('/customers')
export class CustomersController {
constructor(private readonly service: CustomersService) {}
@HttpOperation.GET('/:id')
async get(ctx: HttpContext) {
return this.service.findById(ctx.pathParams.id);
}
}
Guards and interceptors
NestJS guards and interceptors work normally on OPRA controllers. Apply them at the controller or method level as you would with any NestJS controller:
import { UseGuards, UseInterceptors } from '@nestjs/common';
import { AuthGuard } from './auth.guard.js';
import { LoggingInterceptor } from './logging.interceptor.js';
@UseGuards(AuthGuard)
@UseInterceptors(LoggingInterceptor)
@HttpController('/customers')
export class CustomersController { ... }
Schema endpoint
When schemaIsPublic: true is set, the GET {basePath}/$schema endpoint is registered without any authentication guard. When omitted or false, the schema endpoint respects your application's global auth guards.
GET /api/$schema → OpraSchema.ApiDocument (JSON)