{"_id":"@audc/class-validator-jsonschema","_rev":"1-3f41799c4653fb6244f78baf77ea46cf","name":"@audc/class-validator-jsonschema","dist-tags":{"latest":"3.1.0"},"versions":{"3.1.0":{"name":"@audc/class-validator-jsonschema","version":"3.1.0","keywords":["class-validator","jsonschema","openapi","swagger"],"author":{"name":"Aleksi Pekkala","email":"aleksipekkala@gmail.com"},"license":"MIT","_id":"@audc/class-validator-jsonschema@3.1.0","maintainers":[{"name":"bivas6","email":"yaakov.bivas@audiocodes.com"},{"name":"orgads","email":"orgads@gmail.com"}],"homepage":"https://github.com/epiphone/class-validator-jsonschema#readme","bugs":{"url":"https://github.com/epiphone/class-validator-jsonschema/issues"},"dist":{"shasum":"0e4df85531bc14cff17f168ae82e825035adba95","tarball":"https://registry.npmjs.org/@audc/class-validator-jsonschema/-/class-validator-jsonschema-3.1.0.tgz","fileCount":31,"integrity":"sha512-NJ7lX96H3ZiSk/Rxi6VQFv9lPbmdyqj/VrOF1jyJ/426ULm4nqJocdAAz3BUja6ZH+ktMgc0L6zzTlZDw16YgQ==","signatures":[{"sig":"MEUCIH9CBI95P22AQnPPguLYNku0M8FHyby37qG+ucF57vv2AiEA2KMgDtxnVX/FEVydts650oxlg701oweylVriCsYTbKc=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":124008,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJieTFbACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoKGxAAl9WIJwkyRy8G8X3iGtB6tmBHsnG/CUV+2zT4L5uv+V9EsxC+\r\nmE50goZFOov7SrFoumKSk180/z9eSTgOQKx0X5q9wSovoIJoD+zCIl5vPnKw\r\n0uv7hMuNfTzzU/1rTbheTCNvM9UZrPUuWftKxqDTh+Z/1BBWmd7zp6KcgnGI\r\nMLgkm4GHo42BNy6pwFN2wA74BA1b/9fXv/3txqBkm7NK0JsSW6cHx+gRSeFp\r\nj4evYDOFvX2eEolfoFwqD7IOrMdcDOPVJsdtPFuB4r4KhxN/yNXVOsm0LAd3\r\n3d8oeM3KV1rrryBbUzAtXL53WhZWbrsUvlOzT3tyFehyn3F0eL2dXGBhgFjZ\r\nN6MdrkM8qF3VilLExy03cXkm9TlpPWn3DHRp/JytukSM1CLPatmLrnDJgpn6\r\nEff/SvkmyulqM7YhkKlmMAHEXb3qTeqxdbf8554ZFcWJvDazuvaTdradgocI\r\nBYV1B38SfJ2MVr+GkjGHEDLh1zCi5sDLA6EuEzCSOIjLo9lbMSPeNPAIOXaH\r\nRekc2bhrnjRoSCAx/+DeivpQO3goGt/U6eNW4omepynnISZSl264pGI1D9gR\r\nAp6/6ehqi4Wrb3eKD6KLTQOVj4iUbZtB3Krw45ZpnBQY/xYJVg8Iyos4xMbJ\r\nRCXZ8Ai+lPq5ToRvtV5OqOFbZVLqPPezBqA=\r\n=WHz7\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/index.js","types":"build/index.d.ts","gitHead":"2820969a1ff3621de9122c024ef34932a49460f7","scripts":{"test":"jest --coverage","build":"npm run clean && tsc -p tsconfig.release.json","clean":"rimraf coverage build","format":"prettier --write {src,__tests__}/**/*.ts","prepare":"install-self-peers --npm -- --no-save --ignore-scripts && npm run build","test:lint":"tslint --project . src/**/*.ts","test:watch":"jest --watch","test:format":"prettier --check {src,__tests__}/**/*.ts","send-coverage":"codecov -f coverage/*.json"},"_npmUser":{"name":"orgads","email":"orgads@gmail.com"},"prettier":{"semi":false,"singleQuote":true},"repository":{"url":"git+ssh://git@github.com/epiphone/class-validator-jsonschema.git","type":"git"},"_npmVersion":"8.5.0","description":"Convert class-validator-decorated classes into JSON schema","directories":{},"_nodeVersion":"16.14.2","dependencies":{"tslib":"^2.4.0","openapi3-ts":"^2.0.2","lodash.merge":"^4.6.2","lodash.groupby":"^4.6.0","reflect-metadata":"^0.1.13"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^28.1.0","rimraf":"^3.0.2","tslint":"^6.1.3","codecov":"^3.8.2","ts-jest":"^28.0.2","tsutils":"^3.21.0","prettier":"^2.6.2","lodash.get":"^4.4.2","typescript":"^4.6.4","@types/jest":"^27.5.0","@types/node":"^16.11.33","@types/prettier":"^2.6.0","@types/validator":"^13.7.2","@types/lodash.get":"^4.4.7","@types/lodash.merge":"^4.6.7","@types/lodash.groupby":"^4.6.7","tslint-config-prettier":"^1.18.0","tslint-config-standard":"^9.0.0","@types/reflect-metadata":"^0.1.0","@team-griffin/install-self-peers":"^1.1.1"},"peerDependencies":{"class-validator":"^0.13.1","class-transformer":"^0.4.0 || ^0.5.0"},"_npmOperationalInternal":{"tmp":"tmp/class-validator-jsonschema_3.1.0_1652109659803_0.09069741295965783","host":"s3://npm-registry-packages"}}},"time":{"created":"2022-05-09T15:20:59.738Z","modified":"2025-01-19T20:18:57.381Z","3.1.0":"2022-05-09T15:20:59.954Z"},"bugs":{"url":"https://github.com/epiphone/class-validator-jsonschema/issues"},"author":{"name":"Aleksi Pekkala","email":"aleksipekkala@gmail.com"},"license":"MIT","homepage":"https://github.com/epiphone/class-validator-jsonschema#readme","keywords":["class-validator","jsonschema","openapi","swagger"],"repository":{"url":"git+ssh://git@github.com/epiphone/class-validator-jsonschema.git","type":"git"},"description":"Convert class-validator-decorated classes into JSON schema","maintainers":[{"email":"orgads@gmail.com","name":"orgads"}],"readme":"# class-validator-jsonschema\r\n\r\n[![codecov](https://codecov.io/gh/epiphone/class-validator-jsonschema/branch/master/graph/badge.svg)](https://codecov.io/gh/epiphone/class-validator-jsonschema) [![npm version](https://badge.fury.io/js/class-validator-jsonschema.svg)](https://badge.fury.io/js/class-validator-jsonschema)\r\n\r\nConvert [class-validator](https://github.com/typestack/class-validator)-decorated classes into OpenAPI-compatible JSON Schema. The aim is to provide a best-effort conversion: since some of the `class-validator` decorators lack a direct JSON Schema counterpart, the conversion is bound to be somewhat opinionated. To account for this multiple extension points are available.\r\n\r\n## Installation\r\n\r\n`npm install class-validator-jsonschema`\r\n\r\n**Note the peer dependency versions** in [package.json](./package.json). Try installing a previous major version of `class-validator-jsonschema` in case you're stuck with older peer dependencies.\r\n\r\n## Usage\r\n\r\n```typescript\r\nimport { IsOptional, IsString, MaxLength } from 'class-validator'\r\nimport { validationMetadatasToSchemas } from 'class-validator-jsonschema'\r\n\r\nclass BlogPost {\r\n  @IsString() id: string\r\n\r\n  @IsOptional()\r\n  @MaxLength(20, { each: true })\r\n  tags: string[]\r\n}\r\n\r\nconst schemas = validationMetadatasToSchemas()\r\nconsole.log(schemas)\r\n```\r\n\r\nwhich prints out:\r\n\r\n```json\r\n{\r\n  \"BlogPost\": {\r\n    \"properties\": {\r\n      \"id\": {\r\n        \"type\": \"string\"\r\n      },\r\n      \"tags\": {\r\n        \"items\": {\r\n          \"maxLength\": 20,\r\n          \"type\": \"string\"\r\n        },\r\n        \"type\": \"array\"\r\n      }\r\n    },\r\n    \"required\": [\"id\"],\r\n    \"type\": \"object\"\r\n  }\r\n}\r\n```\r\n\r\n`validationMetadatasToSchemas` takes an `options` object as an optional parameter. Check available configuration objects and defaults at [`options.ts`](src/options.ts).\r\n\r\n### Adding and overriding default converters\r\n\r\nWith `options.additionalConverters` you can add new validation metadata converters or override [the existing ones](src/defaultConverters.ts). Let's say we want to, for example, add a handy `description` field to each `@IsString()`-decorated property:\r\n\r\n```typescript\r\nimport { ValidationTypes } from 'class-validator'\r\n\r\n// ...\r\n\r\nconst schemas = validationMetadatasToSchemas({\r\n  additionalConverters: {\r\n    [ValidationTypes.IS_STRING]: {\r\n      description: 'A string value',\r\n      type: 'string',\r\n    },\r\n  },\r\n})\r\n```\r\n\r\nwhich now outputs:\r\n\r\n```json\r\n{\r\n  \"BlogPost\": {\r\n    \"properties\": {\r\n      \"id\": {\r\n        \"description\": \"A string value\",\r\n        \"type\": \"string\"\r\n      }\r\n      // ...\r\n    }\r\n  }\r\n}\r\n```\r\n\r\nAn additional converter can also be supplied in form of a function that receives the validation metadata item and global options, outputting a JSON Schema property object (see below for usage):\r\n\r\n```typescript\r\ntype SchemaConverter = (\r\n  meta: ValidationMetadata,\r\n  options: IOptions\r\n) => SchemaObject | void\r\n```\r\n\r\n### Custom validation classes\r\n\r\n`class-validator` allows you to define [custom validation classes](https://github.com/typestack/class-validator#custom-validation-classes). You might for example validate that a string's length is between given two values:\r\n\r\n```typescript\r\nimport {\r\n  Validate,\r\n  ValidationArguments,\r\n  ValidatorConstraint,\r\n  ValidatorConstraintInterface,\r\n} from 'class-validator'\r\n\r\n// Implementing the validator:\r\n\r\n@ValidatorConstraint()\r\nexport class CustomTextLength implements ValidatorConstraintInterface {\r\n  validate(text: string, validationArguments: ValidationArguments) {\r\n    const [min, max] = validationArguments.constraints\r\n    return text.length >= min && text.length <= max\r\n  }\r\n}\r\n\r\n// ...and putting it to use:\r\n\r\nclass Post {\r\n  @Validate(CustomTextLength, [0, 11])\r\n  title: string\r\n}\r\n```\r\n\r\nNow to handle your custom validator's JSON Schema conversion include a `CustomTextLength` converter in `options.additionalConverters`:\r\n\r\n```typescript\r\nconst schemas = validationMetadatasToSchemas({\r\n  additionalConverters: {\r\n    CustomTextLength: (meta) => ({\r\n      maxLength: meta.constraints[1],\r\n      minLength: meta.constraints[0],\r\n      type: 'string',\r\n    }),\r\n  },\r\n})\r\n```\r\n\r\n### Decorating with additional properties\r\n\r\nValidation classes can also be supplemented with the `JSONSchema` decorator. `JSONSchema` can be applied both to classes and individual properties; any given keywords are then [merged](https://lodash.com/docs/4.17.4#merge) into the JSON Schema derived from class-validator decorators:\r\n\r\n```typescript\r\nimport { JSONSchema } from 'class-validator-jsonschema'\r\n\r\n@JSONSchema({\r\n  description: 'A User object',\r\n  example: { id: '123' },\r\n})\r\nclass BlogPost {\r\n  @IsString()\r\n  @JSONSchema({\r\n    description: 'User primary key',\r\n    format: 'custom-id',\r\n  })\r\n  id: string\r\n}\r\n```\r\n\r\nResults in the following schema:\r\n\r\n```json\r\n{\r\n  \"BlogPost\": {\r\n    \"description\": \"A User object\",\r\n    \"example\": { \"id\": \"123\" },\r\n    \"properties\": {\r\n      \"id\": {\r\n        \"description\": \"User primary key\",\r\n        \"format\": \"custom-id\",\r\n        \"type\": \"string\"\r\n      }\r\n    },\r\n    \"required\": [\"id\"],\r\n    \"type\": \"object\"\r\n  }\r\n}\r\n```\r\n\r\n`JSONSchema` decorators also flow down from parent classes into [inherited validation decorators](https://github.com/typestack/class-validator#inheriting-validation-decorators). Note though that if the inherited class uses `JSONSchema` to redecorate a property from the parent class, the parent class `JSONSchema` gets overwritten - i.e. there's no merging logic.\r\n\r\n#### Custom handlers\r\n\r\nAlternatively `JSONSchema` can take a function of type `(existingSchema: SchemaObject, options: IOptions) => SchemaObject`. The return value of this function is then **not** automatically merged into existing schema (i.e. the one derived from `class-validator` decorators). Instead you can handle merging yourself in whichever way is preferred, the idea being that removal of existing keywords and other more complex overwrite scenarios can be implemented here.\r\n\r\n### @ValidateNested and arrays\r\n\r\n`class-validator` supports validating nested objects via the [`@ValidateNested` decorator](https://github.com/typestack/class-validator#validating-nested-objects). Likewise JSON Schema generation is supported out-of-the-box for nested properties such as\r\n\r\n```typescript\r\n@ValidateNested()\r\nuser: UserClass\r\n```\r\n\r\nHowever, due to [limitations in Typescript's reflection system](https://github.com/Microsoft/TypeScript/issues/10576) we cannot infer the inner type of a generic class. In effect this means that properties like\r\n\r\n```typescript\r\n@ValidateNested({ each: true })\r\nusers: UserClass[]\r\n\r\n@ValidateNested()\r\nuser: Promise<UserClass>\r\n```\r\n\r\nwould resolve to classes `Array` and `Promise` in JSON Schema. To work around this limitation we can use `@Type` from `class-transformer` to explicitly define the nested property's inner type:\r\n\r\n```typescript\r\nimport { Type } from 'class-transformer'\r\nimport { validationMetadatasToSchemas } from 'class-validator-jsonschema'\r\nconst { defaultMetadataStorage } = require('class-transformer/cjs/storage') // See https://github.com/typestack/class-transformer/issues/563 for alternatives\r\n\r\nclass User {\r\n  @ValidateNested({ each: true })\r\n  @Type(() => BlogPost) // 1) Explicitly define the nested property type\r\n  blogPosts: BlogPost[]\r\n}\r\n\r\nconst schemas = validationMetadatasToSchemas({\r\n  classTransformerMetadataStorage: defaultMetadataStorage, // 2) Define class-transformer metadata in options\r\n})\r\n```\r\n\r\nNote also how the `classTransformerMetadataStorage` option has to be defined for `@Type` decorator to take effect.\r\n\r\n### Using a custom validation metadataStorage\r\n\r\nUnder the hood we grab validation metadata from the default storage returned by `class-validator`'s `getMetadataStorage()`. In case of a version clash or something you might want to manually pass in the storage:\r\n\r\n```typescript\r\nconst schemas = validationMetadatasToSchemas({\r\n  classValidatorMetadataStorage: myCustomMetadataStorage,\r\n})\r\n```\r\n\r\n## Limitations\r\n\r\nThere's no handling for `class-validator`s **validation groups** or **conditional decorator** (`@ValidateIf`) out-of-the-box. The above-mentioned extension methods can be used to fill the gaps if necessary.\r\n\r\nThe OpenAPI spec doesn't currently support the new JSON Schema **draft-06 keywords** `const` and `contains`. This means that constant value decorators such as `@IsEqual()` and `@ArrayContains()` translate to quite [complicated schemas](https://github.com/sahava/gtm-datalayer-test/issues/4). Hopefully [in a not too distant future](https://github.com/OAI/OpenAPI-Specification/issues/1313#issuecomment-335893062) these keywords are adopted into the spec and we'll be able to provide neater conversion.\r\n\r\nHandling **null values** is also tricky since OpenAPI doesn't support JSON Schema's `type: null`, providing its own `nullable` keyword instead. The default `@IsEmpty()` converter for example opts for `nullable` but you can use `type: null` instead via `options.additionalConverters`:\r\n\r\n```typescript\r\n// ...\r\nadditionalConverters: {\r\n  [ValidationTypes.IS_EMPTY]: {\r\n    anyOf: [\r\n      {type: 'string', enum: ['']},\r\n      {type: 'null'}\r\n    ]\r\n  }\r\n}\r\n```\r\n\r\n## TODO\r\n\r\n- [x] handle `skipMissingProperties` and `@isDefined()`\r\n- [x] decorators for overwriting prop schemas\r\n- [ ] optional property descriptions (e.g. `A Base64-encoded string`)\r\n- [ ] optional draft-06 keywords\r\n","readmeFilename":"README.md"}