shou2017.com
JP / EN

TypeScriptでバリデーション、Ajvを使ってみた

Wed Oct 12, 2022
Sat Aug 10, 2024

最近はlambdaをTypeScriptで書いている僕です。

api gatewayから受け取った値にバリデーションする際にAjvを使ってみたのですが、大変便利だったのでメモしておきます。

環境

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

基本的な使い方

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

コードとドキュメントを見れば、だいぶ簡単にバリデーションが実装できることがわかると思います。

試しにエラーの場合はどうなるか見てみます。場合はrequired: ["foo"]になっているのでこれをちょっと変えて。わざとエラーを発生させます。

const data = {
  bar: "abc"
}

そうすると以下のようにエラーを教えてくれます。

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

フォーマットのバリデーション

emailなどのバリデーションをしたい場合はFormat validationを使用します。

version 7 Ajvからajv-formatsを使用するようになっていますので、古い記事などにはお気をつけてください。

新しい使い方はプラグインを追加することで使えるようになります。

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

const ajv = new Ajv()
addFormats(ajv)

試しに先ほどコードでfooをemailにしてバリデーションができているかどうか確認してみます。プロパティにformatを追加するだけです。

全体のコードはこうなります。

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);
}

プロパティでemailをバリデーションするようにしています。

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

上記のコードは試しにわざとエラーを出してます。

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

must match format "email"となってますね! 成功です。これは大変便利です。lambaの開発が捗りそう。

See Also