shou2017.com
JP

Validation in TypeScript: Trying Ajv

Wed Oct 12, 2022
Sat Aug 10, 2024

Recently, I’ve been writing Lambda functions in TypeScript.

I tried using Ajv for validation of values received from API Gateway, and it was very convenient, so I’m making a note of it.

Environment

  • “ts-node”: “^10.9.1”
  • “typescript”: “~4.8.2”
  • “ajv”: “^8.11.0”
  • “ajv-formats”: “^2.1.1”

Basic usage

Using with TypeScript

import Ajv, {JSONSchemaType} from "ajv"
const ajv = new Ajv()

interface MyData {
  foo: number
  bar?: string
}
const schema: JSONSchemaType<MyData> = {
  type: "object",
  properties: {
    foo: {type: "integer"},
    bar: {type: "string", nullable: true}
  },
  required: ["foo"],
  additionalProperties: false
}

// validate is a type guard for MyData - type is inferred from schema type
const validate = ajv.compile(schema)

// or, if you did not use type annotation for the schema,
// type parameter can be used to make it type guard:
// const validate = ajv.compile<MyData>(schema)

const data = {
  foo: 1,
  bar: "abc"
}

if (validate(data)) {
  // data is MyData here
  console.log(data.foo)
} else {
  console.log(validate.errors)
}
>> 1

You can see that validation can be easily implemented just by looking at the code and documentation.

Let’s try to see what happens in the case of an error. Since we have required: ["foo"], let’s change this a little. We’ll deliberately cause an error.

const data = {
  bar: "abc"
}

Then, it will tell us the error as follows:

[
  {
    instancePath: '',
    schemaPath: '#/required',
    keyword: 'required',
    params: { missingProperty: 'foo' },
    message: "must have required property 'foo'"
  }
]

Format validation

If you want to validate formats like email, use Format validation.

Since version 7 of Ajv, you need to use ajv-formats, so be careful with old articles and resources.

The new way to use it is by adding a plugin.

import Ajv from "ajv"
import addFormats from "ajv-formats"

const ajv = new Ajv()
addFormats(ajv)

Let’s try to validate foo as an email in the code. We just need to add format to the property.

The complete code looks like this:

import Ajv, {JSONSchemaType} from "ajv";
import addFormats from "ajv-formats";
const ajv = new Ajv();
addFormats(ajv);

interface MyData {
  foo: string;
  bar?: string;
}

const schema: JSONSchemaType<MyData> = {
  type: "object",
  properties: {
    foo: {type: "string", format: "email"},
    bar: {type: "string", nullable: true}
  },
  required: ["foo"],
  additionalProperties: false
};

const validate = ajv.compile(schema);

const data = {
  foo: "examplxample.com",
  bar: "abc"
};

if (validate(data)) {
  // data is MyData here
  console.log(data.foo);
} else {
  console.log(validate.errors);
}

We are validating the email format in the property.

  properties: {
    foo: {type: "string", format: "email"},
    bar: {type: "string", nullable: true}
  },

The above code is deliberately causing an error for testing.

[
  {
    instancePath: '/foo',
    schemaPath: '#/properties/foo/format',
    keyword: 'format',
    params: { format: 'email' },
    message: 'must match format "email"'
  }
]

It says must match format "email"! This is successful. This is very convenient. It seems that Lambda development will be more efficient.

See Also