Skip to main content

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

OptionTypeDescription
namestringAPI name.
infoobjectDocument metadata — title, version, description.
typesany[]Data types to register (decorated classes, EnumType results).
controllersType[]OPRA controller classes. Must be provided statically.
referencesRecord<string, ReferenceThunk>Namespaced references to other ApiDocument instances or async thunks.
basePathstringURL prefix for all OPRA routes. Defaults to '/'.
schemaIsPublicbooleanExpose the GET /$schema endpoint without authentication.
scopestringValidation 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.
importsany[]NestJS modules to import into the OPRA module context.
providersProvider[]NestJS providers available for injection inside controllers.
exportsany[]Providers to export from the OPRA module to the rest of the application.
globalbooleanRegister the module as a NestJS global module.
tokenanyCustom 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)