@edirect/documentation
v11.2.0
Published
Maintainers
Keywords
Readme
@edirect/documentation
Self-contained NestJS module that serves code-first OpenAPI documentation at /docs, with a
bolttech app bar and pluggable renderers.
- Multi-renderer — Redoc (default), Scalar, or Swagger UI, picked per service.
- Version switching — declare multiple API versions; the viewer renders a version dropdown.
- Deprecated badge — mark a version
deprecatedand it shows an amber badge in the app bar. - bolttech app bar — one slim, sticky bar shared across renderers: logo, workspace breadcrumb, environment badge, version switch, support link, and OpenAPI download.
- Code-first — specs are generated lazily from the live Nest application via
@nestjs/swagger; no hand-written OpenAPI files to maintain.
Installation
pnpm add @edirect/documentationPeer libraries used at runtime (already present in any Nest service): @nestjs/common,
@nestjs/swagger, express.
Quick start
The module is @Global. Register it in your AppModule, then bind the running app once after
bootstrap so declarative versions can be generated lazily.
// app.module.ts
import { Module } from '@nestjs/common';
import { DocumentationModule } from '@edirect/documentation';
@Module({
imports: [
DocumentationModule.forRoot({
title: 'Messaging Service',
workspace: 'Shared Services',
support: '[email protected]',
versions: [
{ slug: 'v2', label: 'v2', version: '2.0', isDefault: true, paths: ['/v2'] },
],
}),
],
})
export class AppModule {}// main.ts
import { NestFactory } from '@nestjs/core';
import { DocumentationService } from '@edirect/documentation';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// REQUIRED — without this the declarative versions cannot be generated.
app.get(DocumentationService).useApp(app);
await app.listen(3000);
}
bootstrap();Open http://localhost:3000/docs.
Async configuration
Use forRootAsync when options depend on config/DI. Because the resolved root value is unknown at
module-build time, opt into the / route with mountRoot: true.
DocumentationModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
mountRoot: true, // only needed if you want root: 'redirect' | 'serve'
useFactory: (config: ConfigService) => ({
enabled: config.get('DOCS_ENABLED') !== 'false',
title: config.get('SERVICE_NAME'),
root: 'redirect',
versions: [{ slug: 'v2', isDefault: true, paths: ['/v2'] }],
}),
});Routes
| Route | Description |
| --- | --- |
| GET /docs | HTML viewer (renderer of your choice). Honors ?spec=<slug> to switch version. |
| GET /docs/spec | JSON index of registered specs ({ default, specs: [{ slug, url, ... }] }). |
| GET /docs/spec/:slug | OpenAPI JSON for a spec, with ETag + Cache-Control and 304 support. |
| GET / | Only when root is redirect or serve (and mountRoot: true for async). |
When enabled: false, every docs route responds 404.
Options
| Option | Type | Default | Description |
| --- | --- | --- | --- |
| enabled | boolean | true | Master switch. false → all docs routes return 404. |
| title | string | 'API Documentation' | Page title and OpenAPI info.title. |
| description | string | — | Default description for generated versions. |
| workspace | string | — | Domain/workspace shown before the title in the breadcrumb. |
| support | string | — | Support email or URL shown in the app bar. |
| renderer | 'redoc' \| 'scalar' \| 'swagger-ui' | 'redoc' | Which viewer to render. |
| logo | { url; altText? } | bolttech logo | Brand logo in the app bar. |
| brand.accent | string (hex or CSS keyword) | bolttech cyan #00bac7 | Accent color; validated, invalid values are ignored. |
| bearerAuth | boolean | true | Adds a bearer security scheme to every generated version. |
| servers | string[] | — | Servers advertised in every generated version. |
| root | 'off' \| 'redirect' \| 'serve' | 'off' | / behavior. Default never claims /. |
| versions | DocumentationVersionOptions[] | — | Declarative versions, generated lazily from the app. |
| specs | DocumentationSpec[] | — | Pre-built OpenAPI documents registered as-is. |
DocumentationVersionOptions: slug (required), label, description, version, order,
isDefault, enabled, deprecated, paths (keep only routes under these prefixes), include
(restrict generation to given Nest modules), configure(builder) (customize the DocumentBuilder).
Note: the actual runtime defaults are
renderer: 'redoc'androot: 'off'.
Recipes
Multiple versions
DocumentationModule.forRoot({
title: 'Policy Service',
versions: [
{ slug: 'v2', label: 'v2', version: '2.0', order: 1, isDefault: true, paths: ['/v2'] },
{ slug: 'v1', label: 'v1', version: '1.0', order: 2, deprecated: true, paths: ['/v1'] },
],
});Mark a version deprecated
Set deprecated: true on the version — the app bar shows an amber Deprecated badge when that
version is selected.
Custom accent / logo / workspace
DocumentationModule.forRoot({
workspace: 'Payments',
brand: { accent: '#7c3aed' },
logo: { url: 'https://example.com/logo.svg', altText: 'Acme' },
versions: [{ slug: 'v2', isDefault: true }],
});Turn off in production
DocumentationModule.forRoot({
enabled: process.env.NODE_ENV !== 'production',
versions: [{ slug: 'v2', isDefault: true }],
});Redirect root to the docs
DocumentationModule.forRoot({ root: 'redirect', versions: [{ slug: 'v2', isDefault: true }] });
// forRootAsync: also pass `mountRoot: true`.Pre-built spec (escape hatch)
DocumentationModule.forRoot({
specs: [
{
slug: 'partner',
label: 'Partner API',
isDefault: true,
document: () => loadPartnerOpenApi(), // OpenAPIObject or a (possibly async) factory
},
],
});Security notes
- Pinned CDN + SRI — Redoc
2.5.3, Scalar1.62.1, andswagger-ui-dist5.32.8are loaded from jsDelivr at fixed versions with Subresource Integrity hashes (seerenderers/shared.ts,CDN). Versions cannot drift tolatest, and a compromised CDN cannot inject script. When bumping a renderer, change the version and theintegrityhash together. noindex— the viewer emits<meta name="robots" content="noindex, nofollow">.enabledgate — setenabled: falseto make every docs route return404(e.g. production).- Auth — the module does not gate
/docsitself. If docs should require authentication, the consumer scopes them in its own auth middleware/guard.
Gotcha
app.get(DocumentationService).useApp(app) is required in main.ts. Declarative versions
are generated lazily from the live app — without useApp, they cannot be built and the service logs
a warning shortly after boot. (specs registered as pre-built documents do not need it.)
