# AJV

This tutorial shows you how you can validate your data with decorators.

Validation feature uses Ajv (opens new window) and json-schema (opens new window) to perform the model validation.

# Installation

Before using the validation decorators, we need to install the ajv (opens new window) module.

npm install --save ajv
npm install --save @tsed/ajv
1
2

Then import @tsed/ajv in your Server:

import {Configuration} from "@tsed/common";
import "@tsed/ajv"; // import ajv ts.ed module

@Configuration({
    rootDir: __dirname
})
export class Server {
}
1
2
3
4
5
6
7
8

The AJV module allows a few settings to be added through the ServerSettings (all are optional):

  • options are AJV specific options passed directly to the AJV constructor,
  • errorFormatter can be used to alter the output produced by the @tsed/ajv package.

The error message could be changed like this:

import {Configuration} from "@tsed/common";
import "@tsed/ajv"; // import ajv ts.ed module

@Configuration({
    rootDir: __dirname,
     ajv: {
       errorFormatter: (error) => `At ${error.modelName}${error.dataPath}, value '${error.data}' ${error.message}`,
       verbose: true
    },
})
export class Server {}
1
2
3
4
5
6
7
8
9
10
11

# Decorators

Ts.ED gives some decorators to write your validation model:

# Examples

# Model validation

A model can be used on a method controller along with @BodyParams or other decorators, and will be validated by Ajv.

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

export class CalendarModel {
  @MaxLength(20)
  @MinLength(3)
  @Required()
  title: string;

  @Minimum(0)
  @Maximum(10)
  rating: number;

  @Email()
  email: string;

  @Format("date")  // or date-time, etc...
  createDate: Date;
  
  @Pattern(/hello/)
  customInput: string;
  
  @Enum("value1", "value2")
  customInput: "value1" | "value2";
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# Validation error

When a validation error occurs, AJV generates a list of errors with a full description like this:

[
  {
    "keyword": "minLength",
    "dataPath": ".password",
    "schemaPath": "#/properties/password/minLength",
    "params": {"limit": 6},
    "message": "should NOT be shorter than 6 characters",
    "modelName": "User"
  }
]
1
2
3
4
5
6
7
8
9
10

# User defined keywords

Ajv allows you to define custom keywords to validate a property.

You can find more details on the different ways to declare a custom validator on this page: https://ajv.js.org/docs/keywords.html

Ts.ED introduces the Keyword decorator to declare a new custom validator for Ajv. Combined with the CustomKey decorator to add keywords to a property of your class, you can use more complex scenarios than what basic JsonSchema allows.

For example, we can create a custom validator to support the range validation over a number. To do that, we have to define the custom validator by using Keyword decorator:

import {Keyword, KeywordMethods} from "@tsed/ajv";
import {array, number} from "@tsed/schema";

@Keyword({
  keyword: "range",
  type: "number",
  schemaType: "array",
  implements: ["exclusiveRange"],
  metaSchema: array()
    .items([number(), number()])
    .minItems(2)
    .additionalItems(false)
})
class RangeKeyword implements KeywordMethods {
  compile([min, max]: number[], parentSchema: any) {
    return parentSchema.exclusiveRange === true
      ? (data: any) => data > min && data < max
      : (data: any) => data >= min && data <= max;
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

Then we can declare a model using the standard decorators from @tsed/schema:

    Finally, we can create a unit test to verify if our example works properly:

    import "@tsed/ajv";
    import {PlatformTest} from "@tsed/common";
    import {getJsonSchema} from "@tsed/schema";
    import {Product} from "./Product";
    import "../keywords/RangeKeyword";
    
    describe("Product", () => {
      beforeEach(PlatformTest.create);
      afterEach(PlatformTest.reset);
    
      it("should call custom keyword validation (compile)", () => {
        const ajv = PlatformTest.get<Ajv>(Ajv);
        const schema = getJsonSchema(Product, {customKeys: true});
        const validate = ajv.compile(schema);
    
        expect(schema).to.deep.equal({
          "properties": {
            "price": {
              "exclusiveRange": true,
              "range": [
                10,
                100
              ],
              "type": "number"
            }
          },
          "type": "object"
        });
    
        expect(validate({price: 10.01})).toEqual(true);
        expect(validate({price: 99.99})).toEqual(true);
        expect(validate({price: 10})).toEqual(false);
        expect(validate({price: 100})).toEqual(false);
      });
    });
    
    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

    # With "code" function

    Starting from v7 Ajv uses CodeGen module (opens new window) for all pre-defined keywords - see codegen.md (opens new window) for details.

    Example even keyword:

      # Author

        # Maintainers

          Last Updated: 3/4/2021, 6:16:12 PM

          Other topics