Skip to content

@ApiTags({ name, parent, kind }) silently drops parent and kind #3922

@frbuceta

Description

@frbuceta

Is there an existing issue for this?

  • I have searched the existing issues

Current behavior

PR #3725 made @ApiTags() accept an ApiTagOptions object form so it can co-exist with strings:

@ApiTags({ name: 'Cats', parent: 'Animals', kind: 'navigation' })
class CatsController {}

However, parent and kind are silently discarded by the decorator implementation — only name is preserved. The PR's own test suite codifies this behavior at test/decorators/api-use-tags.decorator.spec.ts:

@ApiTags({ name: 'Cats', parent: 'Animals' })
objectWithParent() {}

// asserts:
expect(tags).toEqual(['Cats']);

The architecture supports this drop today: SwaggerScanner.scanApplication() does not aggregate tag definitions into document.tags — the root-level tags array is populated only by DocumentBuilder.addTag(). The decorator-level metadata has nowhere to land.

Net effect for consumers: passing @ApiTags({ name: 'Pets', parent: 'Animals' }) is exactly equivalent to @ApiTags('Pets'), but the type signature suggests otherwise.

Expected behavior

One of:

  1. Propagate@ApiTags(ApiTagOptions) aggregates definitions into document.tags, deduping by name with entries declared via DocumentBuilder.addTag(). This requires:

    • Decorator persists full options (in addition to/instead of just names).
    • A new global tag-definitions explorer.
    • Scanner aggregates and returns them in document.tags.
    • SwaggerModule.createDocument merges scanner-collected tags with config tags.
  2. Reject / warn — emit a runtime warning (or compile-time error via type narrowing) when hierarchy fields are set on @ApiTags, redirecting users to DocumentBuilder.addTag(). Less surprising than silent drop.

Option (1) matches what the type signature already promises and is what most users would expect. Option (2) keeps the current architecture intact.

Happy to open a PR once a direction is decided. Related: #3921 (adds summary field at the builder level — same OpenAPI 3.2 work, deliberately scoped narrower because of this open question).

Package version

@nestjs/swagger >= 11.4.0

What is the motivation / use case for changing the behavior?

OpenAPI 3.2 hierarchical tags are useful for organizing large APIs. Today users have to declare every tag's parent/kind via DocumentBuilder.addTag() in main.ts, even though @ApiTags() looks like it accepts the same options.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions