JSON スキーマの検証 発送前にAPI契約違反を検出
JSONスキーマ検証は、API契約違反を元の段階で検出します。基本キーワード、Express用AJVミドルウェア、FastAPI統合、スキーマのずれ検出、およびOpenAPIの関係について学びます。
APIが返すフィールドがスキーマで定義されていない場合、またはリクエストボディに必須プロパティが欠けている場合、契約違反が発生しています。これらのバグは微妙で、しばしば静かで、生産環境で発見するのは非常に難しいものです。JSON スキーマ検証は、それらを早期に検出するための層です — その場所に近い、導入された場所に近いです。
このガイドでは、JSON スキーマの仕組み、Node.js、Python、AWSで使用すべき検証ライブラリ、そしてスキーマが現実と離れていないように保つ方法について説明します。
JSON スキーマとは
JSON スキーマは、JSONドキュメントの構造を記述するための語彙です。コードではありません — メタデータです。有効なJSONオブジェクトの外見を宣言したスキーマを書きます。その後、そのスキーマに対して検証を実行します。
スキーマそのものはJSONドキュメントです。最小限の例は次の通りです:
{
"$schema": "https://json-schema.org/draft/2020-12",
"type": "object",
"properties": {
"email": { "type": "string", "format": "email" },
"age": { "type": "integer", "minimum": 0 }
},
"required": ["email"]
}
それだけです。このスキーマを検証ライブラリに与え、任意のJSONペイロードを渡すと、通過または構造化された失敗リストが得られます。
核心キーワード
少数のキーワードで、実際の世界での検証ニーズの大部分をカバーできます:
| キーワード | 検証する内容 | 例 |
|---|---|---|
type |
値のデータ型 | "type": "string" |
properties |
オブジェクトのフィールドの構造 | "properties": { "name": { "type": "string" } } |
required |
存在すべきプロパティ | "required": ["email", "password"] |
additionalProperties |
未知のプロパティが許容されるか | "additionalProperties": false |
enum |
値が固定されたセットのいずれかでなければならない | "enum": ["admin", "editor", "viewer"] |
format |
セマンティックフォーマットチェック | "format": "email" または "format": "date-time" |
pattern |
文字列が正規表現に一致するか | "pattern": "^[a-z0-9-]+$" |
$ref |
別のスキーマへの参照 | "$ref": "#/$defs/Address" |
additionalProperties: false 特に注目すべきです。これはスキーマを厳格にすることで、スキーマに宣言されていないプロパティに対して検証エラーを発生させます。デフォルトでは無効で、つまりほとんどのスキーマは、明示的に選択しない限り、不正なフィールドを受け入れます。 properties 検証エラーをトリガーします。デフォルトでは無効で、つまりほとんどのスキーマは、明示的に選択しない限り、不正なフィールドを受け入れます。
完全なスキーマ:ユーザー登録リクエスト
ここでは、登録エンドポイントのリクエストボディに使用する完全なJSONスキーマを示します。これは、エンドポイントに触れるすべてのレイヤーで一度書くこと、そして検証するためのスキーマです。
{
"$schema": "https://json-schema.org/draft/2020-12",
"title": "UserRegistration",
"type": "object",
"required": ["email", "password", "username"],
"additionalProperties": false,
"properties": {
"email": {
"type": "string",
"format": "email"
},
"password": {
"type": "string",
"minLength": 8
},
"username": {
"type": "string",
"pattern": "^[a-zA-Z0-9_]{3,32}$"
},
"role": {
"type": "string",
"enum": ["user", "admin"],
"default": "user"
},
"birthDate": {
"type": "string",
"format": "date"
}
}
}
このスキーマを任意のペイロードに対してインタラクティブにテストできます。次の IO Tools JSON スキーマ検証ツール コードベースに組み込む前に使用できます。
APIリクエストボディの検証
Express + AJV
AJVはNode.js用の最も速いJSONスキーマ検証ライブラリです。次のExpressのミドルウェアは、ハンドラに到達する前にリクエストボディを検証します: AJVに、ドキュメント内のすべてのエラーを収集して、最初のエラーに止まらず、すべての検証失敗をクライアントに一括で返すように指示します。
import Ajv from "ajv";
import addFormats from "ajv-formats";
const ajv = new Ajv({ allErrors: true });
addFormats(ajv);
function validateBody(schema) {
const validate = ajv.compile(schema);
return (req, res, next) => {
if (validate(req.body)) {
return next();
}
res.status(400).json({
error: "Validation failed",
details: validate.errors,
});
};
}
// Usage
app.post("/register", validateBody(registrationSchema), registerHandler);
allErrors: true Pythonでは、FastAPIはPydanticを内部で使用しており、タイプアノテーションから自動的にJSONスキーマを生成します。外部からのJSONスキーマを使用している場合、
は、
は標準ライブラリです。 jsonschema AWS API Gateway
from jsonschema import validate, ValidationError
schema = { ... } # your schema dict
try:
validate(instance=request_body, schema=schema)
except ValidationError as e:
return {"error": e.message}, 400
API Gatewayは、リクエストボディの検証をネイティブにサポートしています。モデル(これはJSONスキーマドキュメント)を定義し、そのメソッドに`schema`として関連付けます。検証に失敗したリクエストはゲートウェイレベルで拒否され、Lambda関数が実行される前に検証されます。これにより、一クラスのハンドラエラーが排除され、無効なトラフィックの冷スタート呼び出しも減少します。
複数のスキーマが共通の構造を持つ場合(例:住所、金額、ページネーションオブジェクトなど)は、その構造を一度に REQUEST_BODY で定義し、
で参照します。 $ref と $defs
大きなプロジェクトでは、スキーマは別ファイルに保存され、 $defs URIにポイントしています。検証ライブラリが $ref:
{
"$defs": {
"Address": {
"type": "object",
"required": ["street", "city", "country"],
"properties": {
"street": { "type": "string" },
"city": { "type": "string" },
"country": { "type": "string", "pattern": "^[A-Z]{2}$" }
}
}
},
"type": "object",
"properties": {
"billingAddress": { "$ref": "#/$defs/Address" },
"shippingAddress": { "$ref": "#/$defs/Address" }
}
}
キーワードとURI解決をサポートしている場合、スキーマは遅延読み込みまたはレジストリから読み込まれます。AJVはこれを $ref を通じて処理します。 $id スキーマのずれ addSchema().
スキーマと実際のAPIがずれた状態を「スキーマのずれ」と呼びます。これはより一般的に起こります。スキーマは一度書かれ、APIが進化し、誰もスキーマを更新しません。
症状は微妙です。コード内でフィールド名が変更されたが、スキーマでは変更されていない — 検証はまだ通過します。なぜなら
がfalseに設定されていないためです。必須フィールドが実際にはオプションになった場合、コードがそのフィールドをチェックしなくなったため、スキーマではまだ必須とされていますが、クライアントがそのフィールドを送信しない場合に初めて問題が発覚します。 additionalProperties ずれを検出するには、スキーマ検証をテストとして扱う必要があります。一部のチームは、実際の応答フィクスチャーに対してスナップショットテストを自動化しています。他のチームは、CIでキャプチャされたAPIリクエストのセットに対してスキーマ検証を実行しています。要点は、スキーマを定期的にテストし、単にデプロイしただけではいけないということです。
JSON スキーマとOpenAPI
OpenAPI(以前はSwagger)は、リクエストおよびレスポンスボディをJSON スキーマで記述していますが、いくつかの変更を加えています。以前のバージョン(OpenAPI 2.0、3.0)はJSON スキーマのサブセットを使用し、拡張機能を追加しました。OpenAPI 3.1はJSON スキーマ draft 2020-12により近づいており、スキーマはほとんど互換性があります。
実際の違いは、OpenAPIはスキーマをより大きなドキュメントに包み、パス、操作、認証、サーバーを記述しています。JSON スキーマは単に検証契約です。もしスキーマファーストのAPIを構築している場合、各エンドポイントのJSON スキーマを書く、それを検証し、そのスキーマをそのままOpenAPIドキュメントに昇格させることができます。
既存データからスキーマを生成
新しいAPIでは手でスキーマを書くことが効果的です。既存のAPIでドキュメントがなく、動作が不明な場合、実際のペイロードからスキーマを生成してからそれを厳密に調整するのがより速い方法です。
(Python) および
ツールのように genson (Node.js) はサンプルJSONオブジェクトを受け取り、ドーフィーのスキーマを生成します。生成されたスキーマは常にあまりにも緩やかで、すべてがオプションになります。そして、 generate-schema は含まれていませんが、スタートポイントとして使えます。その後、 additionalProperties: false 、 required、 type 、そして値が制限されている場所で enum を追加します。
このアプローチは、スキーマドキュメントが存在しない第三者APIを導入する場合にも役立ちます。複数の応答をスキーマ生成ツールに通して、出力の結果をマージし、その結果を検証用の契約として使用できます。
構造化データを受け取るすべてのレイヤーにJSON スキーマ検証が必要です — HTTPミドルウェア、メッセージキューのコンシューマ、データベースの書き込みパスです。契約違反が早期に発見されれば、修正コストは低いです。オンラインで JSON スキーマ検証ツール を用いることで、ライブラリ統合に前に進む前に、実際のペイロードに対してスキーマをプロトタイピングできます。これは、どの検証作業でも最初のステップとして適しています。
恵 スコアボードが到着しました!
スコアボード ゲームを追跡する楽しい方法です。すべてのデータはブラウザに保存されます。さらに多くの機能がまもなく登場します!
