InteractiveFrameworks

Documented and live

Make the service readable and watchable: an OpenAPI document that says which routes need a token and what every body looks like, and adoptions announced to every connected screen the moment they are committed, and never when they are rolled back.

What you'll learn

  • Document a whole API with @nestjs/swagger: tags per controller, bearer authentication on the routes that need it, and DTO schemas with the same limits the validators enforce
  • Derive an update DTO with @nestjs/swagger's PartialType so the document describes it too
  • Announce a change from a service through a gateway the service injects
  • Announce only what was committed: after the transaction, never inside it

Two kinds of people meet the service from the outside. Developers building the shelter's website and app need to know what it accepts and answers without reading its source. And the screens in the shelter, the front desk and the kiosk in the hall, need to show an adoption the moment it happens, without asking every few seconds. This lesson serves both: an OpenAPI document generated from the code, and adoptions announced over WebSockets.

A document that cannot drift

@nestjs/swagger builds an OpenAPI document from the controllers and DTOs, so it changes when they do. Its frame comes from DocumentBuilder; SwaggerModule.setup('api', app, factory) serves the document at /api-json and an interactive UI at /api.

const config = new DocumentBuilder().setTitle('Kennels API').setVersion('2.0').addBearerAuth().build();
SwaggerModule.setup('api', app, () => SwaggerModule.createDocument(app, config));

What the scanner cannot see, you declare. Without the CLI plugin (a compiler step, and this runtime compiles in the browser), DTO properties need @ApiProperty(), and the limits the validators enforce should be repeated there, so the document says minLength: 2 where the API refuses one letter. @ApiTags() groups a controller's routes. addBearerAuth() declares the scheme, and @ApiBearerAuth() marks the routes that need it, on a method or a whole controller: the document should say a token is required exactly where the guard requires one, no more and no less.

One trap from the OpenAPI course: PartialType from @nestjs/mapped-types copies validation rules but not the document's metadata. Import it from @nestjs/swagger instead, which does both.

Announcing what happened

The gateway is the WebSockets course's: @WebSocketGateway() on the app's port, @WebSocketServer() for the server, and a method that emits cat:adopted to everyone. The service injects it and calls it, so neither the controller nor the transaction knows sockets exist.

The question is when. The adoption's writes happen inside a transaction that may still be rolled back, by the limit or by a failure of the next write. An announcement made inside it can tell every screen about an adoption that never happened, and an event, unlike a row, cannot be rolled back. So announce after the transaction has committed, with the adoption as it was read back:

const id = await this.dataSource.transaction(async (manager) => { /* writes, rules */ });
const booking = await this.bookings.findOneByOrFail({ id });
this.gateway.announce(booking);

Refused requests announce nothing. A new cat is not an adoption, so it announces nothing either.

Your task

Everything from lessons 1 to 4 still holds.

  1. An OpenAPI document, titled Whiskers Shelter API, version 1.0, with bearer authentication, served at /api and /api-json.
  2. Every controller tagged: health, auth, cats, adoptions. A bearer token documented on exactly the routes that need one: /auth/me, the cats' writes, and every adoptions route.
  3. Every DTO property documented with an example and the validators' limits; the update DTO documented too.
  4. A gateway on the app's port whose announce() sends cat:adopted with { id, name, adopter } to every connected client.
  5. A committed adoption is announced; a refused one, rolled back or never started, is not.

Then open the API's page in a new tab from the request panel, and try a request from the documentation with a token.

When it fails

  • The document lacks "security":[{"bearer":[]}] on a route: @ApiBearerAuth() is missing there, or addBearerAuth() from the builder.
  • UpdateCatDto has no properties in the document: PartialType still comes from @nestjs/mapped-types.
  • desk received [{"event":"cat:adopted",...}], expected nothing for the refused third adoption: the announcement is made inside the transaction, before the limit rolled it back.
  • desk could not connect: the gateway is not in the adoptions module's providers.

Remember

  • Generate the document from the code; declare with @ApiProperty() what the scanner cannot see.
  • Bearer auth in the document exactly where the guard enforces it.
  • PartialType from @nestjs/swagger for DTOs the document describes.
  • Announce after the commit: an event cannot be rolled back.
Stuck? Show a hint

main.ts: new DocumentBuilder().setTitle('Whiskers Shelter API').setVersion('1.0').addBearerAuth().build(), then SwaggerModule.setup('api', app, () => SwaggerModule.createDocument(app, config)). @ApiTags() on each controller, @ApiBearerAuth() on the routes that need a token (the whole adoptions controller), @ApiProperty() on each DTO property. adoptions.gateway.ts: @WebSocketGateway(), @WebSocketServer() server, announce() emits 'cat:adopted'. AdoptionsService injects the gateway and announces after the transaction resolves.