docfy patch-spec

Parchea un documento OpenAPI ya construido, enteramente vía análisis estático (ts-morph), sin require() de ningún docs file.

Por qué existe

Esta es una forma manual, impulsada por CI, de sortear lo único que el pipeline en runtime de DocfyModule no puede hacer estructuralmente: funcionar bajo el modo de build webpack: true del CLI de NestJS (consulta El plugin de CLI para la alternativa automática y recomendada, que calcula este mismo análisis en cada build en vez de un paso aparte). patch-spec esquiva el muro de webpack emparejando por path + método HTTP, calculado igual que ya hacen check/coverage/lint, en vez de necesitar la referencia viva de la clase controller.

Uso

bash
npx nestjs-docfy patch-spec --spec <path-or-url> [options]
bash
# Patch a running app's served document
npx nestjs-docfy patch-spec --spec http://localhost:3000/api-json --out openapi.json

# Patch a file already written to disk
npx nestjs-docfy patch-spec --spec dist/openapi.json --out dist/openapi.json

Opciones

OptionDefaultDescription
--spec <path|url>(required)A local openapi.json, or a URL (e.g. a running app's /api-json)
--out <path>stdoutWhere to write the patched document
--root <path>.Project root directory
--tsconfig <path>auto-detectedPath to tsconfig.json
--pattern <glob>**/*.controller.tsGlob pattern to find controllers
--format <format>tsFormato de docs file a buscar: ts o js
--quietfalseSuppress all output except errors

Qué se fusiona

Emparejado por path + método HTTP contra el documento base:

  • ApiTags → se une a tags (nunca elimina tags que el documento base ya tenía)
  • ApiOperation({ summary, description, deprecated }) → sobrescribe esos campos
  • ApiResponse({ status, description, schema, example, examples }) → se fusiona por código de estado (otros códigos de estado no se tocan); cuando no se da schema/type, recae en el tipo de retorno resuelto del propio método, la misma inferencia de DTO/class-validator/interface que ya hace generate, incluyendo un oneOf de $refs cuando el tipo de retorno es una unión de ≥2 DTOs/entities con nombre (ej. Promise<UserDto | AdminDto>)
  • ApiBody({ schema, examples }) → establece requestBody, con el mismo fallback estilo tipo-de-retorno al tipo resuelto del parámetro @Body(), incluyendo el mismo manejo de unión → oneOf (example deliberadamente no se lee aquí, ya que el tipo real ApiBodyOptions de @nestjs/swagger solo tiene examples, a diferencia de ApiResponse, que soporta ambos)
  • ApiBearerAuth() → se añade a security
  • ApiParam / ApiQuery / ApiHeader → se fusionan en parameters por nombre + ubicación: un parámetro genuinamente nuevo se añade, pero uno que ya existe (por ejemplo, @nestjs/swagger autogenera una entrada required: true a secas solo por reflection para cualquier argumento decorado con @Query()/@Param()) tiene sus campos superpuestos, no descartados. enum se lee de un array literal (enum: ['a', 'b']) o una referencia a un enum de TS, incluso uno importado de otro archivo (enum: Role), y el type del schema se infiere como number cuando todos los valores resueltos son numéricos, string en caso contrario

What this does not do

Con alcance intencional

Este comando tiene alcance intencional, no es una reimplementación completa de la semántica de decorators de @nestjs/swagger: anyOf (ninguna construcción de TS mapea a "cualquiera de estos" como una union mapea naturalmente a oneOf), links/callbacks (el propio @nestjs/swagger no tiene opción de decorator para ninguno de los dos), y cualquier argumento de decorator que no sea un literal (una variable, una llamada a función, un spread) se deja como está en vez de adivinarse. Es mejor dejar un campo como ya lo tenía el documento base que parchear algo incorrecto. Un tipo de retorno union o un payload de @Body() solo se convierte en oneOf cuando cada rama resuelve a un DTO/entity con nombre. Las rutas que un docs file documenta pero que no existen en --spec se reportan como warnings, no se descartan ni fallan en silencio.