スキーマ定義の「ネスト」という手間

**OpenAPI Specification(OAS)**は、RESTful APIのインターフェースを記述する標準仕様です(以前は「Swagger」と呼ばれていました)。APIドキュメントの自動生成、クライアントSDKの生成、モックサーバーの構築など、開発フローの自動化に不可欠な存在です。

ただ、リクエスト/レスポンスボディの「Schema」定義を手書きするのは手間がかかります。とくに階層が深いオブジェクトを type / properties / required / items に合わせて書き起こすのは、時間がかかるうえに入力ミスの元です。

JSON→OpenAPI生成ツール は、サンプルJSONを貼り付けるだけで components/schemas 形式の定義を自動生成します。

JSONからOpenAPIスキーマを生成する方法|Swagger対応

型推論のルール

値を1つずつ見て、OpenAPIの型に対応づけていきます。

private static inferSchema(value: unknown): OpenApiSchema {
    if (value === null) return { type: 'null' };

    if (Array.isArray(value)) {
        // 配列の中身が全部同じ型ならそのまま、混在していればoneOfで表現
        const itemSchemas = this.mergeSchemas(value.map((item) => this.inferSchema(item)));
        const items = itemSchemas.length === 1 ? itemSchemas[0] : { oneOf: itemSchemas };
        return { type: 'array', items };
    }

    if (typeof value === 'number') {
        return { type: Number.isInteger(value) ? 'integer' : 'number' };  // 整数と小数を区別
    }
    // オブジェクトは再帰的にpropertiesへ展開し、存在するキーはすべてrequiredとみなす
}

ポイントは2つあります。

  1. 数値は Number.isInteger で整数/浮動小数を自動判定します。30integer30.5number になります
  2. 配列内の型混在は oneOf で表現します。["admin", 1, true] のような配列があれば、items: { oneOf: [{type: string}, {type: integer}, {type: boolean}] } のように出力されます

日付文字列の自動検知

ISO 8601形式の文字列は、正規表現でパターンマッチして format: date-time を自動付与します。

if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z$/.test(stringValue)) {
    return { type: 'string', format: 'date-time' };
}

2026-01-15T12:00:00Z のような値は自動検知されますが、日付のみ(2026-01-15)や、タイムゾーンオフセット付き(+09:00)の形式はこの正規表現にマッチしないため、生成後に手動で format: date を追加する必要があります。

使い方と出力例

{
  "id": 123,
  "name": "Yamada Taro",
  "email": "taro@example.com",
  "roles": ["admin", "editor"],
  "profile": {
    "createdAt": "2026-01-15T12:00:00Z",
    "isActive": true
  }
}

このJSONから、idintegernameemailstringrolesarrayprofile はネストした objectcreatedAtformat: date-time 付きの string として推論されます。

  1. APIを実行して実際のJSONデータを取得する
  2. JSON→OpenAPI生成ツール にそのJSONを貼り付ける
  3. 数秒で components/schemas 形式の定義が出力される
  4. 生成された定義を openapi.yaml などの定義ファイルへ統合する

「必須項目」はJSONにあるキー全部という前提

現在の実装では、サンプルJSONに存在するキーはすべて required に入ります

for (const [key, val] of Object.entries(value)) {
    properties[key] = this.inferSchema(val);
    required.push(key);   // 存在するキーは無条件でrequired
}

これは「1件のサンプルJSONだけでは、そのキーが本当に必須なのか、たまたま値が入っていただけなのかを区別できない」ためのシンプルな割り切りです。実際のAPIで省略可能なフィールドがある場合は、生成後に手動で required から取り除いてください。

生成後に必ず確認したい項目

JSONから作ったOpenAPIスキーマは、完成品ではなく「精度の高い下書き」と考えるのが安全です。

  • 実際には省略される可能性がある項目を required から外す
  • ID・金額・件数などの数値型が integernumber のどちらか確認する
  • 日付文字列に想定した format が付いているか確認する(date-time 以外は自動検知されない)
  • 配列が空のサンプルだけになっていないか確認する(空配列は items: {} になる)
  • descriptionexample を追加して、利用者が意味を理解できるようにする

OpenAPI 3.1準拠

生成されるスキーマは OpenAPI 3.1.0 基準です。OpenAPI 3.0の nullable: true ではなく、3.1で標準化された type: "null"(またはunion型表現)が使われます。既存プロジェクトがOpenAPI 3.0で統一されている場合は、この違いを吸収する変換が必要になることがあります。

よくある質問

Swaggerにも使えますか?

はい。OpenAPIは以前Swaggerと呼ばれていた流れがあり、現在でも「Swaggerスキーマ」「Swagger UI」と呼ばれる場面があります。生成したスキーマはSwagger UIなどのドキュメント表示にも活用できます。

リクエストボディとレスポンスボディの両方に使えますか?

使えます。POSTやPUTのリクエストJSON、GETのレスポンスJSON、Webhookのペイロードなど、JSON構造が分かるものならスキーマ化の起点にできます。

配列の中身の型がバラバラな場合はどうなりますか?

oneOf を使って複数の型を列挙する形で出力されます。実務では型を統一するか、oneOf のまま許容するかをAPI設計として判断してください。

OpenAPI 3.0と3.1の違いは気にするべきですか?

既存プロジェクトがOpenAPI 3.0で統一されている場合は、出力を取り込む前に nullabletype: null の扱いを確認してください。新規作成や内部ツール用途なら、OpenAPI 3.1の表現に寄せても問題ないケースが多いです。

まとめ

  • 型推論は値の再帰走査。数値は整数/浮動小数を自動判別、配列の型混在は oneOf で表現
  • 日付検知は正規表現ベースで date-time 形式のみ対応。それ以外は手動調整が必要
  • JSONに存在するキーは無条件で required になる。省略可能な項目は生成後に外す
  • 出力はOpenAPI 3.1.0基準

自動生成はあくまで下書きです。生成後に descriptionexample を追加すると、チームメンバーやAPI利用者に優しい実用的なドキュメントに仕上がります。