schema-metadata
is a library that annotates classes with JSON schema. It uses Decorators and Decorator Metadata Stage 3 proposals.
This library contains differences from other libraries that provide more convenient ways to define JSON schema and may not align with your preferences. See Differences from other libraries for more information.
Install the package:
npm i schema-metadata
Disable the experimentalDecorators
and emitDecoratorMetadata
in your tsconfig.json
:
{
"compilerOptions": {
"experimentalDecorators": false,
"emitDecoratorMetadata": false
}
}
Import the decorators:
import { json, getJSONSchema } from "schema-metadata";
enum Role {
Admin = "admin",
User = "user",
}
@json.schema({ type: "object", $id: "User" })
class User {
@json.property({ type: "string" })
name: string;
@json.property(json.enum(Role))
role: Role;
constructor(name: string) {
this.name = name;
}
}
@json.schema({ type: "object", $id: "Comment" })
class Comment {
@json.property({ type: "string" })
public content: string;
@json.property(json.ref(User))
public author: User;
constructor(content: string, author: User) {
this.content = content;
this.author = author;
}
}
@json.schema({ type: "object", $id: "Post" })
class Post {
@json.property({ type: "string" })
title: string;
@json.property({ type: "string" })
content: string;
@json.property(json.ref(User))
user: User;
@json.property({ type: "array", items: json.ref(Comment) })
comments: Comment[] = [];
constructor(title: string, content: string, user: User) {
this.title = title;
this.content = content;
this.user = user;
}
}
printSchemas({ User, Comment, Post });
function printSchemas(ctors: any) {
// console.log(ctor.name, JSON.stringify(getJSONSchema(ctor), null, 2));
const schemas = Object.entries(ctors).reduce(
(acc, [key, ctor]) => {
acc[key] = getJSONSchema(ctor);
return acc;
},
{} as Record<string, any>
);
console.log(JSON.stringify(schemas, null, 2));
}
Output:
{
"User": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"role": {
"enum": ["admin", "user"]
}
},
"$id": "User"
},
"Comment": {
"type": "object",
"properties": {
"content": {
"type": "string"
},
"author": {
"$ref": "User"
}
},
"$id": "Comment"
},
"Post": {
"type": "object",
"properties": {
"title": {
"type": "string"
},
"content": {
"type": "string"
},
"user": {
"$ref": "User"
},
"comments": {
"type": "array",
"items": {
"$ref": "Comment"
}
}
},
"$id": "Post"
}
}
This library does not provide validation. It only generates JSON schema from the decorated classes. You can use other libraries like ajv to validate the data against the generated schema.
Other libraries use experimentalDecorators
and emitDecoratorMetadata
to generate metadata. emitDecoratorMetadata
is a TypeScript compiler option that provides the decorated field's TypeScript type information, enabling it to work without specifically annotating the type of the field.
class User {
@Property() // <-- { type: 'string' } is inferred from the TypeScript type
name: string;
}
schema-metadata
requires you to explicitly define the type of the field:
class User {
@json.property({ type: "string" })
name: string;
}
While helpful, experimentalDecorators
and emitDecoratorMetadata
syntax is not part of the TC39 standardization process and is not compatible with the latest specification. Such libraries also only work with TypeScript, and not with JavaScript where there is no type information.
TypeBox is a library that provides a more convenient way to define JSON schema. It uses a fluent API to define the schema.
One inconvenience with this library is that for properties that are $ref
to another class, ctrl+clicking the reference will not navigate to the class definition.