---
title: "Ajvとは：JSON SchemaをコンパイルするNode.jsの検証ライブラリの使い方とエラー整形"
url: "https://www.issoh.co.jp/tech/details/19150/"
published: 2026-11-01
updated: 2026-11-01
categories: ["テスト"]
publisher: "株式会社一創"
---

# Ajvとは：JSON SchemaをコンパイルするNode.jsの検証ライブラリの使い方とエラー整形

Ajvは、JSON Schemaで書いた検証ルールをJavaScriptの関数へコンパイルし、その関数でデータを検査するライブラリです。名前はAnother JSON Schema Validatorの略で、2026年11月1日時点の最新版はnpmで8.20.0です。この記事では、npm installからcompileとvalidateの基本、strictモードやallErrorsの既定値、ajv-errorsで日本語メッセージを付ける方法、APIで400を返す実装、standaloneコードとReDoS対策までを順に扱います。最後にZodとの選び分けを条件付きで示します。

## まとめ：Ajvは起動時に1回コンパイルし本番ではallErrorsを切る

Ajvはスキーマを検証関数に変換してから使う方式なので、コンパイルを起動時に1回だけ行い、生成した関数を使い回す設計にします。リクエストのたびにcompileを呼ぶ実装は、この方式の利点を自分で消してしまいます。

設定で押さえておく値は、次に挙げる4つです。strictモードは既定で有効で、未知のキーワードやformatがあるとcompileが例外を投げる動作になります。allErrorsは既定で無効で、外部から来るデータの検証では無効のままにする運用です。formatを使うならajv-formatsを追加します。利用者向けの文言はajv-errorsのerrorMessageでスキーマ側に寄せ、エラーオブジェクトはそのまま返さずに整形します。

選び分けは「スキーマをJSONのまま他の言語やOpenAPIと共有するか」で決まります。共有するならAjv、TypeScriptの中で閉じるならZodの方が書く量は少なく済みます。

## Ajvとは何か：JSON Schemaを検証関数へ変換するJavaScriptライブラリ

### Ajvの正式名称とv8.20.0で検証できるスキーマ仕様の対応範囲

