Skip to the content
GigaJSON
All tools
Format and check
View and query
Convert
JSON to code
Encode and decode
Sample files
Guides Open GigaJSON

How to create a JSON Schema from JSON

· 5 min read · By Amir Badi

You have JSON from an API or a config file and you need a schema for it: to validate requests, to document an API or to catch a broken file before it reaches production. Writing one by hand is slow. Generating one from an example takes a second, but the result is only a starting point.

The quickest route: paste your JSON into the JSON Schema Generator, copy the schema, then tighten the few rules a generator can't guess. Below is a worked example with the generator's real output, the changes I'd make, and what the JSON Schema Validator reports afterwards.

What a JSON Schema is

A JSON Schema is a JSON document that describes other JSON: which fields exist, their types, which ones are required and what values are allowed. A validator reads both and lists every place where the data breaks a rule.

{
  "type": "object",
  "properties": { "id": { "type": "integer" } },
  "required": ["id"]
}

This one says: the data is an object, and it must have an id that's a whole number.

Step 1: generate a schema from example JSON

Give the generator more than one record. From a single object it can't know which fields are optional, so it marks every key as required. Here are two users from an API response:

{
  "users": [
    { "id": 1, "name": "Ada Lovelace", "email": "ada@example.com", "role": "admin",
      "signedUpAt": "2026-03-14T09:30:00Z", "tags": ["beta"], "manager": null },
    { "id": 2, "name": "Linus", "email": "linus@example.com", "role": "editor",
      "signedUpAt": "2026-05-02T16:05:00Z", "tags": [], "manager": 1, "phone": "+44 20 7946 0000" }
  ]
}

The generator's output, unedited:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "users": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": { "type": "integer" },
          "name": { "type": "string" },
          "email": { "type": "string" },
          "role": { "type": "string" },
          "signedUpAt": { "type": "string" },
          "tags": { "type": "array", "items": { "type": "string" } },
          "manager": { "type": ["null", "integer"] },
          "phone": { "type": "string" }
        },
        "required": ["id", "name", "email", "role", "signedUpAt", "tags", "manager"]
      }
    }
  },
  "required": ["users"]
}

It got the structure right:

  • phone is optional, because only one user has it.
  • manager can be null or a whole number, because the examples had one of each.
  • tags is a list of strings, even though one user's list was empty.

Step 2: decide what the generator couldn't know

Now feed it a record that's clearly wrong:

{ "users": [ { "id": 0, "name": "Grace", "email": "grace.example.com", "role": "owner",
  "signedUpAt": "yesterday", "tags": [], "manager": null, "nickname": "G" } ] }

The generated schema accepts it: 0 errors. Every field has the right type, and that's all a generator can check. It saw two emails, not a rule about emails. It saw "admin" and "editor", not the list of allowed roles. The rules come from you:

Rule you know Keyword to add
ids start at 1 "minimum": 1
email is an email address "format": "email"
a role is admin, editor or viewer "enum": ["admin", "editor", "viewer"]
signedUpAt is a timestamp "format": "date-time"
no other fields are allowed "additionalProperties": false

Step 3: make the schema strict

Change those fields in items:

"id": { "type": "integer", "minimum": 1 },
"email": { "type": "string", "format": "email" },
"role": { "enum": ["admin", "editor", "viewer"] },
"signedUpAt": { "type": "string", "format": "date-time" },

Then add "additionalProperties": false beside "required". Paste the schema and the bad record into the JSON Schema Validator, and it lists five errors, each with the path of the value:

$.users[0].id          0 is less than 1.
$.users[0].email       String does not match format "email".
$.users[0].role        Instance does not match any of ["admin","editor","viewer"].
$.users[0].signedUpAt  String does not match format "date-time".
$.users[0].nickname    Property "nickname" is not allowed.

The two good users still pass with no errors.

Be careful with additionalProperties: false. It's right for data you produce, like a config file or a request body, where an unknown key is usually a typo. For responses from an API you don't control, leave it out, or a new field the provider adds will break your validation.

When records have different shapes

Lists often hold more than one kind of object. Two payment methods:

[ { "type": "card", "last4": "4242" }, { "type": "bank", "iban": "DE89" } ]

A generator merges them into one object type where only type is required and last4 and iban are both optional. That schema accepts a card with an iban and no last4. To say what you mean, split it with oneOf and const:

{
  "type": "array",
  "items": {
    "oneOf": [
      { "type": "object", "properties": { "type": { "const": "card" }, "last4": { "type": "string", "pattern": "^[0-9]{4}$" } }, "required": ["type", "last4"] },
      { "type": "object", "properties": { "type": { "const": "bank" }, "iban": { "type": "string" } }, "required": ["type", "iban"] }
    ]
  }
}

Now { "type": "card", "iban": "DE89" } fails: it doesn't match the card shape (no last4) or the bank shape (the type isn't "bank").

Going the other way: JSON from a schema

Sometimes you have the schema and need test data. In the GigaJSON app, open your document, go to Types → Validate, paste the schema and choose Apply schema. Then ⋯ → Generate sample data… creates records that follow it: values picked from enum, numbers within minimum and maximum, and realistic emails and timestamps for format: "email" and "date-time". The data goes to a new document by default, so your original stays as it was.

If you need TypeScript types rather than a schema, JSON to TypeScript reads the same example JSON and writes interfaces, with optional fields marked ?.

A short checklist

  1. Generate from several real records, not one.
  2. Check required: is every key in it really mandatory?
  3. Add format, enum, minimum, maximum and pattern where you know the rule.
  4. Add additionalProperties: false only for data you control.
  5. Split mixed lists with oneOf.
  6. Test with one good record and one bad one before you rely on it.

Questions

Can I generate a JSON Schema from a JSON file?

Yes. Paste the JSON or open the file in GigaJSON's JSON Schema Generator. It writes a draft 2020-12 schema with the type of every field, the required keys and the shape of nested objects and lists. It runs in your browser, so the file isn't uploaded.

Which JSON Schema draft should I use?

Use 2020-12 unless a tool you depend on needs an older one. The generator declares 2020-12 but only uses keywords that drafts 4, 7, 2019-09 and 2020-12 share, and the validator checks against all four. OpenAPI 3.0 is the exception to watch: it uses its own subset of draft 4 and writes nullable: true instead of a list of types.

Why does the generated schema accept data that is obviously wrong?

A generator only sees types. It can't tell that an email needs an @, that a role has three allowed values or that an id starts at 1. Add format, enum, minimum and additionalProperties by hand for the rules that matter.

Can I generate JSON from a JSON Schema?

Yes. In the GigaJSON app, open Types, then Validate, paste the schema and choose Apply schema. Then ⋯ → Generate sample data… creates records that follow it, including enum values, minimum and maximum, and email or date-time formats.

More from the blog