ajv の使い方 — JavaScript/TypeScript向け高速JSONスキーマバリデーター
一言でいうと
**ajv(Another JSON Schema Validator)**は、JSON Schemaの仕様に基づいてデータの構造・型・制約をバリデーションする、Node.js/ブラウザ対応の高速バリデーションライブラリです。スキーマをコンパイルして検証関数を生成するアプローチにより、他のバリデーターと比較して圧倒的なパフォーマンスを実現しています。
どんな時に使う?
- APIリクエスト/レスポンスのバリデーション — Express/Fastifyなどで受け取るリクエストボディが期待する構造かどうかを検証する
- 設定ファイル・外部データの検証 — JSON/YAMLで読み込んだ設定ファイルやサードパーティAPIのレスポンスが正しい形式かチェックする
- フォーム入力のサーバーサイドバリデーション — JSON Schemaを単一の定義として、フロントエンドとバックエンドの両方でバリデーションルールを共有する
インストール
# npm
npm install ajv
# yarn
yarn add ajv
# pnpm
pnpm add ajv
JSON Schemaの format キーワード(email, uri, date-time など)を使う場合は、追加パッケージも必要です。
npm install ajv-formats
基本的な使い方
import Ajv, { JSONSchemaType } from "ajv";
// Ajvインスタンスを生成
const ajv = new Ajv();
// バリデーション対象の型定義
interface User {
name: string;
age: number;
email?: string;
}
// JSON Schemaを定義(型安全)
const userSchema: JSONSchemaType<User> = {
type: "object",
properties: {
name: { type: "string" },
age: { type: "integer", minimum: 0 },
email: { type: "string", nullable: true },
},
required: ["name", "age"],
additionalProperties: false,
};
// スキーマをコンパイルして検証関数を生成
const validate = ajv.compile(userSchema);
// バリデーション実行
const data: unknown = { name: "田中太郎", age: 30 };
if (validate(data)) {
// この時点で data は User 型として推論される
console.log(`ユーザー名: ${data.name}, 年齢: ${data.age}`);
} else {
console.error("バリデーションエラー:", validate.errors);
}
よく使うAPI
1. ajv.compile() — スキーマのコンパイル
最も基本的なAPI。スキーマをコンパイルして再利用可能な検証関数を返します。同じスキーマで何度もバリデーションする場合に最適です。
import Ajv from "ajv";
const ajv = new Ajv();
const schema = {
type: "object",
properties: {
id: { type: "number" },
title: { type: "string", minLength: 1, maxLength: 200 },
},
required: ["id", "title"],
};
const validate = ajv.compile(schema);
// 何度でも使い回せる
console.log(validate({ id: 1, title: "Hello" })); // true
console.log(validate({ id: "abc", title: "" })); // false
console.log(validate.errors);
// [
// { keyword: 'type', instancePath: '/id', message: 'must be number', ... },
// { keyword: 'minLength', instancePath: '/title', message: 'must NOT have fewer than 1 characters', ... }
// ]
2. ajv.validate() — コンパイル+バリデーションを一度に実行
スキーマを事前にコンパイルせず、一度きりのバリデーションを行いたい場合に使います。内部的にはコンパイル結果がキャッシュされます。
import Ajv from "ajv";
const ajv = new Ajv();
const schema = {
type: "array",
items: { type: "number" },
minItems: 1,
};
const valid = ajv.validate(schema, [1, 2, 3]);
if (!valid) {
console.error(ajv.errors);
}
3. ajv.addSchema() / $ref — スキーマの分割と参照
複数のスキーマを登録し、$ref で相互参照できます。大規模なスキーマを管理しやすくなります。
import Ajv from "ajv";
const ajv = new Ajv();
// アドレススキーマを登録
ajv.addSchema(
{
$id: "address",
type: "object",
properties: {
prefecture: { type: "string" },
city: { type: "string" },
zipCode: { type: "string", pattern: "^\\d{3}-\\d{4}$" },
},
required: ["prefecture", "city", "zipCode"],
}
);
// 別のスキーマから $ref で参照
const orderSchema = {
type: "object",
properties: {
orderId: { type: "string" },
shippingAddress: { $ref: "address" },
},
required: ["orderId", "shippingAddress"],
};
const validate = ajv.compile(orderSchema);
const result = validate({
orderId: "ORD-001",
shippingAddress: {
prefecture: "東京都",
city: "渋谷区",
zipCode: "150-0001",
},
});
console.log(result); // true
4. ajv.addKeyword() — カスタムキーワードの追加
JSON Schema標準にないバリデーションルールを独自に定義できます。
import Ajv from "ajv";
const ajv = new Ajv();
// カスタムキーワード "isNotEmpty" を追加
ajv.addKeyword({
keyword: "isNotEmpty",
type: "string",
validate: (schema: boolean, data: string) => {
if (!schema) return true; // isNotEmpty: false なら常にOK
return data.trim().length > 0;
},
errors: false,
});
const schema = {
type: "object",
properties: {
username: { type: "string", isNotEmpty: true },
},
required: ["username"],
};
const validate = ajv.compile(schema);
console.log(validate({ username: "tanaka" })); // true
console.log(validate({ username: " " })); // false
5. ajv-formats との連携 — format バリデーション
email, uri, date-time, uuid などの組み込みフォーマットを使うには ajv-formats を追加します。
import Ajv from "ajv";
import addFormats from "ajv-formats";
const ajv = new Ajv();
addFormats(ajv);
const schema = {
type: "object",
properties: {
email: { type: "string", format: "email" },
website: { type: "string", format: "uri" },
createdAt: { type: "string", format: "date-time" },
id: { type: "string", format: "uuid" },
},
required: ["email"],
};
const validate = ajv.compile(schema);
console.log(
validate({
email: "user@example.com",
website: "https://example.com",
createdAt: "2024-01-15T10:30:00Z",
id: "550e8400-e29b-41d4-a716-446655440000",
})
); // true
console.log(validate({ email: "not-an-email" })); // false
console.log(validate.errors);
// [{ keyword: 'format', message: 'must match format "email"', ... }]
類似パッケージとの比較
| 特徴 | ajv | zod | joi | yup |
|---|---|---|---|---|
| バリデーション方式 | JSON Schema準拠 | TypeScriptファースト | 独自DSL | 独自DSL |
| パフォーマンス | ◎(最速クラス) | ○ | △ | △ |
| TypeScript型推論 | ○(JSONSchemaType) | ◎(ネイティブ) | △ | ○ |
| スキーマの可搬性 | ◎(JSON Schemaは言語非依存) | ×(TS専用) | ×(JS専用) | ×(JS専用) |
| 学習コスト | 中(JSON Schema仕様の理解が必要) | 低 | 低 | 低 |
| ブラウザ対応 | ○ | ○ | ○ | ○ |
| エコシステム | OpenAPI/Swagger等と親和性高い | tRPC等と親和性高い | hapi由来 | Formik等と連携 |
選定の目安:
- JSON Schemaを活用したい・OpenAPIと連携したい → ajv
- TypeScriptの型とバリデーションを一元管理したい → zod
- フォームバリデーション中心 → yup / zod
注意点・Tips
1. allErrors オプションで全エラーを取得する
デフォルトでは最初のエラーで検証が停止します。すべてのエラーを一度に取得したい場合は allErrors: true を指定してください。
const ajv = new Ajv({ allErrors: true });
⚠️ セキュリティ上の注意:
allErrors: trueは信頼できない入力に対して使うと、巨大なスキーマでDoS攻撃のリスクがあります。本番環境では信頼できるデータにのみ使用してください。
2. removeAdditional で余分なプロパティを自動削除
const ajv = new Ajv({ removeAdditional: true });
const schema = {
type: "object",
properties: {
name: { type: "string" },
},
additionalProperties: false,
};
const data = { name: "tanaka", hack: "malicious" };
ajv.validate(schema, data);
console.log(data); // { name: "tanaka" } — "hack" が自動削除される
3. coerceTypes で型の自動変換
クエリパラメータなど、文字列で渡される値を自動的に適切な型に変換できます。
const ajv = new Ajv({ coerceTypes: true });
const schema = {
type: "object",
properties: {
age: { type: "number" },
active: { type: "boolean" },
},
};
const data: Record<string, unknown> = { age: "25", active: "true" };
ajv.validate(schema, data);
console.log(data); // { age: 25, active: true } — 文字列から変換される
4. エラーメッセージのカスタマイズ
ajv-errors パッケージを使うと、スキーマ内にカスタムエラーメッセージを定義できます。
npm install ajv-errors
import Ajv from "ajv";
import ajvErrors from "ajv-errors";
const ajv = new Ajv({ allErrors: true });
ajvErrors(ajv);
const schema = {
type: "object",
properties: {
age: {
type: "integer",
minimum: 18,
errorMessage: {
type: "年齢は整数で入力してください",
minimum: "18歳以上である必要があります",
},
},
},
required: ["age"],
};
const validate = ajv.compile(schema);
validate({ age: 15 });
console.log(validate.errors?.[0]?.message);
// "18歳以上である必要があります"
5. JSON Schema のドラフトバージョンに注意
ajv v8 はデフォルトで JSON Schema draft-2020-12 に対応していますが、draft-07 のスキーマも広く使われています。古いスキーマを使う場合はインスタンス生成時に注意してください。
// draft-07 を明示的に使いたい場合
import Ajv from "ajv/dist/2019"; // draft-2019-09
// または
import Ajv from "ajv"; // draft-07 がデフォルトサポート
※ ajv v8 のデフォルトインポート(
import Ajv from "ajv")は draft-07 と draft-2012-12 の共通部分をサポートします。$dynamicRefなど 2020-12 固有の機能を使う場合はajv/dist/2020からインポートしてください。
まとめ
ajvは、JSON Schemaに基づく高速かつ柔軟なバリデーションライブラリです。スキーマのコンパイルによる高いパフォーマンス、$ref によるスキーマ分割、カスタムキーワード、型の自動変換など、実運用に必要な機能が揃っています。OpenAPI/Swagger との親和性が高く、言語を問わずスキーマを共有できる点が、zodやyupにはない大きな強みです。