Document Data
data on SwaggerGenerateOptions carries the document-level content of the OpenAPI spec: the info block, servers, security schemes, default content types.
type SwaggerGenerateData = {
name?: string;
version?: string;
description?: string;
license?: string;
servers?: string | string[] | ServerOption | ServerOption[];
securityDefinitions?: SecurityDefinitions;
consumes?: string[];
produces?: string[];
responses?: Response[];
pathParameters?: Record<string, { description?: string }>;
collectionFormat?: 'csv' | 'ssv' | 'tsv' | 'pipes' | 'multi';
operationIdStrategy?: 'method' | 'path';
extra?: Record<string, any>;
};info Block
name, version, description, license populate the info block. Each falls back to the corresponding field of the nearest package.json when omitted — convenient locally, easy to overlook in CI. Prefer to pass them explicitly for builds.
data: {
name: 'Example API',
version: '1.2.3',
description: 'REST endpoints for the example service.',
license: 'MIT',
}Servers
type ServerOption = {
url: string;
description?: string;
};Accepts:
- a single URL string
- an array of URL strings
- a
ServerOptionobject - an array of
ServerOptionobjects
// String shorthand
servers: 'https://api.example.com'
// Multiple endpoints
servers: [
{ url: 'https://api.example.com', description: 'Production' },
{ url: 'https://staging.example.com', description: 'Staging' },
]In OpenAPI 2.0 there is no servers array — the emitter maps the first entry to host + basePath + schemes.
Security Schemes
securityDefinitions accepts a map of named schemes. The exact shape follows the OpenAPI spec for the version you emit.
data: {
securityDefinitions: {
basicAuth: {
type: 'http',
scheme: 'basic',
},
apiKey: {
type: 'apiKey',
in: 'header',
name: 'X-API-Key',
},
oauth2: {
type: 'oauth2',
flows: {
authorizationCode: {
authorizationUrl: 'https://auth.example.com/authorize',
tokenUrl: 'https://auth.example.com/token',
scopes: { 'read:users': 'Read users', 'write:users': 'Write users' },
},
},
},
},
}Supported schemes: apiKey (header/query), http with scheme: 'basic', and oauth2 with any of the four OAuth2 flow types (implicit, password, authorizationCode, clientCredentials). For bearer-token JWT auth you can model it as an API key in the Authorization header, or extend the spec through data.extra if you need a non-basic HTTP scheme.
Operation-level security is driven by the @Security decorator on your controllers and methods — the value you pass here only defines the schemes.
Default Content Types
consumes and produces become the default media types for request bodies and responses respectively. Both emitters honour them, but they surface differently: v2 writes the top-level consumes/produces keys, while v3 folds them into the requestBody.content and responses.<code>.content keys — OpenAPI 3 removed the document-level fields.
data: {
consumes: ['application/json'],
produces: ['application/json', 'application/xml'],
}Precedence: @Consumes/@Produces on a controller and on a method are merged (controller entries first) and win over these defaults. A @Produces on a specific response wins over the method's in v3. When nothing is declared anywhere, a request body with file parameters uses multipart/form-data, any other form body uses application/x-www-form-urlencoded, and everything else falls back to these defaults — or to application/json when they are unset too.
Document Responses
responses is merged into every emitted operation. OpenAPI has no document-level responses field, so without it a spec-wide error shape has to be repeated on every method — or patched onto the finished document, which cannot know which paths exist.
data: {
responses: [
{
name: 'default',
status: 'default',
description: 'Error',
schema: { typeName: 'refObject', refName: 'ErrorResponse', properties: [] },
},
],
}Entries are Response objects from @trapi/core, not raw OpenAPI fragments, so one config works for every emitter: v2 writes { description, schema: { $ref: '#/definitions/…' } } and v3 writes { description, content: { 'application/json': { schema: { $ref: '#/components/schemas/…' } } } }.
A schema may reference a type your controllers never mention — the $ref is emitted without requiring the type to be in the metadata. Supply the component itself through extra and the two compose:
data: {
responses: [ /* … as above … */ ],
extra: { components: { schemas: { ErrorResponse: { type: 'object' } } } },
}Precedence: a method's own response with the same status wins. There is no per-operation opt-out, so reserve this for responses that genuinely apply everywhere — a default error shape is the intended case; 404 is usually not.
Path Parameters
pathParameters supplies descriptions for path-template variables, keyed by the variable name as it appears in the template — realmId for /realms/:realmId/users, not the name of a method or of a decorated argument.
data: {
pathParameters: {
realmId: { description: 'The realm, addressed by id or by name.' },
},
}It covers two cases. A variable TRAPI synthesizes — one the path template declares but no decorated argument does, the /realms/:realmId/users case under Multi-Mount Controllers — has no declaration to read a description from, so this is the only way to give it one. A declared path parameter that carries no description of its own is filled in from the same entry, so one line reads identically on every operation the variable appears in.
The option is document-wide rather than per-method because a synthesized variable appears on every verb of its mount — there is no single handler a per-method description could hang off — and path variable names are global in practice anyway.
Precedence: a description that came from a decorator always wins; an entry here only fills a gap. Keys that match no template variable are ignored.
Collection Format
collectionFormat controls how array-valued query parameters are serialised in OpenAPI 2.0. It has no effect on v3 emitters.
data: { collectionFormat: 'multi' } // ?tag=a&tag=bOperation IDs
operationIdStrategy selects how a default operationId is derived. An explicit operationId on a method always wins over both strategies.
'method' (default)
Ucfirst(methodName). When two operations end up with the same id — the same method name on two controllers, or one method mounted at several controller paths — the later one gets a positional _2, _3, … suffix. That suffix depends on emission order, so adding, removing or reordering a controller can shift the ids of unrelated operations. It is kept as the default for backwards compatibility.
'path'
The HTTP verb plus the emitted URL segments, with {param} becoming By<Param>:
data: { operationIdStrategy: 'path' }| Operation | operationId |
|---|---|
GET /roles | getRoles |
GET /roles/{id} | getRolesById |
POST /roles | postRoles |
GET /realms/{realmId}/roles | getRealmsByRealmIdRoles |
DELETE /realms/{realmId}/roles/{id} | deleteRealmsByRealmIdRolesById |
Details:
- The verb is not aliased —
POSTyieldspost…, notcreate…. - The root path
/yields the bare verb (get,post). One verb per path makes it unique by construction. - Non-alphanumerics are separators:
by-idbecomesById,user.profilebecomesUserProfile. The lowercase verb prefix keeps the id identifier-safe even when a segment starts with a digit (/2fa→get2fa). - A multi-mount controller gets a distinct id per mount with no numeric suffix, because the path differs.
Two different paths can still normalise to the same id — /users/{id} and /users/by-id both yield getUsersById. The _2 suffix remains as the backstop for that case; set an explicit operationId on one of the two to remove the ambiguity.
Extra Properties
extra is merged into the generated spec as a raw fragment. Useful for x-* vendor extensions or properties TRAPI does not surface directly:
data: {
extra: {
'x-audience': ['internal', 'partners'],
tags: [
{ name: 'Users', description: 'User management' },
{ name: 'Orders', description: 'Order processing' },
],
},
}Generated properties take precedence where keys overlap — extra cannot overwrite content TRAPI has produced from your decorators.
extra is built before generation, so it cannot key on values the emitter assigns — per-operation additions that need the emitted path or operationId have nothing to attach to. For those, use the CLI's swagger.transform hook, which runs on the finished document just before it is written.