{"id":49670857,"url":"https://github.com/Akronae/nestjs-openapi-validation","last_synced_at":"2026-05-23T12:00:41.181Z","repository":{"id":303924584,"uuid":"1014974894","full_name":"Akronae/nestjs-openapi-validation","owner":"Akronae","description":"✅ Validate NestJS DTOs with TypeScript/OpenAPI","archived":false,"fork":false,"pushed_at":"2026-03-12T14:47:32.000Z","size":204,"stargazers_count":7,"open_issues_count":0,"forks_count":2,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-28T15:58:30.423Z","etag":null,"topics":["nestjs","openapi"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Akronae.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-07-06T19:11:12.000Z","updated_at":"2026-03-12T14:46:44.000Z","dependencies_parsed_at":"2025-07-10T14:55:35.570Z","dependency_job_id":"8f00620b-1e41-4363-9b50-bcddf358930d","html_url":"https://github.com/Akronae/nestjs-openapi-validation","commit_stats":null,"previous_names":["akronae/nestjs-openapi-validation"],"tags_count":21,"template":false,"template_full_name":null,"purl":"pkg:github/Akronae/nestjs-openapi-validation","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Akronae%2Fnestjs-openapi-validation","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Akronae%2Fnestjs-openapi-validation/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Akronae%2Fnestjs-openapi-validation/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Akronae%2Fnestjs-openapi-validation/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Akronae","download_url":"https://codeload.github.com/Akronae/nestjs-openapi-validation/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Akronae%2Fnestjs-openapi-validation/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33394672,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-23T04:15:53.637Z","status":"ssl_error","status_checked_at":"2026-05-23T04:15:53.242Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["nestjs","openapi"],"created_at":"2026-05-06T23:00:23.406Z","updated_at":"2026-05-23T12:00:41.166Z","avatar_url":"https://github.com/Akronae.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"# 🛡️ NestJS OpenAPI Validation\n\n[![npm version](https://badge.fury.io/js/nestjs-openapi-validation.svg)](https://badge.fury.io/js/nestjs-openapi-validation)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![TypeScript](https://img.shields.io/badge/TypeScript-5.1%2B-blue.svg)](https://www.typescriptlang.org/)\n[![Node.js](https://img.shields.io/badge/Node.js-16%2B-brightgreen)](https://nodejs.org/)\n[![NestJS](https://img.shields.io/badge/NestJS-10%2B-ea2845)](https://nestjs.com/)\n\n---\n\n\u003e **Stop repeating yourself. Validate your NestJS applications using only TypeScript and OpenAPI.**\n\n## Why use this?\n\nIn a standard NestJS project, defining a single property often requires you to write it **three times**:\n\n1. **TypeScript:** `id: number;` (For type safety)\n2. **Validation:** `@IsInt()` (For `class-validator`)\n3. **Documentation:** `@ApiProperty()` (For Swagger/OpenAPI)\n\nThis is redundant, error-prone, and a nightmare to maintain. **nestjs-openapi-validation** uses TypeScript inference to automatically generate validation rules and OpenAPI schemas. For most properties, **the TypeScript definition is enough.**\n\n---\n\n## The \"Before vs. After\"\n\n### ❌ The Standard Way (class-validator)\n\nYou have to manually sync decorators for every single field. Missing one `@Expose()` or `@IsNumber()` breaks your API or documentation.\n\n```typescript\nclass Item {\n  @Expose() // Needed for response transformation\n  @Type(() =\u003e Number) // Needed to ensure it's a number\n  @IsNumber() // Validation\n  @Min(0) // Logic\n  @ApiProperty({ minimum: 0 }) // Documentation\n  size?: number;\n}\n```\n\n### ✅ The `nestjs-openapi-validation` Way\n\nThe library infers the validation and documentation from your types and the `@OpenApiRegister()` decorator.\n\n```typescript\n@OpenApiRegister()\nclass Item {\n  @ApiProperty({ minimum: 0 }) // Only add what can't be inferred\n  size?: number;\n}\n```\n\n---\n\n## ✨ Features\n\n- 🔄 **Auto-sync validation** - Your OpenAPI docs and validation logic stay perfectly in sync\n- ⚡ **Zero config** - Works out of the box with existing NestJS + Swagger setups\n- 🛠️ **Zod-powered** - Leverage Zod's robust validation and error reporting\n- 📚 **Rich validation** - Support for enums, unions, nested objects, arrays, and more\n- 🧯 **Prevent data leaks** - Blocks unexpected request fields so accidental extra fields (e.g., `passwordHash`, `internalNotes`) never leak\n- 🛡️ **Strict user input validation** - Rejects user provided extra fields and enforces types/coercion so unintended data never reaches your code\n- 🔍 **Format validation** - Email, URL, date-time, and custom pattern validation\n- 🎨 **Beautiful errors** - Clear, actionable validation error messages\n\n## 🚀 Quick Start\n\n```bash\n# Using npm\nnpm install nestjs-openapi-validation zod\n\n# Using yarn\nyarn add nestjs-openapi-validation zod\n\n# Using pnpm\npnpm add nestjs-openapi-validation zod\n```\n\n### Basic Setup\n\n```jsonc\n// package.json\n{\n  \"scripts\": {\n    \"dev\": \"nest start --watch --type-check\",\n  },\n}\n```\n\n```jsonc\n// nest-cli.json\n{\n  \"$schema\": \"https://json.schemastore.org/nest-cli\",\n  \"collection\": \"@nestjs/schematics\",\n  \"sourceRoot\": \"src\",\n  \"compilerOptions\": {\n    \"builder\": {\n      \"type\": \"swc\",\n      \"options\": {\n        \"stripLeadingPaths\": false,\n        \"includeDotfiles\": true,\n      },\n    },\n    \"deleteOutDir\": true,\n    \"plugins\": [\n      {\n        \"name\": \"@nestjs/swagger\",\n        \"options\": {\n          \"introspectComments\": true,\n        },\n      },\n    ],\n  },\n}\n```\n\n```typescript\n// main.ts\nimport { ValidationPipe } from '@nestjs/common';\nimport { NestFactory } from '@nestjs/core';\nimport { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';\n// This is automatically generated thanks to @nestjs/swagger plugin\n// defined in nest-cli.json\nimport metadata from './metadata';\nimport { AppModule } from './app.module';\nimport {\n  OpenApiValidationInterceptor,\n  OpenApiValidationPipe,\n  getRegisteredOpenApiModels,\n} from 'nestjs-openapi-validation';\n\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule);\n\n  const config = new DocumentBuilder()\n    .setTitle('My API')\n    .setDescription('API with automatic validation')\n    .setVersion('1.0')\n    .build();\n\n  await SwaggerModule.loadPluginMetadata(metadata);\n  const document = SwaggerModule.createDocument(app, config, {\n    // Include models registered with @OpenApiRegister()\n    extraModels: getRegisteredOpenApiModels(),\n  });\n\n  // Validate user input\n  app.useGlobalPipes(new OpenApiValidationPipe(metadata, document));\n\n  // Validate responses\n  app.useGlobalInterceptors(\n    new OpenApiValidationInterceptor(metadata, document),\n  );\n\n  await app.listen(3000);\n}\n```\n\n### Define DTOs\n\n```typescript\n// user.dto.ts\nimport { ApiProperty } from '@nestjs/swagger';\nimport { OpenApiRegister } from 'nestjs-openapi-validation';\n\nexport enum UserRole {\n  ADMIN = 'admin',\n  USER = 'user',\n  MODERATOR = 'moderator',\n}\n\nexport class CreateUserDto {\n  @ApiProperty({ minLength: 2, maxLength: 50 })\n  name: string;\n\n  @ApiProperty({ format: 'email' })\n  email: string;\n\n  @ApiProperty({ minimum: 18, maximum: 120 })\n  age: number;\n\n  @ApiProperty({ enum: UserRole })\n  role: UserRole;\n\n  @ApiProperty({ format: 'url' })\n  website?: string;\n\n  @ApiProperty({ pattern: /^[0-9]{10}$/.source })\n  phone: string;\n\n  tags?: string[];\n}\n\n// For DTOs not automatically discovered by the Swagger plugin\n@OpenApiRegister()\nexport class SpecialUserDto {\n  @ApiProperty({ minimum: 0, maximum: 120 })\n  age?: number;\n\n  name: string;\n}\n```\n\n### Use in Controllers\n\n```typescript\n// user.controller.ts\nimport { Controller, Post, Body } from '@nestjs/common';\nimport { CreateUserDto } from './user.dto';\n\n@Controller('users')\nexport class UserController {\n  @Post()\n  createUser(@Body() userData: CreateUserDto) {\n    // userData is automatically validated against your OpenAPI schema!\n    return { message: 'User created', data: userData };\n  }\n}\n```\n\n## 🎯 Advanced Features\n\n### Union Types \u0026 OneOf\n\n```typescript\nenum Status {\n  active = 'ACTIVE',\n  inactive = 'INACTIVE',\n}\n\nexport class FlexibleQuery {\n  @ApiProperty({\n    oneOf: [{ enum: Object.values(Status) }, { type: 'string' }],\n  })\n  status: $`{Status}` | (string \u0026 {});\n\n  @ApiProperty({\n    oneOf: [\n      { $ref: getSchemaPath(UserDto) },\n      { $ref: getSchemaPath(AdminDto) }\n    ]\n  })\n  profile: UserDto | AdminDto;\n}\n```\n\n### Nested Objects \u0026 Arrays\n\nComplexe types are supported out of the box without decorators.\n\n```typescript\nexport class OrderDto {\n  items: OrderItemDto[];\n  customer: CustomerDto;\n  orderDate: Date;\n  stringMatrix: string[][];\n  deeply: {\n    nested {\n      value: true;\n    }\n  }\n}\n```\n\n### Complex Validation Rules\n\n```typescript\nexport class ProductDto {\n  @ApiProperty({\n    minLength: 3,\n    maxLength: 100,\n    pattern: /^[a-zA-Z0-9\\s-]+$/.source,\n  })\n  name: string;\n\n  @ApiProperty({\n    minimum: 0.01,\n    maximum: 1000000,\n  })\n  price: number;\n\n  @ApiProperty({\n    minItems: 1,\n    maxItems: 10,\n    items: { type: 'string', maxLength: 50 },\n  })\n  tags: string[];\n\n  @ApiProperty({ format: 'url' })\n  imageUrl: string;\n}\n```\n\n### More\n\nCheck [src/modules/app/app.dto.ts](https://github.com/Akronae/nestjs-openapi-validation/blob/main/src/modules/app/app.dto.ts) to see all tested features.\n\n## Registering Models\n\nIf you see an error like:\n\n```\nYourDto is not registered in your OpenAPI document. Use `@OpenApiRegister()` on your DTO to register it.\n```\n\nit means the DTO is not automatically discovered by the NestJS Swagger plugin. This commonly happens when the DTO is only referenced in unions (`oneOf`), used in dynamic contexts, lives outside of scanned modules, or is only used as an input query and never returned.\n\nUse the `@OpenApiRegister()` decorator on such DTOs, and pass the registered models to Swagger via `extraModels`:\n\n## Ignoring Models\n\nUse the `@OpenApiIgnore()` to skip specific model validation.\n\n```ts\nimport { ApiProperty } from '@nestjs/swagger';\nimport {\n  OpenApiRegister,\n  getRegisteredOpenApiModels,\n} from 'nestjs-openapi-validation';\n\n@OpenApiRegister()\nexport class CustomDto {\n  name: string;\n  @ApiProperty({ minimum: 0, maximum: 120 })\n  age?: number;\n}\n\n// main.ts (excerpt)\nconst document = SwaggerModule.createDocument(app, config, {\n  extraModels: getRegisteredOpenApiModels(),\n});\n```\n\nOnce registered, request/response validation will work as expected for these models.\n\n## 📊 Validation Support Matrix\n\n| OpenAPI Feature       | Supported | Zod Equivalent           |\n| --------------------- | --------- | ------------------------ |\n| `type: string`        | ✅        | `z.string()`             |\n| `type: number`        | ✅        | `z.coerce.number()`      |\n| `type: boolean`       | ✅        | `z.coerce.boolean()`     |\n| `type: array`         | ✅        | `z.array()`              |\n| `enum`                | ✅        | `z.enum()`               |\n| `oneOf`               | ✅        | `z.union()`              |\n| `$ref`                | ✅        | Nested schemas           |\n| `format: email`       | ✅        | `z.string().email()`     |\n| `format: url`         | ✅        | `z.string().url()`       |\n| `format: date-time`   | ✅        | `z.string().datetime()`  |\n| `minimum/maximum`     | ✅        | `z.number().min().max()` |\n| `minLength/maxLength` | ✅        | `z.string().min().max()` |\n| `pattern`             | ✅        | `z.string().regex()`     |\n| `required` fields     | ✅        | Required vs optional     |\n\n## 🔧 Configuration\n\nBoth `OpenApiValidationPipe` and `OpenApiValidationInterceptor` accept two parameters:\n\n1. **metadata function** — Generated by the `@nestjs/swagger` CLI plugin\n2. **OpenAPI document** — Your Swagger document object\n\n```typescript\nnew OpenApiValidationPipe(metadata, document);\nnew OpenApiValidationInterceptor(metadata, document);\n```\n\n## 🚨 Error Handling\n\nOn validation failures, the library returns HTTP 400 (Bad Request) with Zod errors grouped by location (e.g., `query`, `body`, or `response`). Each group contains an `issues` array describing the problems.\n\n```jsonc\n// Example error response (request validation)\n{\n  \"error\": {\n    \"query\": {\n      \"issues\": [\n        {\n          \"code\": \"invalid_type\",\n          \"expected\": \"string\",\n          \"message\": \"required\",\n          \"path\": [\"str1\"],\n          \"received\": \"undefined\",\n        },\n        {\n          \"code\": \"invalid_string\",\n          \"message\": \"Invalid datetime\",\n          \"path\": [\"date\"],\n          \"validation\": \"datetime\",\n        },\n      ],\n    },\n  },\n}\n```\n\nThe same structure applies to response validation, where issues would be reported under `response`.\n\n## 🧪 Testing\n\nThe library includes comprehensive tests covering all validation scenarios:\n\n```bash\n# Run tests\n# Using yarn\nyarn test\n\n# Using npm\nnpm test\n\n# Using pnpm\npnpm test\n```\n\nExample test case:\n\n```typescript\nit('should validate email format', async () =\u003e {\n  const res = await request(app.getHttpServer()).post('/users').send({\n    name: 'John Doe',\n    email: 'invalid-email', // This will fail\n    age: 25,\n  });\n\n  expect(res.status).toBe(400);\n  expect(res.body.message).toContain('Invalid email');\n});\n```\n\n## 🛠️ Development\n\n```bash\n# Clone the repository\ngit clone https://github.com/akronae/nestjs-openapi-validation.git\n\n# Install dependencies\nyarn install\n\n# Build the library\nyarn lib:build\n\n# Run tests\nyarn test\n\n# Start development server\nyarn dev\n```\n\n## 📋 Requirements\n\n- Node.js \u003e= 16\n- NestJS \u003e= 10\n- TypeScript \u003e= 5.1\n- Zod \u003e= 3.25\n\n## 🤝 Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.\n\n1. Fork the repository\n2. Create a new branch for your feature or bugfix\n3. Make your changes and write tests\n4. Submit a pull request\n\n## 📄 License\n\nThis project is [MIT licensed](LICENSE).\n\n## 🙏 Acknowledgments\n\n- [NestJS](https://nestjs.com/)\n- [Zod](https://zod.dev/)\n- [Swagger/OpenAPI](https://swagger.io/)\n\n---\n\n\u003cdiv align=\"center\"\u003e\n\n**[⭐ Star this repo](https://github.com/akronae/nestjs-openapi-validation) if you find it useful!**\n\nMade with ❤️ for the NestJS community\n\n\u003c/div\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAkronae%2Fnestjs-openapi-validation","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FAkronae%2Fnestjs-openapi-validation","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAkronae%2Fnestjs-openapi-validation/lists"}