diff --git a/openapi/frameworks/laravel.mdx b/openapi/frameworks/laravel.mdx
index 2c4fa952..1b323df8 100644
--- a/openapi/frameworks/laravel.mdx
+++ b/openapi/frameworks/laravel.mdx
@@ -1,15 +1,15 @@
---
-title: How To Generate a OpenAPI/Swagger Spec for Laravel APIs
-description: "Learn how to create a Swagger/OpenAPI spec for your Laravel API."
+title: How to generate OpenAPI for Laravel APIs
+description: "Generate OpenAPI from Laravel API source code to avoid manual documentation."
---
import { Callout, YouTube } from "@/mdx/components";
-# How To Generate a OpenAPI for Laravel
+# How to generate OpenAPI for Laravel APIs
-You're investing in your API, and that means finally creating an OpenAPI document that accurately describes your API. With the rise in popularity of API-first design some APIs might have declared their OpenAPI before writing the code, but for many the code-first workflow is still fundamental for older APIs. If you're working with an existing Laravel application, you can generate a complete OpenAPI document directly from the API's source code.
+Investing in an API properly means creating an OpenAPI document that accurately describes the API, for documentation, testing, security scans, governance, and SDK generation. With the rise in popularity of API design-first some APIs might have declared their OpenAPI before writing the code, but for many the code-first workflow is still fundamental for older APIs. If you're working with an existing Laravel application, you can generate a complete OpenAPI document directly from the API's source code.
A few excellent tools have come and gone over the years, but these days [Scribe](https://scribe.knuckles.wtf/laravel) is the go to for generating API documentation form Laravel source code, and it happily exports OpenAPI to be used in a variety of other tools: like Speakeasy.
diff --git a/openapi/frameworks/nestjs.mdx b/openapi/frameworks/nestjs.mdx
index ba072aa3..5b4e01a5 100644
--- a/openapi/frameworks/nestjs.mdx
+++ b/openapi/frameworks/nestjs.mdx
@@ -1,579 +1,548 @@
---
title: How to generate an OpenAPI document with NestJS
-description: "Learn how to generate an OpenAPI document for your NestJS API and use it to automatically generate and customize client SDKs across different languages."
+description: "Learn how to generate an OpenAPI document for a NestJS Train Travel API and use it to generate SDKs with Speakeasy."
---
import { Callout } from "@/mdx/components";
# How to generate an OpenAPI document with NestJS
-This guide walks you through generating an OpenAPI document for a [NestJS](https://nestjs.com/) API and using Speakeasy to create an SDK based on the generated document.
+Writing the code for an API is just one piece of the puzzle. Maximizing benefits in every phase of the API lifecycle requires creating an OpenAPI document that accurately describes the API, enabling documentation, testing, security scans, governance, SDK generation, and more. OpenAPI is a powerful tool for API teams, but it can be daunting to create and maintain an OpenAPI document on top of building the actual API.
-
-Clone the [Speakeasy NestJS example repo](https://github.com/speakeasy-api/nestjs-openapi-example.git) to follow along with the example code in this tutorial. The `initial-app` branch has the initial state of the app that we'll use to start this tutorial.
+Thankfully NestJS takes a lot of the effort out of creating an OpenAPI document, leveraging DTOs, validation decorators, and response models in the code to describe a large part of the API contract. With that legwork done, the main focus for creating high-quality OpenAPI document is expanding the metadata that generates with contextual information and descriptions that make the document useful for everyone who interacts with it.
+
+This tutorial shows how to turn that existing structure into a useful OpenAPI document, and then use it with Speakeasy to generate SDKs.
+
+
+ Follow along with this guide using the sample code in the [Speakeasy examples repository](https://github.com/speakeasy-api/examples/), with this code living under
+ frameworks-nestjs. This sample code is based on the common OpenAPI example: the [Train Travel API](https://github.com/bump-sh-examples/train-travel-api).
-Here's what we'll do:
+Here is what this guide covers:
-1. Add the NestJS OpenAPI module to a NestJS project.
-2. Generate an OpenAPI document using the NestJS OpenAPI module.
-3. Improve the OpenAPI document for better downstream SDK generation.
-4. Use the Speakeasy CLI to generate an SDK based on the OpenAPI document.
-5. Use a Speakeasy OpenAPI extension to improve the generated SDK.
+1. Install and configure `@nestjs/swagger`.
+2. Generate an OpenAPI v3.2 document from NestJS controllers and DTOs.
+3. Add Scalar API Reference for interactive documentation.
+4. Export the OpenAPI document to YAML format.
+5. Add stable `operationId`, tags, and component schemas for SDK quality.
+6. Add security schemes and Speakeasy extensions.
+7. Generate SDKs from the OpenAPI document.
-We'll also take a look at how you can use the generated SDK.
+## Requirements
-Your NestJS project might not be as simple as our example app, but the steps below should translate well to any NestJS project.
+This guide assumes a NestJS application and basic familiarity with controllers, DTOs, and decorators, so the first step will be making sure the NestJS CLI is installed and the necessary dependencies are added to the project.
-## The SDK generation pipeline
+```bash filename="Terminal"
+npm install -g @nestjs/cli
+```
-NestJS has an [OpenAPI (Swagger) module](https://github.com/nestjs/swagger) for generating OpenAPI documents. We'll begin by installing, configuring, and initializing the NestJS [OpenAPI (Swagger) module](https://github.com/nestjs/swagger). We will also use the [Scalar UI](https://www.npmjs.com/package/@scalar/nestjs-api-reference) to add an interactive documentation UI for the API.
+Install Speakeasy CLI for SDK generation:
-The quality of your OpenAPI document determines the quality of generated SDKs and documentation, so we'll look into ways you can improve the generated document based on the Speakeasy [OpenAPI best practices](/docs/best-practices).
+```bash filename="Terminal"
+brew install speakeasy-api/homebrew-tap/speakeasy
+```
-We'll then use the improved OpenAPI document to generate an SDK using Speakeasy.
+Then add the necessary dependencies to your NestJS project via the preferred package manager:
-Finally, we'll use a simplified example to demonstrate how to use the SDK we generated and how to add SDK generation to a CI/CD pipeline so that Speakeasy automatically generates fresh SDKs whenever your NestJS API changes in the future.
+```bash filename="Terminal"
+npm install @nestjs/swagger @scalar/nestjs-api-reference class-transformer class-validator js-yaml
+```
-## Requirements
+To quickly break these down a bit:
-This guide assumes that you have an existing NestJS app (or a clone of our [example application](https://github.com/speakeasy-api/nestjs-openapi-example.git)) and basic familiarity with NestJS.
+- `@nestjs/swagger` - Generates OpenAPI documents from NestJS controllers and DTOs
+- `@scalar/nestjs-api-reference` - Serves interactive API documentation
+- `class-transformer` & `class-validator` - Enable DTO validation and transformation with OpenAPI decorators
+- `js-yaml` - Writes the OpenAPI document to YAML format
-The following should be installed on your machine:
+The specific versions will change over time, but the versions used in this guide are:
-- [Node.js version 16 or above](https://nodejs.org/en/download) (we used Node v20.17.0).
-- The [NestJS CLI](https://docs.nestjs.com/cli/overview), which can be installed with the following command once you have Node.js:
+```json filename="package.json"
+{
+ "dependencies": {
+ "@nestjs/swagger": "^11.4.4",
+ "@scalar/nestjs-api-reference": "^1.1.16",
+ "class-transformer": "^0.5.1",
+ "class-validator": "^0.15.1",
+ "js-yaml": "^4.1.1"
+ }
+}
+```
- ```bash filename="Terminal"
- npm install -g @nestjs/cli
- ```
+## Teach NestJS to talk OpenAPI
-- The [JS-YAML](https://www.npmjs.com/package/js-yaml) package, which we'll use to convert the OpenAPI document to a YAML file.
-- The [Speakeasy CLI](/docs/speakeasy-cli/getting-started), which we'll use to generate an SDK from the OpenAPI document.
+NestJS uses the `@nestjs/swagger` package in `src/main.ts`, starting with the generic metadata that describes the API as a whole. This includes the title, description, version, and server URLs, and authentication schemes. This is the foundation of the OpenAPI document, and the parts that aren't automatically generated from controllers and DTOs:
+## Describe routes with OpenAPI decorators
-## Adding `@nestjs/swagger` to a NestJS project
+NestJS route decorators define runtime behavior. OpenAPI decorators add the details needed for generated docs and SDKs.
-Install the NestJS OpenAPI (Swagger) and Scalar API Reference modules:
+Start with one resource at a time. That keeps each controller small and makes it easier to verify that tags, parameters, and response schemas are being emitted the way the document needs them.
-```bash filename="Terminal"
- npm install --save @nestjs/swagger @scalar/nestjs-api-reference
-```
+```typescript filename="src/main.ts"
+import { ValidationPipe } from '@nestjs/common';
+import { NestFactory } from '@nestjs/core';
+import { Station } from './entities/station.entity';
+import { TrainService } from './train.service';
+import {
+ DocumentBuilder,
+ SwaggerDocumentOptions,
+ SwaggerModule,
+} from '@nestjs/swagger';
+import { AppModule } from './app.module';
+
+async function bootstrap() {
+ const app = await NestFactory.create(AppModule);
+
+ app.useGlobalPipes(
+ new ValidationPipe({
+ whitelist: true,
+ forbidNonWhitelisted: true,
+ transform: true,
+ errorHttpStatusCode: 400,
+ }),
+ );
+
+ const config = new DocumentBuilder()
+ .setOpenAPIVersion('3.2.0')
+ .setTitle('Train Travel API')
+ .setDescription('Find stations, discover trips, and create bookings across Europe.')
+ .setVersion('1.2.1')
+ .addServer('https://api.example.com', 'Production server')
+ .addServer('http://localhost:3000', 'Local development server')
+ .addBearerAuth(
+ {
+ type: 'http',
+ scheme: 'bearer',
+ bearerFormat: 'JWT',
+ description: 'Bearer token authentication',
+ },
+ 'BearerAuth',
+ )
+ .build();
-In the `bootstrap` function of your application entry file, initialize Swagger using the `SwaggerModule` class:
+ const options: SwaggerDocumentOptions = {
+ operationIdFactory: (_controllerKey: string, methodKey: string) => methodKey,
+ };
-```typescript filename="main.ts"
-const config = new DocumentBuilder()
- .setTitle('Pet API')
- .setDescription('Create a cat or dog record and view pets by id')
- .setVersion('1.0')
- .addTag('library')
- .build();
+ const document = SwaggerModule.createDocument(app, config, options);
-const document = SwaggerModule.createDocument(app, config); // serializable object - conform to OpenAPI
-SwaggerModule.setup('api', app, document, {
- swaggerUiEnabled: false,
-});
+ // not actually... doing anything with it yet!
-app.use(
- '/api',
- apiReference({
- spec: {
- content: document,
- },
- }),
-);
+ await app.listen(3000);
+}
+
+bootstrap();
```
-Add the required imports:
+So far nothing is actually being done with all this, the variables are just going to waste. One of the first things people generally do is emit human-readable API documentation using open-source tools like Scalar.
+
+## Add Scalar API Reference
-```typescript filename="main.ts"
-import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
+Scalar provides an interactive API documentation interface based on the OpenAPI document. Add it to `src/main.ts` to serve documentation at the `/api` endpoint:
+
+```typescript filename="src/main.ts"
import { apiReference } from '@scalar/nestjs-api-reference';
-```
-In the above code:
+// ... existing imports and config ...
-- The `SwaggerModule.createDocument` method returns a serializable [OpenAPI document](https://spec.openapis.org/oas/v3.0.4.html#openapi-document) object that we'll convert to an OpenAPI YAML document file using JS-YAML.
-- We use `DocumentBuilder` to create the base structure of the OpenAPI document. The [`DocumentBuilder` methods](https://github.com/nestjs/swagger/blob/master/lib/document-builder.ts) set the properties that identify the purpose and owner of the document, such as the title and description properties.
-- We use the `createDocument()` method to define the API routes by passing in two arguments: the `app` instance and the document `config`. We can also provide a third argument, [`SwaggerDocumentOptions`](https://docs.nestjs.com/openapi/introduction#document-options).
-- We use `SwaggerModule.setup()` to expose the OpenAPI document at `/api-yaml` for the YAML format and `/api-json` for the JSON format.
-- The `app.use('/api', ...)` method mounts the Scalar API Reference component to the `/api` route. The `apiReference` function takes the `document` object as a parameter, which represents the OpenAPI document.
+async function bootstrap() {
+ // ... existing app setup ...
-Run the NestJS HTTP development server:
+ const document = SwaggerModule.createDocument(app, config, options);
-```bash filename="Terminal"
-npm run start:dev
+ writeFileSync('openapi.yaml', yaml.dump(document, { noRefs: true }));
+
+ SwaggerModule.setup('api-json', app, document);
+Resource-based controllers keep those operation names easy to map back to the NestJS code.
+ app.use('/api', apiReference({ spec: { content: document } }));
+
+ await app.listen(3000);
+}
+
+bootstrap();
```
-Open your browser and navigate to [`http://localhost:3000/api`](http://localhost:3000/api). You should see the Scalar UI with three API endpoints in the sidebar.
+Run the development server and open `http://localhost:3000/api` to see the Scalar UI:

-For each API endpoint, you can see which parameters are required and try out the different API endpoints.
+The API endpoints are listed in the navigation pane on the left. Click any endpoint to see its details, including path parameters, request body, and responses. The right panel shows example requests in different languages and libraries.
-Open `http://localhost:3000/api-yaml` to see the following basic OpenAPI document in YAML format:
+
-```yaml
-openapi: 3.0.0
-paths:
- /pets:
- post:
- operationId: PetsController_create
- parameters: []
- responses:
- "201":
- description: ""
- /pets/cats/{id}:
- get:
- operationId: PetsController_findOneCat
- parameters:
- - name: id
- required: true
- in: path
- schema:
- type: string
- responses:
- "200":
- description: ""
- /pets/dogs/{id}:
- get:
- operationId: PetsController_findOneDog
- parameters:
- - name: id
- required: true
- in: path
- schema:
- type: string
- responses:
- "200":
- description: ""
-info:
- title: Pet API
- description: Create a cat or dog record and view pets by id
- version: "1.0"
- contact: {}
-tags:
- - name: library
- description: ""
-servers: []
-components:
- schemas: {}
-```
+Click the **Test Request** button to open an API client that lets you test the endpoints directly from the documentation.
+
+## Export OpenAPI from NestJS
+
+Having the documentation come right out of the application is handy, but many tools (like Speakeasy) will need an actual `openapi.yaml` to work with. To automate this, the build script can be configured to export the OpenAPI as a YAML file, by adding `fs` and `js-yaml` dependencies to `src/main.ts`:
-Note that the document uses OpenAPI Specification version 3.0.0.
+```typescript filename="src/main.ts"
+import { writeFileSync } from 'fs';
+import * as yaml from 'js-yaml';
-## Supported OpenAPI Specification versions in NestJS and Speakeasy
+// ... existing imports ...
-Speakeasy currently supports the OpenAPI Specification versions 3.0.x and 3.1.x. We recommend using OpenAPI Specification version 3.1 if possible, as it's fully compatible with [JSON Schema](https://json-schema.org/), which gives you access to a large ecosystem of tools and libraries. NestJS supports OpenAPI Specification version 3.0.x.
+async function bootstrap() {
+ // ... existing app setup ...
-## Adding OpenAPI `info` in NestJS
+ const document = SwaggerModule.createDocument(app, config, options);
-Let's improve the OpenAPI document by better describing it. We'll add more fields to the [info object](https://spec.openapis.org/oas/v3.0.4.html#info-object), which contains metadata about the API.
+ writeFileSync('openapi.yaml', yaml.dump(document, { noRefs: true }));
-Add the following `DocumentBuilder` methods to the `config` section of the document (`main.ts`) to supply more data about the API:
+ await app.listen(3000);
+}
-```typescript filename="main.ts"
-.setContact(
- 'Speakeasy support',
- 'http://www.example.com/support',
- 'support@example.com',
-)
-.setTermsOfService('http://example.com/terms/')
-.setLicense(
- 'Apache 2.0',
- 'https://www.apache.org/licenses/LICENSE-2.0.html',
-)
+bootstrap();
```
-## Updating NestJS to generate OpenAPI components schemas
+This writes the OpenAPI document to `openapi.yaml` in the project root each time the application starts. The `noRefs` option ensures that all YAML references are expanded inline, which some tools prefer.
-In the example app, [NestJS core decorators](https://docs.nestjs.com/custom-decorators) define the structure and functionality of the Pets Controller and its API routes.
+## Describe routes with OpenAPI decorators
-We can add [OpenAPI decorators](https://docs.nestjs.com/openapi/decorators) to better describe our API. The OpenAPI document lacks details about the POST request body, data schema, and API response.
+NestJS uses route decorators to define all sorts of behavior, and OpenAPI is no different. Using decorators like `@ApiOperation`, `@ApiQuery`, and `@ApiOkResponse` allows details to be added to improve the quality of the OpenAPI.
-Add the following OpenAPI decorators, with the `Api` prefix to distinguish them from the core decorators, to the `@Get('cats/:id')` route handler:
+```typescript filename="src/train/stations.controller.ts"
+import { Controller, Get, Query } from '@nestjs/common';
+import { ApiOkResponse, ApiOperation, ApiQuery } from '@nestjs/swagger';
+import { Station } from './entities/station.entity';
+import { TrainService } from './train.service';
-```typescript filename="pets.controller.ts"
-@ApiOperation({ summary: 'Get cat' })
-@ApiResponse({
- description: 'The found record',
- type: Cat,
-})
-@ApiBadRequestResponse({ description: 'Bad Request' })
+@Controller('stations')
+export class StationsController {
+ constructor(private readonly trainService: TrainService) {}
+
+ @Get()
+ @ApiOperation({ summary: 'Get a list of train stations' })
+ @ApiQuery({ name: 'search', required: false, type: String })
+ @ApiQuery({ name: 'country', required: false, type: String })
+ @ApiOkResponse({ type: [Station] })
+ getStations(
+ @Query('search') search?: string,
+ @Query('country') country?: string,
+ ): Station[] {
+ return this.trainService.getStations(search, country);
+ }
+}
```
-Import these decorators from `'@nestjs/swagger'`:
+The NestJS decorators define the controller name via `@Controller` which helps create the URL, and `@Get` makes it clear it's a `GET /stations`. The OpenAPI decorators add the summary, query parameter details, and response schema that make the generated documentation useful for end-users.
-```typescript filename="pets.controller.ts"
-import {
- ApiBadRequestResponse,
- ApiBody,
- ApiExtension,
- ApiForbiddenResponse,
- ApiOkResponse,
- ApiOperation,
- ApiResponse,
- ApiTags,
- getSchemaPath,
-} from '@nestjs/swagger';
-```
+Mixing core NestJS decorators with OpenAPI-specific decorators allows NestJS to cover the core basics and avoid repetition. There are some frameworks force developers to write the URL, HTTP methods and status codes again, despite it being right there a few lines away. Now that frameworks are a bit smarter they cut down the amount of OpenAPI specific stuff that needs to be written.
-Add the following OpenAPI decorators to the `@Get('dogs/:id')` route handler:
+The reason for both `@Query` and `@ApiQuery` is that core NestJS does not know about OpenAPI, so it has nowhere for the extra properties to go like `required` and `type`.
-```typescript filename="pets.controller.ts"
-@ApiOperation({ summary: 'Get dog' })
-@ApiResponse({
- description: 'The found record',
- type: Dog,
-})
-@ApiBadRequestResponse({ description: 'Bad Request' })
-```
+Add DTO-backed query parameters for routes like `GET /trips`:
-Add the following OpenAPI decorators to the `@Post()` route handler:
-
-```typescript filename="pets.controller.ts"
-@ApiOperation({ summary: 'Create a pet' })
-@ApiBody({
- schema: {
- oneOf: [{ $ref: getSchemaPath(Cat) }, { $ref: getSchemaPath(Dog) }],
- discriminator: {
- propertyName: 'type',
- mapping: {
- cat: getSchemaPath(Cat),
- dog: getSchemaPath(Dog),
- },
- },
- },
- description: 'Create a pet cat or dog',
-})
-@ApiOkResponse({
- schema: {
- oneOf: [{ $ref: getSchemaPath(Cat) }, { $ref: getSchemaPath(Dog) }],
- discriminator: {
- propertyName: 'type',
- mapping: {
- cat: getSchemaPath(Cat),
- dog: getSchemaPath(Dog),
- },
- },
- },
-})
-@ApiForbiddenResponse({ description: 'Forbidden' })
-@ApiBadRequestResponse({ description: 'Bad Request' })
-```
+```typescript filename="src/train/dto/trips-query.dto.ts"
+import { IsBoolean, IsDateString, IsOptional, IsUUID } from 'class-validator';
-The `@ApiBody()` and `@ApiOkResponse` decorators use the [Schema Object](https://spec.openapis.org/oas/v3.0.4.html#schemaObject), which defines the input and output data types. The allowed data types are defined by the `Cat` and `Dog` data transfer objects (DTO) schema. A DTO schema defines how data will be sent over the network.
+export class TripsQueryDto {
+ /**
+ * Origin station ID.
+ * @example 'efdbb9d1-02c2-4bc3-afb7-6788d8782b1e'
+ */
+ @IsUUID()
+ readonly origin!: string;
-Now, run the NestJS HTTP server and open `http://localhost:3000/api-yaml/`. You'll see the OpenAPI endpoints description is more fleshed out.
+ /**
+ * Destination station ID.
+ * @example 'b2e783e1-c824-4d63-b37a-d8d698862f1d'
+ */
+ @IsUUID()
+ readonly destination!: string;
-The POST request originally looked like the following:
+ /**
+ * Desired departure date in ISO 8601.
+ * @example '2026-05-21T09:00:00Z'
+ */
+ @IsDateString()
+ readonly date!: string;
-```yaml
-/pets:
- post:
- operationId: PetsController_create
- parameters: []
- responses:
- "201":
- description: ""
-```
+ @IsOptional()
+ @IsBoolean()
+ readonly bicycles?: boolean;
-It should now look as follows:
-
-```yaml focus=6:38
-/pets:
- post:
- operationId: PetsController_create
- summary: Create pet
- parameters: []
- requestBody:
- required: true
- description: Create a pet cat or dog
- content:
- application/json:
- schema:
- oneOf:
- - $ref: "#/components/schemas/Cat"
- - $ref: "#/components/schemas/Dog"
- discriminator:
- propertyName: type
- mapping:
- cat: "#/components/schemas/Cat"
- dog: "#/components/schemas/Dog"
- responses:
- "200":
- description: ""
- content:
- application/json:
- schema:
- oneOf:
- - $ref: "#/components/schemas/Cat"
- - $ref: "#/components/schemas/Dog"
- discriminator:
- propertyName: type
- mapping:
- cat: "#/components/schemas/Cat"
- dog: "#/components/schemas/Dog"
- "400":
- description: Bad Request
- "403":
- description: Forbidden
+ @IsOptional()
+ @IsBoolean()
+ readonly dogs?: boolean;
+}
```
-The [Reference Object](https://spec.openapis.org/oas/v3.0.4.html#reference-object) (`$ref`) is a reference identifier that specifies the location, as a URI, of the value being referenced. It references the `schemas` field of the [Components Object](https://spec.openapis.org/oas/v3.0.4.html#components-object), which holds reusable schema objects.
+Then use it in the trips controller:
-If you look at the `components` schema, you'll see the `properties` objects are empty.
+```typescript filename="src/train/trips.controller.ts"
+import { Controller, Get, Query } from '@nestjs/common';
+import { ApiOkResponse, ApiOperation } from '@nestjs/swagger';
+import { TripsQueryDto } from './dto/trips-query.dto';
+import { Trip } from './entities/trip.entity';
+import { TrainService } from './train.service';
-```yaml mark=5,8
-components:
- schemas:
- Cat:
- type: object
- properties: {}
- Dog:
- type: object
- properties: {}
-```
-
-To make the model properties visible to the `SwaggerModule`, we can annotate each property using the `@ApiProperty()` decorator. For example:
+@Controller('trips')
+export class TripsController {
+ constructor(private readonly trainService: TrainService) {}
-```typescript filename="cat.entity.ts" mark=1
-@ApiProperty({ example: 'Panama', description: 'The name of the cat' })
-@IsString()
-readonly name: string;
+ @Get()
+ @ApiOperation({ summary: 'Get available train trips' })
+ @ApiOkResponse({ type: [Trip] })
+ getTrips(@Query() query: TripsQueryDto): Trip[] {
+ return this.trainService.getTrips(query);
+ }
+}
```
-This can be tedious, especially with medium- to large-sized projects. You can use the NestJS [Swagger CLI plugin](https://docs.nestjs.com/openapi/cli-plugin#cli-plugin) to automate this annotation.
+## Ensure schemas include property metadata
+
+Without the Swagger plugin, generated component schemas can stay too thin for high-quality docs or SDKs. Enabling comment introspection fills in descriptions and examples from the model definitions already used by the app.
-To enable the plugin, open your `nest-cli.json` file, add the following `plugins` configuration, and restart the server:
+Enable the Nest Swagger plugin in `nest-cli.json`:
-```json filename="nest-cli.json" mark=7
+```json filename="nest-cli.json"
{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"deleteOutDir": true,
- "plugins": ["@nestjs/swagger"]
+ "plugins": [
+ {
+ "name": "@nestjs/swagger",
+ "options": {
+ "introspectComments": true
+ }
+ }
+ ]
}
}
```
-You'll now see that the `properties` fields are populated as follows:
+With comment introspection enabled, doc comments on DTO and entity fields become descriptions and examples in `components.schemas`.
-```yaml
-components:
- schemas:
- Cat:
- type: object
- properties:
- type:
- type: string
- name:
- type: string
- age:
- type: number
- breed:
- type: string
- environment:
- type: string
- enum:
- - indoor
- - outdoor
- required:
- - type
- - name
- - age
- - breed
- - environment
- Dog:
- type: object
- properties:
- type:
- type: string
- name:
- type: string
- age:
- type: number
- breed:
- type: string
- size:
- type: string
- enum:
- - small
- - medium
- - large
- required:
- - type
- - name
- - age
- - breed
- - size
-```
+Example entity:
-The plugin annotates all DTO properties with the `@ApiProperty` decorator, sets the `type` or `enum` property depending on the type, sets validation rules based on `class-validator` decorators, and carries out various other [automated actions](https://docs.nestjs.com/openapi/cli-plugin#overview).
+```typescript filename="src/train/entities/station.entity.ts"
+import { IsString, IsUUID } from 'class-validator';
-You can generate descriptions for properties and endpoints, and create example values for properties based on comments:
+export class Station {
+ /**
+ * Unique identifier for the station.
+ * @example 'efdbb9d1-02c2-4bc3-afb7-6788d8782b1e'
+ */
+ @IsUUID()
+ readonly id!: string;
-```typescript filename="cat.entity.ts" mark=1:4
/**
- * The type of pet
- * @example 'cat'
+ * Human-readable station name.
+ * @example 'Berlin Hauptbahnhof'
*/
- @IsEnum(['cat'])
- readonly type: 'cat';
+ @IsString()
+ readonly name!: string;
+}
```
-For this to work, `introspectComments` must be set to `true` in the `options` property of the plugin:
+These objects are then used in the OpenAPI document as response schemas, and the descriptions and examples are included in the generated documentation and SDKs.
-```json filename="nest-cli.json"
-"plugins": [
- {
- "name": "@nestjs/swagger",
- "options": {
- "introspectComments": true
- }
- }
-]
-```
-The example and description will then be added to the OpenAPI document `components` schema:
+## Describing request bodies and non-200 responses
-```yaml focus=8:9
-components:
- schemas:
- Cat:
- type: object
- properties:
- type:
- type: string
- description: The type of pet
- example: cat
+```typescript filename="src/train/bookings.controller.ts" focus=25:33
+import {
+ Body,
+ Controller,
+ HttpCode,
+ Param,
+ Post,
+} from '@nestjs/common';
+import {
+ ApiBody,
+ ApiCreatedResponse,
+ ApiExtension,
+ ApiOkResponse,
+ ApiOperation,
+ ApiParam,
+ ApiTags,
+} from '@nestjs/swagger';
+import { CreateBookingDto } from './dto/create-booking.dto';
+import { Booking } from './entities/booking.entity';
+import { TrainService } from './train.service';
+
+@Controller('bookings')
+export class BookingsController {
+ constructor(private readonly trainService: TrainService) {}
+
+ @Post()
+ @HttpCode(201)
+ @ApiOperation({ summary: 'Create a booking' })
+ @ApiBody({ type: CreateBookingDto })
+ @ApiCreatedResponse({ type: Booking })
+ createBooking(@Body() payload: CreateBookingDto): Booking {
+ return this.trainService.createBooking(payload);
+ }
+}
```
-Add comments that provide a description and example value for each property of the `Cat` and `Dog` entities.
-
-The Scalar UI will allow you to access the example value for the request body and a successful response. Click on the **Test Request** button of the request to display a modal, where you can try a request to the endpoint:
-
-
+The `@ApiBody()` decorator describes the request body schema, and `@HttpCode(201)` makes the success status explicit. NestJS uses `201 Created` for `POST` handlers by default, but showing it in the controller keeps the intent obvious when scanning the code.
-You can see an example of a request body in the **Body** section of the modal:
+The request body for `createBooking` looks like this:
-
-
-By toggling the **200** option under **Responses**, you can also see the details of the example data schemas:
-
-
+```json filename="create-booking.json"
+{
+ "trip_id": "ea399ba1-6d95-433f-92d1-83f67b775594",
+ "passenger_name": "John Doe",
+ "has_bicycle": true,
+ "has_dog": false
+}
+```
-## Customizing the OpenAPI `operationId` with NestJS
+This produces a `POST /bookings` operation in the OpenAPI document with a `requestBody` section and a `201` response.
-In the OpenAPI document, each HTTP request has an `operationId` that identifies the operation. In SDKs, the `operationId` is also used to generate method names and documentation.
+## Add stable `operationId` values
-By default, the NestJS OpenAPI (Swagger) module uses the NestJS `controllerKey` and `methodKey` to name the `operationID` something like `PetsController_findOneDog`.
+SDK method names are derived from `operationId`, and SDK groupings are derived from tags.
-A long operation name is not ideal. We can use the `operationIdFactory` method in the [Swagger document options](https://docs.nestjs.com/openapi/introduction#document-options) to instruct the module to generate more concise names using only the `methodKey`.
+Default operation names can become verbose, especially once controller names are included. Tightening them here keeps generated SDK method names shorter and easier to scan.
-Define the following `options` in the `bootstrap` function:
+Use `SwaggerDocumentOptions` to keep `operationId` values short and stable:
-```typescript filename="main.ts"
+```typescript filename="src/main.ts"
const options: SwaggerDocumentOptions = {
- operationIdFactory: (controllerKey: string, methodKey: string) => methodKey,
+ operationIdFactory: (_controllerKey: string, methodKey: string) => methodKey,
};
-```
-Pass the `options` to the `SwaggerModule` as follows:
-
-```typescript filename="main.ts"
const document = SwaggerModule.createDocument(app, config, options);
```
-Import `SwaggerDocumentOptions` from `@nestjs/swagger`:
+This keeps operation names like `getStations`, `getTrips`, and `createBooking` instead of controller-prefixed names.
-```typescript filename="main.ts" focus=4
-import {
- SwaggerModule,
- DocumentBuilder,
- SwaggerDocumentOptions,
-} from '@nestjs/swagger';
-```
-
-## Adding OpenAPI tags to NestJS routes
-
-Whether you're building a big application or only have a handful of operations, we recommend adding tags to all your NestJS routes so you can group them by tag in the generated SDK code and documentation.
-
-### Adding tags
+## Tagging routes for better docs and SDKs
-To add an OpenAPI tag to a route in NestJS, add the `@ApiTags` decorator:
+Use route tags to keep generated docs and SDK groups readable. Each controller now maps to one resource area, and the document builder defines the hierarchy up front:
-```typescript filename="pets.controller.ts"
-@Get('cats/:id')
-@ApiTags('cats')
+```typescript filename="src/main.ts"
+const config = new DocumentBuilder()
+ .setOpenAPIVersion('3.2.0')
+ .addTag('Resources', 'Top-level navigation tag for related resources', undefined, {
+ kind: 'nav',
+ })
+ .addTag('Stations', 'Find and filter train stations', undefined, {
+ parent: 'Resources',
+ })
+ .addTag('Trips', 'Timetables and routes for train trips', undefined, {
+ parent: 'Resources',
+ })
+ .addTag('Bookings', 'Create and manage trip bookings', undefined, {
+ parent: 'Resources',
+ })
+ .build();
```
-### Adding metadata to tags
-
-We've already added metadata to the `@ApiTags('cats')` decorator using other decorators provided by `@nestjs/swagger`, such as `@ApiOperation` and `@ApiResponse`.
+OpenAPI 3.2 improves the [Tag Object](https://spec.openapis.org/oas/v3.2.0#tag-object) so tags can be organized into a hierarchy with the `parent` property, and annotated with a `kind` property. The `kind` property is a free-form, machine-readable string that hints how the tag should be used by various tools, but the common usage is: nav, badge, or audience.
-We can add metadata to the root tag field in the OpenAPI document.
+The generated OpenAPI document should then look a bit like this.
-Add the following to the `config` section of the OpenAPI document:
+```yaml filename="openapi.yaml"
+tags:
+ - name: Resources
+ description: Top-level navigation tag for related resources
+ kind: nav
+ - name: Stations
+ description: Find and filter train stations
+ parent: Resources
+ - name: Trips
+ description: Timetables and routes for train trips
+ parent: Resources
+ - name: Bookings
+ description: Create and manage trip bookings
+ parent: Resources
+```
+
+Then, each controller only needs to reference the matching tag, and the document stays organized with a clear hierarchy.
+
+```typescript filename="src/train/bookings.controller.ts"
+import {
+ // ...
+ ApiTags,
+} from '@nestjs/swagger';
-```typescript filename="main.ts"
-.addTag('Pets', 'Pets operations', {
- url: 'https://example.com/api',
- description: 'Operations API endpoint',
-})
+@ApiTags('Bookings')
+@Controller('bookings')
+export class BookingsController { }
```
-You can add more than one tag by using additional `.addTag()` method calls:
-
-```typescript filename="main.ts"
-.addTag('cats')
-.addTag('dogs')
-```
+The controller is tagged with Bookings so all of the routes/operations within it will just share that tag. The generated OpenAPI document will then group all of those routes together under the Bookings section in the Scalar UI, and SDKs will also group methods under that tag.
-## Adding a list of servers to the NestJS OpenAPI document
+## Add security and Speakeasy retries
-When validating an OpenAPI document, Speakeasy expects a list of servers at the root of the OpenAPI document. We'll add a server using the `DocumentBuilder` method, `addServer()`.
+After the basic route and schema structure is in place, add the parts that most directly affect SDK behavior: authentication and retry behavior.
-Insert the `addServer()` method in the `config` of the OpenAPI document:
+Define security schemes in `DocumentBuilder`:
-```typescript filename="main.ts"
-.addServer('http://localhost:3000/', 'Development server')
+```typescript filename="src/main.ts"
+const config = new DocumentBuilder()
+ .addBearerAuth(
+ {
+ type: 'http',
+ scheme: 'bearer',
+ bearerFormat: 'JWT',
+ description: 'Bearer token authentication',
+ },
+ 'BearerAuth',
+ )
+ .addOAuth2(
+ {
+ type: 'oauth2',
+ flows: {
+ authorizationCode: {
+ authorizationUrl: 'https://auth.example.com/oauth2/authorize',
+ tokenUrl: 'https://auth.example.com/oauth2/token',
+ scopes: {
+ 'bookings:read': 'Read bookings',
+ 'bookings:write': 'Create and update bookings',
+ },
+ },
+ },
+ },
+ 'OAuth2',
+ )
+ .build();
```
-## Adding retries to your SDK with `x-speakeasy-retries`
+Apply those schemes at the controller level:
-[OpenAPI document extensions](/openapi/extensions) allow us to add vendor-specific functionality to the OpenAPI document.
+```typescript filename="src/train/bookings.controller.ts"
+import { ApiBearerAuth, ApiOAuth2 } from '@nestjs/swagger';
-- Extension fields must be prefixed with `x-`.
-- Speakeasy uses extensions that start with `x-speakeasy-`.
-
-Let's use a Speakeasy extension that adds retries to requests from Speakeasy SDKs by adding a top-level `x-speakeasy-retries` schema to our OpenAPI document. We can also override the retry strategy per operation.
-
-### Adding global retries
+@ApiBearerAuth('BearerAuth')
+@ApiOAuth2(['bookings:read', 'bookings:write'], 'OAuth2')
+@Controller('bookings')
+export class BookingsController {}
+```
-Apply the Speakeasy retries extension globally by adding the `addExtension()` method from `DocumentBuilder` to the `config` section of the OpenAPI document:
+Add Speakeasy retry behavior globally:
-```typescript filename="main.ts"
-.addExtension('x-speakeasy-retries', {
- strategy: 'backoff',
- backoff: {
- initialInterval: 500,
- maxInterval: 60000,
- maxElapsedTime: 3600000,
- exponent: 1.5,
- },
- statusCodes: ['5XX'],
- retryConnectionErrors: true,
-})
+```typescript filename="src/main.ts"
+const config = new DocumentBuilder()
+ .addExtension('x-speakeasy-retries', {
+ strategy: 'backoff',
+ backoff: {
+ initialInterval: 500,
+ maxInterval: 60000,
+ maxElapsedTime: 3600000,
+ exponent: 1.5,
+ },
+ statusCodes: ['5XX'],
+ retryConnectionErrors: true,
+ })
+ .build();
```
-### Adding retries per method
+Override retries for one operation when needed:
-To create a unique retry strategy for a single route, use the `ApiExtension` decorator to add `x-speakeasy-retries` to a NestJS controller route handler as follows:
-
-```typescript filename="pets.controller.ts"
+```typescript filename="src/train/bookings.controller.ts"
+@Post('bookings')
+@ApiTags('Bookings')
+@ApiOperation({ summary: 'Create a booking' })
+@ApiBody({ type: CreateBookingDto })
+@ApiCreatedResponse({ type: Booking })
@ApiExtension('x-speakeasy-retries', {
strategy: 'backoff',
backoff: {
@@ -585,194 +554,122 @@ To create a unique retry strategy for a single route, use the `ApiExtension` dec
statusCodes: ['5XX'],
retryConnectionErrors: true,
})
+createBooking(@Body() payload: CreateBookingDto): Booking {
+ return this.trainService.createBooking(payload);
+}
```
-## Generating an SDK based on your OpenAPI document
+## Generate SDKs from the OpenAPI document
-Before creating an SDK, we need to save the NestJS-generated OpenAPI document to a file. We'll use JS-YAML to do this.
+With the OpenAPI document exported to `openapi.yaml`, use it to generate SDKs with Speakeasy.
-### Saving the OpenAPI document to a YAML file
+The generated file contains OpenAPI 3.2 metadata, resource-based route paths, route parameters derived from DTOs, security requirements, and component schemas generated from entities.
-Add the following imports to your application entry file:
+Example output:
-```typescript filename="main.ts"
-import * as yaml from 'js-yaml';
-import { writeFileSync } from 'fs';
-```
+```yaml filename="openapi.yaml"
+openapi: 3.2.0
-In the `bootstrap` function, convert the OpenAPI document to a YAML string and save it as a file:
-
-```typescript filename="main.ts"
-const yamlString = yaml.dump(document);
-writeFileSync('openapi.yaml', yamlString);
-```
-
-When you run the NestJS dev server, an `openapi.yaml` file will be generated in your root directory.
-
-### Linting the OpenAPI document with Speakeasy
+paths:
+ /stations:
+ get:
+ operationId: getStations
+ tags:
+ - Resources
+ - Stations
-The Speakeasy CLI has an OpenAPI [linting](/docs/linting) command that checks the OpenAPI document for errors and style issues.
+ /trips:
+ get:
+ operationId: getTrips
+ parameters:
+ - name: origin
+ required: true
+ in: query
+ schema:
+ type: string
+ format: uuid
-Run the linting command:
+components:
+ securitySchemes:
+ OAuth2:
+ type: oauth2
+ flows:
+ authorizationCode:
+ authorizationUrl: https://auth.example.com/oauth2/authorize
+ tokenUrl: https://auth.example.com/oauth2/token
+ scopes:
+ 'bookings:read': Read bookings
+ 'bookings:write': Create and update bookings
-```bash filename="Terminal"
-speakeasy lint openapi --schema ./openapi.yaml
+ schemas:
+ Station:
+ type: object
+ properties:
+ id:
+ type: string
+ format: uuid
+ name:
+ type: string
```
-A lint report will be displayed in the terminal, showing errors, warnings, and hints:
-
-
-
-The Speakeasy Linter uses a [recommended set of rules](/docs/linting/linting#speakeasy-recommended), which you can [configure](/docs/linting#configuration).
-
-### Generating an SDK from the Speakeasy CLI
-
-We'll use the [`quickstart`](/docs/speakeasy-cli/quickstart) command for a guided SDK setup.
-
-Run the command using the Speakeasy CLI:
+Generate an SDK:
```bash filename="Terminal"
speakeasy quickstart
```
-Following the prompts, provide the OpenAPI document location, name the SDK `SDK`, and select `TypeScript` as the SDK language.
-
-In the terminal, you'll see the steps taken by Speakeasy to generate the SDK.
-
-```txt
-│ Workflow - success
-│ └─Target: sdk - success
-│ └─Source: SDK-OAS - success
-│ └─Validating Document - success
-│ └─Diagnosing OpenAPI - success
-│ └─Tracking OpenAPI Changes - success
-│ └─Snapshotting OpenAPI Revision - success
-│ └─Storing OpenAPI Revision - success
-│ └─Computing Document Changes - success
-│ └─Downloading prior revision - success
-│ └─Computing changes - success
-│ └─Uploading changes report - success
-│ └─Validating gen.yaml - success
-│ └─Generating Typescript SDK - success
-│ └─Setup Environment - success
-│ └─Load and Validate Document - success
-│ └─Generate SDK - success
-│ └─Compile SDK - success
-│ └─Setup Environment - success
-│ └─Load and Validate Document - success
-│ └─Generate SDK - success
-│ └─Generating Code Samples - success
-│ └─Snapshotting Code Samples - success
-│ └─Snapshotting Code Samples - success
-│ └─Uploading Code Samples - success
-```
-
-Speakeasy [validates](/docs/concepts#validation) the OpenAPI document to check that it's ready for code generation. Validation issues will be printed in the terminal. The generated SDK will be saved as a folder in your project.
-
-## Adding SDK generation to your GitHub Actions
-
-The Speakeasy [`sdk-generation-action`](https://github.com/speakeasy-api/sdk-generation-action) repository provides workflows for integrating the Speakeasy CLI into CI/CD pipelines to automatically regenerate SDKs when the OpenAPI document changes.
-
-You can set up Speakeasy to automatically push a new branch to your SDK repositories so that your engineers can review and merge the SDK changes.
-
-For an overview of how to set up automation for your SDKs, see the Speakeasy [SDK Workflow Matrix](/docs/workflow-reference/generation-reference).
-
-## Using your SDK
-
-Once you've generated your SDK, you can [publish](/docs/publish-sdk) it for use. For TypeScript, you can publish it as an npm package.
-
-A quick, non-production-ready way to see your SDK in action is to copy your SDK folder to a frontend TypeScript project and use it there.
-
-For example, you can create a Vite project that uses TypeScript:
+Or run Speakeasy directly:
```bash filename="Terminal"
-npm create vite@latest
+speakeasy generate sdk --schema ./openapi.yaml --lang typescript --out ./sdk-typescript
```
-Copy the SDK folder from your NestJS app to the `src` directory of your TypeScript Vite project.
+## Validate the document
-Delete the SDK folder in your NestJS project.
+Lint the generated OpenAPI file:
-In the SDK `README.md` file, you'll find documentation about your Speakeasy SDK.
+```bash filename="Terminal"
+speakeasy lint openapi --schema ./openapi.yaml
+```
-Note that the SDK is not ready for production use. To get it production-ready, follow the steps outlined in your Speakeasy workspace.
+## Example SDK usage
-The SDK includes Zod as a bundled dependency, as can be seen in the `sdk-typescript/package.json` file.
+After generating a TypeScript SDK, call operations using methods named from `operationId` values.
-Replace the code in the `src/main.ts` file with the following lines of code:
+The `createBooking` SDK call uses the same request body shape as the NestJS controller. The camelCase field names come from the generated SDK, while the payload still maps cleanly to the `CreateBookingDto` schema.
-```typescript filename="main.ts"
-import { SDK } from './sdk-typescript/src/'; // Adjust the path as necessary eg if your generated SDK has a different name
-import { catsFindOneCat } from './sdk-typescript/src/funcs/catsFindOneCat';
+```typescript filename="sdk-usage.ts"
+import { SDK } from './sdk-typescript/src';
+import { getStations, createBooking } from './sdk-typescript/src/funcs';
const sdk = new SDK();
+
async function run() {
- const res = await catsFindOneCat(sdk, {
- id: "0",
+ const stations = await getStations(sdk, { search: 'Berlin' });
+ if (!stations.ok) throw stations.error;
+
+ const booking = await createBooking(sdk, {
+ tripId: 'ea399ba1-6d95-433f-92d1-83f67b775594',
+ passengerName: 'Alex Rider',
+ hasBicycle: false,
+ hasDog: true,
});
+ if (!booking.ok) throw booking.error;
- if (!res.ok) {
- throw res.error;
- }
-
- const { value: result } = res;
-
- // Handle the result
- console.log(result);
+ console.log(booking.value);
}
run();
```
-Run the Vite dev server:
-
-```bash filename="Terminal"
-npm run dev
-```
-
-Enable CORS in your NestJS dev server by adding the following configuration to the `bootstrap` function above the `await app.listen(3000);` line:
+## Add SDK Generation to GitHub Actions
-```typescript filename="main.ts"
-app.enableCors({
- origin: 'http://localhost:5173', // Vite's default port
- methods: 'GET,POST,PUT,DELETE,OPTIONS',
- allowedHeaders: 'Content-Type, Authorization',
- credentials: true,
-});
-```
-
-Run the NestJS dev server as well:
-
-```bash filename="Terminal"
-npm run start:dev
-```
-
-You'll see the following logged in your browser dev tools console:
-
-```javascript
-{type: 'cat', name: 'Shadow', age: 8, breed: 'Bombay', environment: 'indoor'}
-```
+The Speakeasy [sdk-generation-action](https://github.com/speakeasy-api/sdk-generation-action) repository provides workflows that can integrate the Speakeasy CLI in a CI/CD pipeline, so SDKs are regenerated when the OpenAPI document changes.
-The SDK functions are type safe and include TypeScript autocompletion for arguments and outputs.
+Set up Speakeasy to automatically push a new branch to SDK repositories so that engineers can review and merge the SDK changes.
-If you try to use an incorrect type for an argument:
-
-```typescript filename="main.ts"
-const res = await catsFindOneCat(sdk, {
- id: 1,
-});
-```
-
-You'll get a TypeScript error:
-
-```
-Type 'number' is not assignable to type 'string'
-```
+For an overview of how to set up automation for your SDKs, see the Speakeasy documentation about [SDK Generation Action and Workflows](/docs/speakeasy-reference/workflow-file).
## Further reading
-This guide covered the basics of generating an OpenAPI document using NestJS. Here are some resources to help you learn more about OpenAPI, the NestJS OpenAPI module, and Speakeasy:
-
-- [NestJS OpenAPI (Swagger) module documentation](https://typespec.io/docs): Learn more about generating an OpenAPI document using NestJS. The topics covered include types and parameters, operations, security, mapped types, and decorators.
-- [Speakeasy documentation](/docs): Speakeasy has extensive documentation covering how to generate SDKs from OpenAPI documents, customize SDKs, and more.
-- [Speakeasy OpenAPI reference](/openapi): View a detailed reference for the OpenAPI Specification.
+Learn more about the OpenAPI decorators available in `@nestjs/swagger` on NestJS's OpenAPI integration [here](https://docs.nestjs.com/openapi).
diff --git a/public/assets/openapi/nestjs/scalar-ui-endpoint.png b/public/assets/openapi/nestjs/scalar-ui-endpoint.png
new file mode 100644
index 00000000..9fccb8c3
Binary files /dev/null and b/public/assets/openapi/nestjs/scalar-ui-endpoint.png differ
diff --git a/public/assets/openapi/nestjs/scalar-ui-example-req-body.png b/public/assets/openapi/nestjs/scalar-ui-example-req-body.png
deleted file mode 100644
index b02faeba..00000000
Binary files a/public/assets/openapi/nestjs/scalar-ui-example-req-body.png and /dev/null differ
diff --git a/public/assets/openapi/nestjs/scalar-ui-example-schema.png b/public/assets/openapi/nestjs/scalar-ui-example-schema.png
deleted file mode 100644
index 9c80ef34..00000000
Binary files a/public/assets/openapi/nestjs/scalar-ui-example-schema.png and /dev/null differ
diff --git a/public/assets/openapi/nestjs/scalar-ui-request-model.png b/public/assets/openapi/nestjs/scalar-ui-request-model.png
deleted file mode 100644
index 2601eda9..00000000
Binary files a/public/assets/openapi/nestjs/scalar-ui-request-model.png and /dev/null differ
diff --git a/public/assets/openapi/nestjs/scalar-ui-try-request.png b/public/assets/openapi/nestjs/scalar-ui-try-request.png
deleted file mode 100644
index a3aa4161..00000000
Binary files a/public/assets/openapi/nestjs/scalar-ui-try-request.png and /dev/null differ
diff --git a/public/assets/openapi/nestjs/scalar-ui.png b/public/assets/openapi/nestjs/scalar-ui.png
index 241e41d3..63fa66f7 100644
Binary files a/public/assets/openapi/nestjs/scalar-ui.png and b/public/assets/openapi/nestjs/scalar-ui.png differ
diff --git a/public/assets/openapi/nestjs/speakeasy-lint-report.png b/public/assets/openapi/nestjs/speakeasy-lint-report.png
index b9a432c0..cb5a1024 100644
Binary files a/public/assets/openapi/nestjs/speakeasy-lint-report.png and b/public/assets/openapi/nestjs/speakeasy-lint-report.png differ