# Model

The classes can be used as a model in your application. Ts.ED use this models to convert JSON objects to theirs class equivalents.

The classes models can be used in the following cases:

  • Serialization and deserialisation of data (Converters),
  • Data validation with AJV,
  • Generating documentation with Swagger.

To create a model, Ts.ED provides decorators who will store and generate a standard JsonSchema model.

# Example

The example below uses decorators to describe a property of the class and store metadata such as the description of the field.

import {
  Default,
  Enum,
  Format,
  Maximum,
  MaxLength,
  Minimum,
  MinLength,
  Pattern,
  Required
} from "@tsed/common";

enum Categories {
  CAT1 = "cat1",
  CAT2 = "cat2"
}

export class MyModel {
  _id: string;

  @Required()
  unique: string;

  @MinLength(3)
  @MaxLength(50)
  indexed: string;

  @Minimum(0)
  @Maximum(100)
  @Default(0)
  rate: Number = 0;

  @Enum(Categories)
    // or @Enum("type1", "type2")
  category: Categories;

  @Pattern(/[a-z]/)
  pattern: String;

  @Format("date-time")
  @Default(Date.now)
  dateCreation: Date = new Date();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

TIP

The Model will generate a JsonSchema which can be used by module supporting JsonSchema spec

WARNING

The schema generated by Ts.ED list only properties decorated by at least one decorator. In the previous example, the _id won't be displayed in the JsonSchema. It's very important to understand that TypeScript only generates metadata on properties with at least of theses decorators:

Our model is now described, we can use it inside a as input type parameter for our methods. Ts.ED will use the model to convert the raw data to an instance of your model.

import {BodyParams, Controller, Post} from "@tsed/common";
import {MyModel} from "../models/MyModel";

@Controller("/")
export class PersonsCtrl {

  @Post("/")
  save(@BodyParams() model: MyModel): MyModel {
    console.log(model instanceof MyModel); // true
    return model; // will be serialized according to your annotation on MyModel class.
  }

  // OR

  @Post("/")
  save(@BodyParams("person") model: MyModel): MyModel {
    console.log(model instanceof MyModel); // true
    return model; // will be serialized according to your annotation on Person class.
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# Collections

Declaring property that use a collection is a bit different than declaring a simple property. TypeScript store only the Array/Set/Map type when your declare the type of your property. The type used by the collection is lost.

To tell Ts.ED (and other third party which use JsonSchema) that a property use a collection with a specific type, you must use decorator as following:

import {PropertyType} from "@tsed/common";
import {Role} from "./Role";
import {Security} from "./Security";

class User {
  @PropertyType(Role)
  roles: Role[];

  @PropertyType(String)
  securities: Map<string, Security>;

  @PropertyType(String)
  scopes: Set<string>;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# Use JsonSchema

In some cases, it may be useful to retrieve the JSON Schema from a Model for use with another library.

Here is an example of use with the AJV library:

import {JsonSchemesService, OverrideService, ValidationService} from "@tsed/common";
import * as Ajv from "ajv";
import {ErrorObject} from "ajv";
import {BadRequest} from "ts-httpexceptions";

@OverrideService(ValidationService)
export class AjvService extends ValidationService {
  constructor(private jsonSchemaService: JsonSchemesService) {
    super();
  }

  public validate(obj: any, targetType: any, baseType?: any): void {
    const schema = this.jsonSchemaService.getSchemaDefinition(targetType);

    if (schema) {
      const ajv = new Ajv();
      const valid = ajv.validate(schema, obj);

      if (!valid) {
        throw(this.buildErrors(ajv.errors!));
      }
    }
  }

  private buildErrors(errors: ErrorObject[]) {

    const message = errors.map(error => {
      return `{{name}}${error.dataPath} ${error.message} (${error.keyword})`;
    }).join("\n");

    return new BadRequest(message);
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

# Decorators