Ajvは[GitHubのajv-validator/ajv](https://github.com/ajv-validator/ajv)で開発されているMITライセンスのOSSで、2026年11月1日時点のスター数は14,866です。npmの週間ダウンロード数は2026年10月23日から29日の集計で約5億1,509万回でした。ESLintやwebpackなど多くのツールが依存しているため、自分で入れた覚えがなくても`node_modules`に入っていることがよくあります。

扱える仕様は、JSON Schemaのdraft-06、draft-07、2019-09、2020-12と、RFC 8927のJSON Type Definition（JTD）です。[公式のJSON Schemaページ](https://ajv.js.org/json-schema.html)によると既定のクラスはdraft-07向けで、2019-09と2020-12には、それぞれ別のクラスの読み込みが必要です。draft-04はv8.5.0以降で追加パッケージのajv-draft-04を使います。JSON Schemaそのものの書き方は[JSON Schemaとは？構造を検証する書き方とDraft 2020-12の実装](https://www.issoh.co.jp/tech/details/16289/)で詳しく扱っています。

### コンパイル方式の仕組みとZod・Valibotとの設計上の違い

Ajvはスキーマを読み、その検査だけを行うJavaScriptのコードを生成して関数にします。毎回スキーマを解釈し直す方式より検証が速く、[公式のGetting started](https://ajv.js.org/guide/getting-started.html)も、コンパイルした関数を使い回す前提で書かれています。

ZodやValibotはTypeScriptのコードでスキーマを組み立て、型推論によって入力の型も得られる仕組みです。Ajvのスキーマはただのデータ（JSON）なので、ファイルに置いてPythonやGoの検証ライブラリ、OpenAPIの定義と同じものを読ませられます。この違いが選び分けの軸であり、判断の章で詳しく扱う内容です。TypeScript側の書き味は[Zodの使い方入門](https://www.issoh.co.jp/tech/details/7391/)と比べると差がつかみやすくなります。

## npm install ajvからcompileとvalidateを動かす初回の検証手順

### npm install ajvとajv-formatsによるformat検証の有効化

本体とformat用のプラグインを入れます。Ajvは型定義を同梱しているので、TypeScriptでも@types/ajvは要りません。

```
# 2026-11-01時点: ajv 8.20.0 ／ ajv-formats 3.0.1
npm install ajv ajv-formats
```

[公式のFormat validation](https://ajv.js.org/guide/formats.html)のとおり、v7以降は`email`や`date-time`などのformatが本体から外れ、[ajv-formats](https://github.com/ajv-validator/ajv-formats)に移りました。プラグインを入れずに`format: "email"`を書くと、strictモードの既定ではcompileの時点で`unknown format "email" ignored in schema at path …`という例外になります。v6時代の記事のコードがそのまま動かない原因の多くはここです。

### compileで関数を作りvalidate.errorsのinstancePathを読む

Ajvのインスタンスはアプリ全体で1つにし、スキーマごとにcompileした関数を保持します。CommonJSでは次のように書けます。

```
const Ajv = require("ajv");
const addFormats = require("ajv-formats");

const ajv = new Ajv(); // アプリ全体で1つだけ作る
addFormats(ajv);

const userSchema = {
  type: "object",
  properties: {
    name: { type: "string", minLength: 1, maxLength: 50 },
    email: { type: "string", format: "email", maxLength: 254 },
    age: { type: "integer", minimum: 0 }
  },
  required: ["name", "email"],
  additionalProperties: false
};

// 起動時に1回だけコンパイルし、関数を使い回す
const validateUser = ajv.compile(userSchema);

const input = { name: "山田", age: 20 };
if (!validateUser(input)) {
  console.log(validateUser.errors);
}
```

この入力は`email`が無いので、`validateUser.errors`には次の形のオブジェクトが1件入ります。項目の並びはAjvのソースで`instancePath`、`schemaPath`、`keyword`、`params`、`message`の順です。

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

`instancePath`はJSON Pointer形式で、`/age`なら`age`の値が違反したことを示します。v6の`dataPath`はv8で`instancePath`に改名されたため、古い記事のコードを写すと値が`undefined`になります。もう1つの注意は、`errors`が関数に1つだけの置き場所で、次の呼び出しで上書きされる点です。非同期処理を挟む前に中身を取り出しておきます。

### TypeScriptで書くときのJSONSchemaTypeと型ガードの使い方

[公式のUsing with TypeScript](https://ajv.js.org/guide/typescript.html)によると、compileで作った関数は型ガードとして働き、検証に通ったあとの値は指定した型に絞られます。`strictNullChecks`が有効であることが前提です。

```
import Ajv, { JSONSchemaType } from "ajv";

interface User {
  name: string;
  age?: number;
}

const schema: JSONSchemaType<User> = {
  type: "object",
  properties: {
    name: { type: "string", minLength: 1 },
    age: { type: "integer", nullable: true }
  },
  required: ["name"],
  additionalProperties: false
};

const ajv = new Ajv();
const validate = ajv.compile(schema);

export function parseUser(body: unknown): User {
  if (validate(body)) return body; // ここで body は User 型に絞られる
  throw new Error(ajv.errorsText(validate.errors));
}
```

`JSONSchemaType`はインターフェースとスキーマの食い違いをコンパイル時に指摘してくれます。省略可能なプロパティには`nullable: true`を書く必要があり、ここで型エラーに戸惑う人が多い部分です。unionの網羅までは確かめられないと公式に書かれているので、`anyOf`を多用するスキーマでは型を手書きで併記します。

## strictモードとallErrorsと型変換の既定値を確かめるAjvの設定

### strictモードが未知のキーワードとformatでcompileを止める動作

[公式のStrict mode](https://ajv.js.org/strict-mode.html)は、v7から既定で有効になった仕組みです。`minLength`を`minLenght`と打ち間違えると、Ajvは`strict mode: unknown keyword: "minLenght"`という例外をcompile時に投げます。JSON Schemaの仕様上は未知のキーワードを無視してよいため、緩い検証器では誤字に気づけないまま、チェックが効かない状態で本番に出ます。

この既定は外さずに使います。社内独自のキーワード（例：`x-label`）をスキーマに書きたいときは、`ajv.addKeyword("x-label")`で登録すれば例外になりません。`strict: false`で全体を止めるのは、誤字検知まで捨てることになるので避けます。

### allErrorsの既定は最初の1件で止まる理由と本番での扱い

既定の`allErrors: false`では、最初に見つかった違反で検証を打ち切ります。フォームの全項目にまとめてエラーを出したいので`true`にしたくなりますが、[公式のSecurity considerations](https://ajv.js.org/security.html)は見出しで「本番でallErrorsを使わない」と書いています。全件を調べると、巨大な配列や重い正規表現を含む入力で処理時間が伸び、DoSの入口になるためです。

Fastifyも[公式のValidation and Serialization](https://fastify.dev/docs/latest/Reference/Validation-and-Serialization/)で`allErrors`を明示的に`false`にしており、`true`ではDoS攻撃が可能だと注記しています。全件表示が要るのは管理画面の設定ファイル検証など、入力元が信頼できる場面に限ります。公開APIは1件ずつ返し、画面側の即時チェックで補うのが現実的な分担です。

### coerceTypes・useDefaults・removeAdditionalの作用

次の3つは、検証のついでに渡したオブジェクトそのものを書き換えます。既定はすべて無効です（[公式のAjv options](https://ajv.js.org/options.html)）。

| オプション            | 既定値   | 指定できる値                   | 入力への作用                  |
| ---------------- | ----- | ------------------------ | ----------------------- |
| coerceTypes      | false | true ／ “array”           | 文字列”3″を数値3に変えるなど型を合わせる  |
| useDefaults      | false | true ／ “empty”           | 欠けた項目にスキーマのdefault値を入れる |
| removeAdditional | false | true ／ “all” ／ “failing” | 定義外のプロパティを削除する          |
| allErrors        | false | true                     | 書き換えはせず全違反を集める          |

クエリ文字列はすべて文字列で届くため、`?page=3`を`integer`で検証するなら`coerceTypes`が要ります。Fastifyは既定で`coerceTypes: "array"`、`useDefaults: true`、`removeAdditional: true`を有効にする設定です。`removeAdditional: "all"`は`additionalProperties`の指定に関係なく余分な項目を消すので、監査のために元の入力を残したい処理では、検証前に複製を取っておきます。

### Draft 2020-12を使うときのajv/dist/2020への切り替え条件

`prefixItems`や`$dynamicRef`など[JSON Schema 2020-12](https://json-schema.org/draft/2020-12)の語を使うなら、読み込むクラスを変えます。既定のクラスに2020-12の`$schema`を書いたスキーマを渡すと、メタスキーマが見つからずにcompileが失敗します。

```
// draft-07用の既定クラスではなく、2020-12用のクラスを読み込む
const Ajv2020 = require("ajv/dist/2020");
const ajv = new Ajv2020();

const validatePair = ajv.compile({
  $schema: "https://json-schema.org/draft/2020-12/schema",
  type: "array",
  prefixItems: [{ type: "string" }, { type: "number" }],
  items: false // 3つ目以降の要素を許さない
});
```

2020-12と以前の版は同じインスタンスに混在できません。OpenAPI 3.1の定義から取り込むスキーマは2020-12系なので`Ajv2020`、手書きの既存スキーマがdraft-07ならインスタンスを2つに分けます。新しい語が要らないなら、公式が生成コードの効率を理由に勧めるdraft-07のままで構いません。

## ajv-errorsのerrorMessageによる日本語エラーとAPIの400応答整形

### ajv-errorsのerrorMessageで項目ごとの日本語メッセージを書く

Ajvのメッセージは`must NOT have fewer than 1 characters`のような英文です。画面に出す文言は、[公式のajv-errors](https://ajv.js.org/packages/ajv-errors.html)の`errorMessage`キーワードでスキーマ側に書きます。ajv-errorsのv3がAjv v8に対応し、`allErrors: true`が必須条件です。`false`のまま組み込むと`ajv-errors: Ajv option allErrors must be true`で起動時に止まります。

```
const Ajv = require("ajv");
const ajvErrors = require("ajv-errors");

// ajv-errors は allErrors: true が前提。入力元を信頼できる用途に限る
const ajv = new Ajv({ allErrors: true });
ajvErrors(ajv);

const validateSignup = ajv.compile({
  type: "object",
  properties: {
    name: { type: "string", minLength: 1, maxLength: 50 },
    age: { type: "integer", minimum: 18 }
  },
  required: ["name"],
  additionalProperties: false,
  errorMessage: {
    required: { name: "氏名は必須です" },
    properties: {
      name: "氏名は1〜50文字で入力してください",
      age: "年齢は18以上の整数で入力してください"
    },
    additionalProperties: "想定していない項目が含まれています"
  }
});
```

前の章のとおり、`allErrors`を有効にする以上、このインスタンスは管理画面や社内ツールの入力に使い、公開APIの入口とは分けます。公開APIで日本語を返したいときは、`keyword`と`instancePath`を見て自前の対応表から文言を引くほうが安全です。

### Expressのミドルウェアで400とProblem Details形式を返す実装

エラーオブジェクトをそのまま応答に載せると、`schemaPath`でスキーマの構造が外から読めます。`verbose: true`では、スキーマ本体と入力値まで応答に載る仕様です。応答に出すのは項目の位置と理由だけに絞ります。

```
// compile済みの関数を受け取り、Expressのミドルウェアを返す
function validateBody(validate) {
  return (req, res, next) => {
    if (validate(req.body)) return next();
    const errors = (validate.errors || []).map((e) => ({
      pointer: e.instancePath || "/",
      reason: e.keyword // required, minLength などの機械向けの理由
    }));
    res.status(400).type("application/problem+json").json({
      type: "about:blank",
      title: "入力内容に誤りがあります",
      status: 400,
      errors
    });
  };
}

app.post("/users", express.json({ limit: "100kb" }), validateBody(validateUser), createUser);
```

`express.json`の`limit`で本文の大きさを先に抑え、Ajvに渡る入力の上限を決めておきます。エラーコードの付け方やクライアントの再試行判断まで含めた応答設計は、[APIエラーレスポンス設計とは？コード体系・リトライ判断・漏洩防止の実装手順](https://www.issoh.co.jp/tech/details/17229/)にまとめています。

## コンパイルを起動時に限定するスキーマ管理とstandaloneとReDoS対策

### 単一インスタンスとaddSchemaとgetSchemaで再コンパイルを防ぐ

[公式のManaging schemas](https://ajv.js.org/guide/managing-schemas.html)は、各スキーマを1回だけコンパイルすることと、アプリ全体で単一のインスタンスを使うことを勧めています。Ajvはスキーマのオブジェクトをキーにキャッシュしますが、リクエストごとにスキーマを組み立て直すと別オブジェクトになり、毎回コンパイルが走ります。

```
// schemas.js: 起動時に$idで登録し、使う側はIDで引く
const ajv = new Ajv();
addFormats(ajv);
ajv.addSchema(userSchema, "user");
ajv.addSchema(orderSchema, "order");

// ハンドラ側: 登録済みのコンパイル結果を取り出すだけ
const validate = ajv.getSchema("user");
```

同じIDで`addSchema`を2回呼ぶと`schema with key or id "user" already exists`で例外になるため、登録はモジュールの読み込み時に1回だけ行います。`getSchema`は初回の呼び出しでコンパイルし、以後は同じ関数を返します。

### standaloneコードの生成とCSPで動的評価が禁止される環境への対応

Ajvは生成したコードを実行時に`new Function`で評価します。ブラウザのContent Security Policyで`unsafe-eval`を許していない画面や、コールドスタートを短くしたいサーバーレス関数では、[公式のStandalone validation code](https://ajv.js.org/standalone.html)の機能でビルド時に検証関数をファイルへ書き出します。

```
// build-validators.js: ビルド時に実行し、生成したファイルを配布物に含める
const fs = require("fs");
const Ajv = require("ajv");
const standaloneCode = require("ajv/dist/standalone").default;

const ajv = new Ajv({ code: { source: true, esm: true } });
const validate = ajv.compile({
  type: "object",
  properties: { id: { type: "integer", minimum: 1 } },
  required: ["id"]
});
fs.writeFileSync("validate-id.mjs", standaloneCode(ajv, validate));
```

`esm: true`を外すとCommonJSで出力されます。生成物は実行時にAjv本体を読み込まないので、ブラウザ向けのバンドルからAjvを外せる仕組みです。スキーマを変えたら生成し直す手順が増えるため、CIのビルドに組み込んで手作業を残さないようにします。

### ReDoSと巨大入力に備えるmaxLengthとpatternとRE2の指定

公式のSecurity considerationsは、既定の正規表現エンジン（Node.js組み込み）の最悪計算量が指数時間であり、短い文字列でも処理が止まりうると警告しています。対策は優先度の高い順に次のとおりです。

1. `pattern`や`format`を付けた文字列には必ず`maxLength`を書く
2. `uniqueItems`を付けた配列には`maxItems`を書く
3. 外部入力に自前の`pattern`を使うなら、`new Ajv({ code: { regExp: RE2 } })`で線形時間のnode-re2に差し替える
4. 利用者がアップロードしたスキーマはサイズを制限し、原則として受け付けない

4つ目は見落とされがちです。Ajvはスキーマを自社のコードと同じく信頼できるものとして扱う前提のライブラリです。SaaSの利用者に独自スキーマを登録させる機能を作るなら、検証用のプロセスを分けるなど、Ajvの外側で隔離を用意します。入力検証全体の守備範囲は[入力バリデーションとは？防げる攻撃の範囲と許可リスト設計](https://www.issoh.co.jp/tech/details/16364/)で整理しています。

## Ajvを採用する条件と見送る場面：スキーマ共有の有無で決める判断

### Ajvを選ぶ場面はOpenAPIや他言語とスキーマを共有するAPI開発

Ajvを選ぶのは、検証ルールをJSONのまま複数の場所で使う場合です。OpenAPIで定義したリクエスト本文をサーバーの入口でそのまま検証する、PythonのバッチとNode.jsのAPIで同じスキーマを読む、Fastifyで入力検証と応答の直列化を1つのスキーマで済ませる、といった構成ではAjvが素直に収まります。OpenAPIの側の設計は[OpenAPIとは？Swaggerとの違いとYAMLの書き方](https://www.issoh.co.jp/tech/details/10408/)が参考になります。

外部システムとの連携で、相手が配るJSON Schemaに合わせて受信データを検査する場面でもAjvが第一候補です。連携先ごとのスキーマ管理やエラー時の再送設計まで含めて実装を任せたい場合は、一創の[API開発・システム連携](https://www.issoh.co.jp/service/system/api/)で要件の整理から対応しています。

### Ajvを見送る場面はTypeScriptの中で閉じるフォームの入力検証

スキーマを他へ渡す予定がなく、ReactのフォームやNext.jsのServer Actionsのように検証がTypeScriptの中で完結するなら、Ajvは採りません。`JSONSchemaType`とインターフェースを二重に書く手間が残り、`nullable`やunionで型エラーと格闘する時間が増えるからです。この用途ではZod（2026年11月1日時点で4.6.7）か、バンドルを小さくしたいなら[Valibot](https://www.issoh.co.jp/tech/details/7584/)を選びます。

迷ったときの線引きは1つです。スキーマを「データ」として保存・配布するならAjv、「コード」として型と一緒に書くならZod系です。両方が必要なAPIでは、境界の入口だけAjvで検証し、内部の処理はZodで型を付けるという併用も成り立ちます。

## よくある質問

Ajvを導入するときに寄せられやすい質問をまとめました。

### Ajvの読み方と正式名称は何ですか？

正式名称はAnother JSON Schema Validatorで、AjvはそのAとJとVを取った略称です。公式に定められた読み方は見当たらず、日本語の現場では「エージェイブイ」とアルファベットで読む人が多くいます。npmのパッケージ名は小文字の`ajv`です。ドキュメントは[ajv.js.org](https://ajv.js.org/)にまとまっています。2020年にはOpenJS Foundationのプロジェクトに加わっています。

### Ajvのエラーメッセージを日本語にする方法はありますか？

方法は2つです。1つはajv-errorsを入れ、スキーマに`errorMessage`で日本語の文言を書く方法で、`allErrors: true`が必須になります。もう1つはajv-i18n（4.2.0）で既定メッセージを翻訳する方法です。公開APIでは`allErrors`を有効にしない方が安全なため、`keyword`と`instancePath`から自前の対応表で文言を引く方法も現実的な選択肢になります。

### Ajvでformat: “email”が効かないのはなぜですか？

v7以降、`email`や`date-time`などのformatは本体から外れ、ajv-formatsに移ったためです。`npm install ajv-formats`のあと`addFormats(ajv)`を呼ぶと有効になります。strictモードが既定で有効なので、プラグインが無いままだとcompileの時点で`unknown format`の例外が出ます。v6向けの記事のコードを写したときによく起きる症状です。

### AjvとZodはどちらを使えばよいですか？

スキーマをJSONとして他の言語やOpenAPIと共有するならAjv、TypeScriptの中で検証と型を一緒に書くならZodです。Ajvはスキーマを関数にコンパイルして使い回すので、同じスキーマを大量のリクエストに当てるサーバーの入口に向きます。Zodは型推論で入力の型が自動で得られ、フォームや社内APIの実装量を減らせます。

### Ajvのcompileは毎回呼んでも問題ありませんか？

呼ぶたびにコード生成が走るので、毎回の呼び出しは避ける設計です。Ajvはスキーマのオブジェクトをキーにキャッシュしますが、リクエストごとにスキーマを組み立て直すと別オブジェクトと見なされ、毎回のコンパイルが必要になる仕組みです。起動時に`compile`か`addSchema`で登録し、ハンドラでは生成済みの関数か`getSchema`の結果を使います。インスタンスもアプリ全体で1つに保ちます。

## 関連記事

- [JSON Schemaとは？JSONの構造を検証する書き方とDraft 2020-12の実装を実装者目線で解説【2026年版】](https://www.issoh.co.jp/tech/details/16289/)：Ajvに渡すスキーマの文法と版の違いを確認できます
- [バリデーションとは？入力検証の目的・種類・実装パターンと設計を実装者視点で解説](https://www.issoh.co.jp/tech/details/13486/)：Ajvを置く層とほかの検証との役割分担が分かります
- [APIエラーレスポンス設計とは？コード体系・リトライ判断・漏洩防止の実装手順](https://www.issoh.co.jp/tech/details/17229/)：Ajvの検証結果を返す応答の形を詰められます
- [Zodの使い方入門｜npm install zodとz.infer・safeParse](https://www.issoh.co.jp/tech/details/7391/)：TypeScriptで閉じる場合の選択肢として比べられます
- [OpenAPIとは？Swaggerとの違い（仕様とツール）とYAMLの書き方・3.0〜3.2の差分](https://www.issoh.co.jp/tech/details/10408/)：Ajvで検証するスキーマの出どころになる仕様です

---

出典: [Ajvとは：JSON SchemaをコンパイルするNode.jsの検証ライブラリの使い方とエラー整形](<https://www.issoh.co.jp/tech/details/19150/>)（株式会社一創）
