The tedium of nested schema definitions

The OpenAPI Specification (OAS) is the standard, language-independent way to describe a RESTful API’s interface (formerly known as “Swagger”). It underpins auto-generated documentation, client SDK generation, and mock servers.

Hand-writing the “Schema” for a request or response body — mapping deeply nested objects to type / properties / required / items — is slow and error-prone.

The JSON to OpenAPI Generator takes a sample JSON payload and produces a components/schemas definition automatically.

Generate an OpenAPI schema from JSON | Swagger-compatible

How type inference works

The generator walks the JSON value by value, mapping each one to an OpenAPI type.

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

    if (Array.isArray(value)) {
        // if every item shares one schema, use it directly; otherwise wrap in 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' };  // distinguish int from float
    }
    // objects recurse into properties; every key present is treated as required
}

Two details matter here:

  1. Numbers are classified with Number.isInteger30 becomes integer, 30.5 becomes number.
  2. Mixed types inside an array become oneOf. An array like ["admin", 1, true] produces items: { oneOf: [{type: string}, {type: integer}, {type: boolean}] }.

Automatic date-string detection

ISO 8601 strings are matched against a regular expression and get format: date-time attached automatically.

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

A value like 2026-01-15T12:00:00Z is detected, but date-only strings (2026-01-15) or ones with a timezone offset (+09:00) don’t match this pattern — you’ll need to add format: date manually for those after generation.

Usage and example output

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

From this, id is inferred as integer, name/email as string, roles as array, profile as a nested object, and createdAt as a string with format: date-time.

  1. Get a real JSON payload from an API call
  2. Paste it into the JSON to OpenAPI Generator
  3. Get a components/schemas definition within seconds
  4. Merge the generated definition into your openapi.yaml

Every key present becomes “required” — by design

Under the current implementation, every key present in the sample JSON is marked required.

for (const [key, val] of Object.entries(value)) {
    properties[key] = this.inferSchema(val);
    required.push(key);   // any key present is unconditionally required
}

This is a deliberate simplification: from a single sample JSON payload alone, there’s no way to tell whether a key is genuinely required or just happened to have a value that time. If your real API has optional fields, remove them from required manually after generating.

What to double-check after generating

Treat a JSON-derived OpenAPI schema as a high-quality draft, not a finished spec.

  • Remove fields from required that can legitimately be omitted
  • Confirm whether numeric fields (IDs, amounts, counts) should be integer or number
  • Confirm date strings have the format you expect — only date-time is auto-detected
  • Check that arrays weren’t sampled empty (an empty array becomes items: {})
  • Add description and example so consumers understand what each field means

OpenAPI 3.1 compliance

The generated schema targets OpenAPI 3.1.0. Rather than OpenAPI 3.0’s nullable: true, it uses the type: "null" (or union type) form standardized in 3.1. If your existing project is standardized on OpenAPI 3.0, you may need to translate this difference.

FAQ

Does it work for Swagger too?

Yes. OpenAPI was previously called Swagger, and the terms “Swagger schema” and “Swagger UI” are still common. The generated schema can be used directly with Swagger UI and similar documentation tools.

Can I use it for both request and response bodies?

Yes — POST/PUT request JSON, GET response JSON, webhook payloads, anything with a discoverable JSON structure works as a starting point.

What happens if an array has mixed types?

The generator expresses it with oneOf, listing each distinct type. Decide as part of your API design whether to unify the type or accept the oneOf as-is.

Should I worry about OpenAPI 3.0 vs 3.1?

If your existing project is standardized on OpenAPI 3.0, check how nullable vs. type: null is handled before importing the output. For new projects or internal tooling, sticking with the OpenAPI 3.1 representation is usually fine.

Summary

  • Type inference is a recursive value walk: numbers split into integer/float, mixed-type arrays become oneOf
  • Date detection is regex-based and only covers date-time format — everything else needs manual adjustment
  • Every key present in the sample becomes required — remove optional fields after generating
  • Output targets OpenAPI 3.1.0

Auto-generation is a draft. Add description and example afterward to turn it into documentation your team and API consumers will actually enjoy reading.