JSON Schema バリデーション:Node・Python・ブラウザで JSON を検証する(2026 年版)
要点:JSON Schema は JSON データに対する契約だ。フィールドの型、必須キー、制約を宣言しておけば、バリデーターが任意の JSON ドキュメントがその契約に従っているかを判定する。 Node では最速のバリデーターである Ajv、Python ではポータブルなスキーマを扱える jsonschema ライブラリ、ブラウザではフォームや設定の即時フィードバックのために Ajv をバンドルするのが定石だ。2026 年に新規プロジェクトを始めるなら、選ぶべきバージョンは Draft 2020-12 である。
これから見せるのは、最小構成の動く例、3 つのランタイムすべてでの一気通貫パターン、そして「バリデーションは通ったのに本番で弾かれる」という不具合を生む現実の落とし穴だ。
JSON Schema とは何か(そして何ではないか)
一文で言うと
JSON Schema とは、他の JSON ドキュメントの形を記述する JSON ドキュメントだ。バリデーターはスキーマとデータを読み込み、適合しているかどうか、または失敗したパスを返す。
最小限ながら役に立つ例:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": { "name": { "type": "string" } },
"required": ["name"]
}
{"name": "Alice"} は通る。{"age": 30} は失敗する(name がない)。{"name": 42} も失敗する(name が文字列でない)。これがメンタルモデルのすべてだ。
JSON Schema と JSON 構文チェックの違い
別物の問題なのに、よく混同される。
| 観点 | JSON 構文チェック | JSON Schema バリデーション |
|---|---|---|
| 何を確認するか | これは正当な JSON ドキュメントか? | この JSON は契約に一致するか? |
| 検出できるもの | カンマ抜け、シングルクォート、コメント | 型違い、必須フィールド欠落、範囲外の値 |
| ツール | JSON.parse()、JSON整形ツール | Ajv、jsonschema(Python)、fastjsonschema |
| いつ使うか | 何より先、パースより前 | パース直後、ビジネスロジックより前 |
実務では両方やる。ペイロードを JSON整形ツール で整形してパースが通ることを確認したうえで、スキーマを通して契約に一致しているかを検証する。
JSON Schema、JSONPath、JSON Patch、jq、TypeScript の使い分け
5 つのツールがこの問題領域を共有している。判断マトリックスは次のとおり。
| ツール | 答える問い | 使うべき場面 |
|---|---|---|
| JSON Schema | この JSON は期待される構造に一致するか? | API 入力、設定ファイル、フォームのペイロード検証 |
| JSONPath | この JSON から値をどう取り出すか? | ネストされたフィールドの抽出、一括読み取り |
| JSON Patch(RFC 6902) | A から B への差分をどう記述するか? | 共同編集、増分同期 |
| jq | コマンドラインで JSON をどう処理するか? | シェルスクリプト、ログパイプライン、CI チェック |
| TypeScript の型 | 自分のコードはこの形を正しく使っているか? | 1 つのコードベース内でのコンパイル時保証 |
決定的な違いはここにある。JSON Schema は実行時に未知のデータを検証する。TypeScript はコンパイル時に既知のコードを検証する。 TypeScript はサードパーティの Webhook や、ユーザーが貼り付けた JSON にはまったく無力だ。そこに JSON Schema の出番がある。Zod や Pydantic はその中間(コンパイル時の型と実行時バリデーションを両立)に位置するが、これは後で扱う。
JSON Schema と OpenAPI
OpenAPI が JSON Schema を置き換える、というのはよくある誤解だ。実際にはそうではない。OpenAPI はリクエストとレスポンスのボディを記述するために内部で JSON Schema を使い、その上にパス、パラメータ、セキュリティスキーム、サーバー URL を重ねている。スキーマがデータ形状の契約であり、OpenAPI はそれを包む API 契約だ。
| 観点 | JSON Schema | OpenAPI |
|---|---|---|
| スコープ | 1 つの JSON ドキュメントの形 | HTTP API 全体の形 |
| 依存関係 | なし(スキーマは自己完結した JSON) | ボディ定義のために JSON Schema を取り込む |
| バージョン対応 | Draft 7 / Draft 2019-09 / Draft 2020-12 | OpenAPI 3.0 は Draft 4 のサブセット、OpenAPI 3.1 は Draft 2020-12 をネイティブ採用 |
| 典型的な用途 | 設定ファイル、メッセージのエンベロープ、フォーム検証、単一ペイロードの契約 | REST API 設計、SDK 生成、モックサーバー、契約テスト |
| コード生成 | 限定的(quicktype 系のツールが一部あり) | 成熟したエコシステム(openapi-generator、oapi-codegen、ベンダー製 SDK) |
| 契約管理 | 1 形状につき 1 ファイル、ルーティングなし | パス、操作、認証フロー、バージョン付きエンドポイントを 1 つのドキュメントに集約 |
成果物が単一のドキュメント、つまり Webhook ペイロード、設定ファイル、キューのメッセージ、フォームなどであるなら、素の JSON Schema を選ぶこと。記述すべき HTTP の表面がないので、OpenAPI は過剰になる。
HTTP API を公開し、ドキュメント、SDK 生成、モックサーバー、契約テストを 1 つのドキュメントから駆動したいなら OpenAPI を選ぶこと。スキーマはまず schemas/ ディレクトリ配下に独立した JSON Schema ファイルとして定義し、OpenAPI ドキュメントから $ref で参照するのがコツだ。こうしておくと、API 文脈の外でもスキーマを再利用できる。
バージョンの組み合わせがチームを混乱させる。OpenAPI 3.0 は Draft 4 のサブセットを使うので、3.0 のドキュメント内で prefixItems や unevaluatedProperties といった Draft 2020-12 のキーワードは使えない(ジェネレーターは黙って無視する)。OpenAPI 3.1 は Draft 2020-12 のスーパーセットなので、2020-12 で有効なものはすべて 3.1 でも有効だ。選択の余地があるなら、OpenAPI 3.1 を狙い、どこでも Draft 2020-12 のスキーマで書くこと。
はじめての JSON Schema(5 分でできる)
まず押さえるキーワード
これだけで 8 割をカバーできる:
{
"type": "object",
"properties": {
"id": { "type": "integer", "minimum": 1 },
"email": { "type": "string", "format": "email" },
"age": { "type": "integer", "minimum": 0, "maximum": 150 },
"tags": { "type": "array", "items": { "type": "string" }, "minItems": 1 },
"role": { "enum": ["admin", "editor", "viewer"] },
"metadata": { "type": "object", "additionalProperties": true }
},
"required": ["id", "email"],
"additionalProperties": false
}
語彙は次のとおり:
type:string、number、integer、boolean、null、array、objectproperties+required:フィールドを宣言し、必須かどうかを示すenum/const:固定の集合または単一リテラルに制限するminimum/maximum/multipleOf:数値の上下限minLength/maxLength/pattern:文字列長と正規表現minItems/maxItems/uniqueItems:配列の形additionalProperties: false:宣言されていないキーを拒否する(入力契約には常にこれを設定すること)
ユースケース別 JSON Schema 例
上のキーワードは、何を検証するかによって異なる組み合わせで姿を現す。代表的な形をいくつか挙げる。
API リクエストボディ — メールとパスワードを受け取るサインアップエンドポイント:
{
"type": "object",
"properties": {
"email": { "type": "string", "format": "email" },
"password": { "type": "string", "minLength": 8, "maxLength": 128 }
},
"required": ["email", "password"],
"additionalProperties": false
}
設定ファイル — レベルを固定セットに縛るロガー設定:
{
"type": "object",
"properties": {
"level": { "enum": ["debug", "info", "warn", "error"] },
"output": { "type": "string", "default": "stdout" }
},
"required": ["level"],
"additionalProperties": false
}
条件付きルールのあるフォームペイロード — accountType が "business" のときに taxId を必須にする:
{
"type": "object",
"properties": {
"accountType": { "enum": ["personal", "business"] },
"taxId": { "type": "string" }
},
"if": { "properties": { "accountType": { "const": "business" } } },
"then": { "required": ["taxId"] }
}
CSV 行を表す JSON レコード — エクスポートされた注文テーブルの 1 行:
{
"type": "object",
"properties": {
"orderId": { "type": "string", "pattern": "^ORD-[0-9]{6}$" },
"orderedOn": { "type": "string", "format": "date" },
"totalUsd": { "type": "number", "minimum": 0 }
},
"required": ["orderId", "orderedOn", "totalUsd"]
}
Webhook イベントのエンベロープ — oneOf が type リテラルで分岐し、各イベントバリアントが固有のペイロード形を持つ:
{
"oneOf": [
{ "properties": { "type": { "const": "order.created" }, "data": { "$ref": "#/$defs/order" } } },
{ "properties": { "type": { "const": "order.refunded" }, "data": { "$ref": "#/$defs/refund" } } }
]
}
実務でチームが書くものは、この 5 例でほぼ網羅できる。最も近いものをコピーしてフィールド名を調整すればよい。キーワードの語彙はそのままだ。
インストール不要で検証する
スキーマとペイロードを ajv.js.org や jsonschemavalidator.net のプレイグラウンドに貼り付ければ、即座に判定が出る。JSON 自体が怪しければ、まず JSON整形ツール を通すといい。
Node.js で Ajv を使って検証する
インストールと 12 行のサンプル
Ajv は最初の compile 時にスキーマを最適化された関数にコンパイルし、それを再利用する。
npm install ajv
import Ajv from "ajv";
const ajv = new Ajv();
const schema = {
type: "object",
properties: {
name: { type: "string" },
age: { type: "integer", minimum: 0 }
},
required: ["name"]
};
const validate = ajv.compile(schema);
const data = { name: "Alice", age: 30 };
if (!validate(data)) console.log(validate.errors);
else console.log("OK");
Draft 2020-12 への切り替え
デフォルトの Ajv コンストラクタは後方互換のため、いまも Draft 7 に固定されている。2020-12 を使うには明示的にオプトインすること:
import Ajv2020 from "ajv/dist/2020";
const ajv = new Ajv2020({ strict: true, allErrors: true });
これで prefixItems、unevaluatedProperties、$dynamicRef が使えるようになる。各機能の役割は後述の Draft 2020-12 のセクションを参照。
format バリデーションを有効にする
これは Ajv の癖の中でも、開発者がいちばんよく踏み抜く落とし穴だ。format: "email" はデフォルトでは何もしない。仕様上 format は助言的扱いなので、フォーマットモジュールを別途登録する必要がある。
npm install ajv-formats
import addFormats from "ajv-formats";
addFormats(ajv); // これで "format": "email" が実際に検証されるようになる
このステップを飛ばすと、format: "email" を要求するスキーマでも {"email": "not-an-email"} が通ってしまう。本番では必ず ajv-formats を入れること。
Express ミドルウェアの実戦投入
ルートごとに 1 つのバリデーターを用意し、起動時にコンパイルしておく:
import express from "express";
import Ajv2020 from "ajv/dist/2020";
import addFormats from "ajv-formats";
const ajv = new Ajv2020({ allErrors: true });
addFormats(ajv);
const validateUser = ajv.compile({
type: "object",
properties: {
email: { type: "string", format: "email" },
age: { type: "integer", minimum: 13 }
},
required: ["email"],
additionalProperties: false
});
const app = express();
app.use(express.json());
app.post("/users", (req, res) => {
if (!validateUser(req.body)) {
return res.status(400).json({ errors: validateUser.errors });
}
// ... ビジネスロジック
res.status(201).json({ ok: true });
});
唯一、もっとも高くつくミスは、リクエストハンドラーの中で ajv.compile(schema) を呼ぶことだ。コンパイルはモジュールスコープで一度だけ行い、返された関数を使い回す。リクエストごとに再コンパイルすると、スループットが 50 倍以上下がる。
Python で jsonschema を使って検証する
インストールと基本的な使い方
pip install jsonschema
from jsonschema import validate, ValidationError
schema = {
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer", "minimum": 0}
},
"required": ["name"]
}
try:
validate(instance={"name": "Alice", "age": 30}, schema=schema)
print("OK")
except ValidationError as e:
print("FAIL:", e.message, "at", list(e.absolute_path))
Draft202012Validator で全エラーを集める
validate() は最初のエラーで例外を投げる。フォームのレスポンスのように全問題を一度に列挙したいなら、iter_errors を使う:
from jsonschema import Draft202012Validator
validator = Draft202012Validator(schema)
errors = sorted(validator.iter_errors(instance), key=lambda e: e.path)
for err in errors:
print(f" - {'/'.join(map(str, err.absolute_path))}: {err.message}")
これでユーザーは何度もやり直すことなく、すべてを一度で修正できる。
jsonschema と Pydantic の使い分け
優れた Python ライブラリが 2 つあるが、解こうとしている問題が違う。
| 観点 | jsonschema | Pydantic v2 |
|---|---|---|
| スキーマ形式 | JSON の dict(スキーマがデータそのもの) | 型ヒント付きの Python クラス |
| 性能 | インタプリタ動作、Pydantic より約 10〜100 倍遅い | Rust コア、エコシステム最速 |
| 言語横断のポータビリティ | あり(同じスキーマが JS、Go、Rust でも動く) | なし(Python 専用) |
| FastAPI/ネイティブモデル統合 | 手動変換 | 標準搭載 |
Draft 2020-12 のキーワード($dynamicRef など) | 完全対応 | 部分対応 |
本番運用で通用する原則はこうだ。言語横断の契約(OpenAPI、公開 API、Webhook)には jsonschema、内部 Python サービスには Pydantic を使う。 両方を併用しているチームも多い。ゲートウェイ層で jsonschema が契約を強制し、アプリケーション層で Pydantic が型付きビジネスロジックを担う構成だ。スキーマは可搬な成果物であり、Ajv に渡すものとまったく同じになる。
ブラウザで検証する
そもそもクライアント側で検証する理由
重要度の高い順に 3 つある。第一に UX で、ユーザーの入力に合わせて即座にフィードバックが返る方が、サーバー往復より速い。第二に帯域で、明らかなミスはブラウザから出ていかない。第三にセキュリティ衛生で、バックエンドへのゴミの量を減らせる(サーバー検証の代替にはならない)。
クライアント側だけのバリデーションを信用してはいけない。サーバー側でも必ずもう一度検証すること。
Ajv をブラウザ向けにバンドルする
npm install ajv ajv-formats
import Ajv2020 from "ajv/dist/2020";
import addFormats from "ajv-formats";
const ajv = new Ajv2020({ allErrors: true });
addFormats(ajv);
export const validateForm = ajv.compile({
type: "object",
properties: {
email: { type: "string", format: "email" },
password: { type: "string", minLength: 8 }
},
required: ["email", "password"]
});
バンドルサイズは gzip 後でおよそ 30 KB 増える。無視できない大きさだが、十分許容範囲に収まる。サーバーとクライアントで 1 つのスキーマ定義を共有したいチームは、迷わず Ajv を採用する。
軽量な代替:Zod と Valibot
JSON Schema エコシステムを必要とせず、すでに TypeScript で書いているなら、TS ネイティブのバリデーターの方がバンドルが小さく、型推論もより厳密にできる:
import { z } from "zod";
const UserSchema = z.object({
email: z.string().email(),
password: z.string().min(8)
});
const result = UserSchema.safeParse(data);
if (!result.success) console.log(result.error.issues);
Valibot は同等の API で gzip 後およそ 3 KB に収まる。バンドルサイズが最優先なら Valibot を選ぶといい。注意点として、どちらも JSON Schema を生成しない。バックエンド、サードパーティクライアント、OpenAPI ジェネレーターと共有する単一の真実の源が必要なら Ajv に留まること。すべて自前の TypeScript で完結するなら、Zod や Valibot の方が扱いやすい。
Draft 2020-12 で増えたもの
prefixItems でタプル検証
Draft 7 はタプルを items: [] と additionalItems で表現していた。Draft 2020-12 はこれをきれいに分離した:
{
"type": "array",
"prefixItems": [
{ "type": "string" },
{ "type": "number" }
],
"items": false
}
["x", 42] は通る。["x", 42, "extra"] は失敗する。スキーマを読めば、それが何をするかがそのままわかる。
unevaluatedProperties で合成スキーマに対処する
allOf や oneOf を使うチーム全員を悩ませる地味な不具合がある。additionalProperties: false は、それが現れている直近の階層しか見ない。allOf の中の兄弟サブスキーマは、好きにプロパティを宣言できる。Draft 2020-12 の解決策が unevaluatedProperties: false だ:
{
"allOf": [
{ "$ref": "#/$defs/base" }
],
"unevaluatedProperties": false
}
これにより、どの分岐でも評価されなかったプロパティはすべて拒否される。多くの開発者が additionalProperties: false に期待していたのは、まさにこの挙動である。
$dynamicRef で再帰スキーマ
Draft 7 で再帰的なツリースキーマを書こうとして苦労した経験がある人なら、その面倒さは身に染みているはずだ。$dynamicRef と $dynamicAnchor の組み合わせがそれをすっきりさせる:
{
"$dynamicAnchor": "node",
"type": "object",
"properties": {
"value": { "type": "string" },
"children": { "type": "array", "items": { "$dynamicRef": "#node" } }
}
}
再帰は宣言的に書けて、$id の書き換えなしに子孫からオーバーライドできる。
Draft 7 と 2020-12 の選び方
- 新規プロジェクト、モダンなツールチェーン → Draft 2020-12
- OpenAPI 3.1 を作る/使う → 2020-12 がネイティブの方言
- OpenAPI 3.0 や旧サービスとやりとりする → Draft 4(OpenAPI 3.0 は Draft 4 のサブセットを使う。方言を混ぜないこと)
- 幅広いバリデーター互換性が必要(Postman、古い CI ツール) → 交換フォーマットとしては Draft 7 がいまだに最も安全
モダンなバリデーター(Ajv、Python の jsonschema、jsonschema-rs、Java の networknt/json-schema-validator)はすべて、現時点で 2020-12 をサポートしている。
実戦パターン
API 入力のバリデーション
先ほどの Express ミドルウェアが本番で見られる典型形だ。これに加えて 2 つの実践がある。スキーマはすべてリポジトリルートの schemas/ ディレクトリにまとめる、そして ajv test(または Python 同等品)でスキーマ自体を JSON Schema のメタスキーマに照らして検証する CI ステップを追加する。
設定ファイル
Visual Studio Code は SchemaStore と統合されており、package.json、tsconfig.json など多数の設定ファイルで自動補完とインライン検証が効く。自分の設定にも $schema フィールドを追加すれば、エディタユーザーは同じ恩恵を受けられる。
CI のテストフィクスチャ
テストフィクスチャは腐る。誰かがモデルを更新し、フィクスチャは古い形のまま、変更されたフィールドにアサーションが触れていないせいでテストはパスし続ける。これをアサーション実行前のスキーマチェックで捕まえよう:
import { glob } from "glob";
const files = await glob("__tests__/fixtures/*.json");
for (const f of files) {
const data = JSON.parse(await fs.readFile(f, "utf8"));
if (!validate(data)) throw new Error(`${f}: ${ajv.errorsText(validate.errors)}`);
}
スキーマチェックが発火したら、次にやるのは構造の差分確認だ。フィクスチャを JSON 差分ツール に投入し、最新の本番サンプルと比べてどこがズレたかを見る。タイムスタンプや ID が差分の大半を占めるなら、JSON 差分ガイドのスナップショット用パス無視パターンを適用してノイズと信号を分離するとよい。
Webhook ペイロード(Stripe、GitHub)
サードパーティの Webhook は、JSON Schema を適用する価値が最も高い場所のひとつだ。Webhook は契約であり、提供元はそれを変えうる。変えた瞬間に気づきたい。Stripe や GitHub は OpenAPI の記述を公開しており、そこから JSON Schema を抽出できる。受信イベントを検証していれば、破壊的アップグレードが静かに状態を壊すかわりに監視を点灯させる。
スキーマ駆動のフォームバリデーション
React Hook Form には @hookform/resolvers/ajv アダプタがあり、Vue の VeeValidate にも同等の Ajv プラグインがある。どちらもフォームの描画、エラーメッセージ、送信時バリデーションを 1 つの JSON Schema で駆動する。スキーマが単一の真実の源となり、UI はそのルールを継承する。
親しみやすいエラーメッセージ
デフォルトが粗削りな理由
Ajv は標準で #/properties/email format must match "email" のようなエラーを出す。400 をデバッグするエンジニアにはちょうどいい。だがチェックアウトフォームを埋めるユーザーには役に立たない。
ajv-errors でカスタムメッセージ
npm install ajv-errors
import ajvErrors from "ajv-errors";
ajvErrors(ajv);
const schema = {
type: "object",
properties: { email: { type: "string", format: "email" } },
required: ["email"],
errorMessage: {
properties: { email: "有効なメールアドレスを入力してください" },
required: { email: "メールアドレスは必須です" }
}
};
errorMessage キーワードはスキーマの中に留まるので、バリデーションルールとユーザー向けのコピーが一緒に移動する。
ajv-i18n で翻訳済みエラー
ajv-i18n はデフォルトメッセージの翻訳を 30 以上の言語で提供している。起動時に 1 行加えるだけで、バリデーターはスペイン語、フランス語、日本語など、提供したいロケールで話してくれる。errorMessage のオーバーライドがすべての制約をカバーしきれない場合のフォールバックとして便利だ。
スキーマパスをフォームフィールドに対応づける
Ajv の各エラーには /users/0/email のような instancePath が含まれる。多くのフォームライブラリは users[0].email のようなドット区切りパスを期待する。ワンライナーで済む:
const fieldPath = error.instancePath.replace(/^\//, "").replace(/\//g, ".");
Python の jsonschema では error.absolute_path に同じ情報がある。. で結合すれば同じ効果になる。
バリデーションを通り抜けて本番でクラッシュさせる 5 つの罠
1. format はデフォルトで助言的
ajv-formats と addFormats(ajv) がなければ、すべての format キーワードは何もしないのと同じだ。{"format": "email"} は "not-an-email" を受け入れてしまう。本番ではフォーマットパッケージを必ず入れること。
2. additionalProperties のデフォルトは true
additionalProperties: false を付けないと、スキーマは宣言されていないあらゆるフィールドを受け入れる。クライアントは検証をすり抜ける追加フィールドを送り込める。入力契約では additionalProperties: false をデフォルトにし、必要に応じて意図的に緩めるのが筋だ。
3. additionalProperties は合成しない
allOf、oneOf、anyOf の中では、additionalProperties: false は自分の階層しか見ない。兄弟のサブスキーマがすり抜ける。Draft 2020-12 の解決策は unevaluatedProperties: false だ。
4. リモート $ref は本番リスク
$ref: "https://example.com/schema.json" を書くと、Ajv は最初のコンパイル時にネットワーク越しにフェッチに行く。これはレイテンシ、リモートホストが固まったときの DoS 露出、MITM の攻撃面を意味する。$ref のターゲットはすべてインライン化するか、ビルド時にディスクから読み込むこと。
5. 生成されたスキーマは実データから乖離する
quicktype や typescript-json-schema のようなツールは、既存の型からスキーマを生成してくれる。ただし出力は通常、緩すぎる。すべてのフィールドが任意で、additionalProperties は開放されている。生成されたスキーマは下書きとして扱い、手で締めて、実本番サンプルをスキーマで検証する(およびその逆を行う)CI を回し、乖離が早期に表面化するようにするとよい。
性能:数値と経験則
- Ajv(Node.js):コンパイル済みバリデーターは 1 件のチェックを 1 マイクロ秒未満でこなす。本番品質の JS バリデーターでは最速。
jsonschema(Python):インタプリタ動作で、Pydantic より 10〜100 倍遅い。ボトルネックになったらfastjsonschemaに差し替えること。Python コードを生成し、Ajv 並みの性能に届く。- Rust と Go:
jsonschema-rsとxeipuuv/gojsonschemaは、ゲートウェイ層で Ajv からさらに 2〜5 倍速くなる。 - 最大の効果が出る一手はプリコンパイルだ。
ajv.compile(schema)をモジュールロード時に一度だけ呼び、返ってきたバリデーターをリクエストごとに使い回す。リクエストごとに再コンパイルすると、スループットが 50 倍以上落ちる。
よくある質問
JSON Schema バリデーションをやさしく言うと?
JSON Schema バリデーションとは、JSON ドキュメントが契約に従っているかを確認することだ。契約(スキーマ)自体も JSON で、型、必須フィールド、制約を宣言する。バリデーターはスキーマとデータを読み、「合格」または「失敗したパスとその理由」を返す。
オンラインで JSON をスキーマに照らして検証するには?
スキーマとデータを ajv.js.org のプレイグラウンドや jsonschemavalidator.net に貼り付ければ、即座に判定が出る。JSON が壊れていそうなら、まず JSON整形ツール で整えるとよい。どちらもブラウザで動き、アップロードは不要だ。
2026 年で最速の JSON Schema バリデーターは?
Node では、プリコンパイル済みの Ajv が 1 件のチェックを 1 マイクロ秒未満でこなす。Python では、fastjsonschema がコードを生成して Ajv 級のスループットに届く。ゲートウェイ層では jsonschema-rs(Rust)と gojsonschema(Go)が Ajv より 2〜5 倍速い。何を選ぶにせよ、一度プリコンパイルして使い回すこと。
JSON Schema と TypeScript の型の違いは?
TypeScript はあなたが書いたコードをコンパイル時にチェックする。JSON Schema は未知の JSON を実行時にチェックする。HTTP レスポンス、ファイル、ユーザーが貼り付けた内容として届く JSON は TypeScript には見えない。そのために JSON Schema がある。
Draft 2020-12 と Draft 7、どちらを選ぶべきか?
2026 年に新規プロジェクトを始めるなら Draft 2020-12 を選ぶこと。prefixItems、unevaluatedProperties、$dynamicRef は実問題を解決する。OpenAPI 3.1 は 2020-12 をネイティブで使う。Draft 7 に留まるべきなのは、Postman 互換性や古いサービスのためだけだ。OpenAPI 3.0 は Draft 4 のサブセットを使うので、方言を混ぜないこと。
既存の JSON から JSON Schema を生成するには?
3 つの選択肢がある。サンプルを quicktype.io か jsonschema.net に貼る。コマンドラインで npx genson-js または pip install genson && genson sample.json を実行する。あるいは手で書く。自動生成されたスキーマはフィールドが任意、additionalProperties: true と緩すぎるので、契約として扱う前に必ず締めること。
JSON Schema は OpenAPI を置き換えられる?
置き換えられない。OpenAPI はリクエスト/レスポンスボディの記述に内部で JSON Schema を使い、その上にパス、セキュリティスキーム、パラメータ、サーバー URL を加えている。両者は組み合わさる。スキーマを書き、OpenAPI ドキュメントから参照すれば、完全な API 契約が手に入る。
JSON Schema は JSONPath や jq と同じ?
別の問題を解いている。JSON Schema は構造を検証する(「この JSON は契約に一致するか?」)。JSONPath と jq は値を抽出する(「Running フェーズの全 Pod 名」)。検証はスキーマで、クエリは JSONPath か jq でやるという棲み分けだ。
Ajv のバリデーションは通ったのに本番でデータが弾かれるのはなぜ?
ほぼすべてのケースは 3 つの原因に集約される。ajv-formats を忘れていたために format: "email" が一切検証されていなかった、additionalProperties: false を省略したために余計なクライアントフィールドがすり抜けた、allOf や oneOf の中で additionalProperties: false を使って合成されないことに気づいた。最後の場合は unevaluatedProperties: false に切り替えること。
エンドユーザー向けに JSON Schema のエラーメッセージをカスタマイズできる?
できる。Node では ajv-errors を入れてスキーマ内に errorMessage を埋め込み、ajv-i18n で 30 以上のロケールに翻訳する。Python では jsonschema が各エラーオブジェクトにバリデーション文脈をすべて公開しているので、エラー型とパスをデザインシステムが使う任意のコピーに対応づけられる。