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
npx nestjs-docfy patch-spec --spec <path-or-url> [options]# 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.jsonOpciones
| Option | Default | Description |
|---|---|---|
--spec <path|url> | (required) | A local openapi.json, or a URL (e.g. a running app's /api-json) |
--out <path> | stdout | Where to write the patched document |
--root <path> | . | Project root directory |
--tsconfig <path> | auto-detected | Path to tsconfig.json |
--pattern <glob> | **/*.controller.ts | Glob pattern to find controllers |
--format <format> | ts | Formato de docs file a buscar: ts o js |
--quiet | false | Suppress all output except errors |
Qué se fusiona
Emparejado por path + método HTTP contra el documento base:
ApiTags→ se une atags(nunca elimina tags que el documento base ya tenía)ApiOperation({ summary, description, deprecated })→ sobrescribe esos camposApiResponse({ status, description, schema, example, examples })→ se fusiona por código de estado (otros códigos de estado no se tocan); cuando no se daschema/type, recae en el tipo de retorno resuelto del propio método, la misma inferencia de DTO/class-validator/interface que ya hacegenerate, incluyendo unoneOfde$refs cuando el tipo de retorno es una unión de ≥2 DTOs/entities con nombre (ej.Promise<UserDto | AdminDto>)ApiBody({ schema, examples })→ establecerequestBody, con el mismo fallback estilo tipo-de-retorno al tipo resuelto del parámetro@Body(), incluyendo el mismo manejo de unión →oneOf(exampledeliberadamente no se lee aquí, ya que el tipo realApiBodyOptionsde@nestjs/swaggersolo tieneexamples, a diferencia deApiResponse, que soporta ambos)ApiBearerAuth()→ se añade asecurityApiParam/ApiQuery/ApiHeader→ se fusionan enparameterspor nombre + ubicación: un parámetro genuinamente nuevo se añade, pero uno que ya existe (por ejemplo,@nestjs/swaggerautogenera una entradarequired: truea secas solo por reflection para cualquier argumento decorado con@Query()/@Param()) tiene sus campos superpuestos, no descartados.enumse lee de un array literal (enum: ['a', 'b']) o una referencia a unenumde TS, incluso uno importado de otro archivo (enum: Role), y eltypedel schema se infiere comonumbercuando todos los valores resueltos son numéricos,stringen caso contrario
What this does not do
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.