JSON to TypeBox Schema Generator

Convert JSON to TypeBox JSON Schema definitions with TypeScript type inference

TypeBox schema will appear here...

What is JSON to TypeBox Schema Generator?

TypeBox (@sinclair/typebox) is unusual among TypeScript validation libraries: the object you build with Type.Object({...}) is a literal, standards-compliant JSON Schema at runtime AND a static TypeScript type at compile time. There is no codegen step and no separate type declaration — you write the schema once with Type.* builders and extract the type with Static<typeof Schema>. Because the output is real JSON Schema, you do not validate with a TypeBox-specific runtime; you hand the schema to any JSON Schema validator, almost always Ajv, or to Fastify, whose serializer and validator both speak JSON Schema natively. That JSON-Schema-first design is the key difference from Zod, Yup, and Valibot, which all carry their own bespoke validation runtime. TypeBox itself ships essentially zero validation logic — it is a schema *builder*. This makes it the natural choice when you already live in the JSON Schema ecosystem: Fastify route schemas, OpenAPI generation, fast-json-stringify serialization, or AJV-based form validation. This generator infers Type.String, Type.Integer (it distinguishes integers from floats), Type.Number, Type.Boolean, Type.Null, Type.Array, and nested Type.Object from your sample, and appends the Static<typeof T> type alias so you get inference immediately.

How to Use

  1. Paste a representative JSON sample and click Generate; the tool emits Type.Object(...) plus a `type T = Static<typeof T>` line so you get the runtime schema and the static type in one shot
  2. Install the package with `pnpm add @sinclair/typebox` and a validator if you are not on Fastify: `pnpm add ajv`
  3. For Fastify, drop the generated schema straight into a route as `{ schema: { body: T } }` — Fastify uses it for both request validation and faster response serialization, no Ajv setup required
  4. For standalone validation, compile once with Ajv: `const validate = ajv.compile(T)` and reuse the compiled function on the hot path — never recompile per request
  5. Mark genuinely optional properties by wrapping them with Type.Optional(...) after generation, and add constraints inline like Type.String({ format: "email", minLength: 1 }) which AJV enforces at runtime

Why Use This Tool?

Output is real JSON Schema — works with Ajv, Fastify, fast-json-stringify, OpenAPI tooling with zero adapters
Single source of truth: Static<typeof T> derives the TypeScript type from the same object that validates at runtime
Distinguishes integers (Type.Integer) from floats (Type.Number) so your schema is precise, not just number
No build step or codegen — the schema is a plain value you can compose, spread, and pass around
Drops into Fastify route schemas directly, giving validation plus serialization speed-ups for free
Runs entirely in the browser; your payloads stay on your machine

Tips & Best Practices

  • Compile Ajv schemas once and cache the validator — ajv.compile is expensive and calling it per request silently tanks throughput
  • Use the TypeCompiler from @sinclair/typebox/compiler (TypeBox's own JIT) instead of Ajv when you want zero extra dependencies and the fastest check — TypeCompiler.Compile(T).Check(data) returns a boolean
  • Type.Optional(Type.String()) makes a key optional (may be absent); for a present-but-nullable field use Type.Union([Type.String(), Type.Null()]) — these are not the same thing
  • Enable AJV options addFormats (the ajv-formats package) before using format: "email" / "uri" / "date-time" — without it AJV ignores format keywords entirely and your validation passes everything
  • Use Type.Record(Type.String(), Type.Number()) for dictionary-shaped objects with dynamic keys, which the generator cannot infer from a fixed sample
  • In Fastify, set ajv.coerceTypes carefully: query/params strings get coerced to numbers, but you usually want coercion off for JSON request bodies to catch type mistakes

Frequently Asked Questions

What is the full JSON to TypeBox type mapping?

JSON string -> Type.String(). JSON integer -> Type.Integer(). JSON float -> Type.Number(). JSON boolean -> Type.Boolean(). JSON null -> Type.Null(). JSON array -> Type.Array(itemType), with a Type.Union of element types when the array is heterogeneous. JSON object -> Type.Object({...}). An empty array becomes Type.Array(Type.Any()). Optional and nullable are not inferable from a sample, so wrap with Type.Optional() or Type.Union([..., Type.Null()]) as needed.

How is TypeBox different from Zod?

TypeBox builds standards-compliant JSON Schema and contains almost no validation logic of its own — you validate with Ajv, Fastify, or TypeBox's TypeCompiler. Zod ships its own validation runtime with a chainable API (z.string().email()) and is not JSON Schema. Choose TypeBox when you need JSON Schema interop (Fastify, OpenAPI, AJV); choose Zod when you want a self-contained validator with transforms and refinements and do not care about JSON Schema output.

How do I validate data once I have a TypeBox schema?

Two common paths. With Ajv: import Ajv from "ajv"; const validate = ajv.compile(T); if (validate(data)) { /* data is valid */ } else { console.log(validate.errors); }. With TypeBox's own compiler: import { TypeCompiler } from "@sinclair/typebox/compiler"; const C = TypeCompiler.Compile(T); if (C.Check(data)) { ... } else { [...C.Errors(data)] }. Compile once, reuse the result.

How does TypeBox integrate with Fastify?

Fastify natively consumes JSON Schema for route validation and response serialization, and TypeBox schemas are JSON Schema. Register the TypeBoxTypeProvider, then pass schemas in the route config: fastify.withTypeProvider().get("/u/:id", { schema: { params: Params, response: { 200: User } } }, handler). The handler gets fully typed request and reply, validation runs automatically, and responses serialize through fast-json-stringify.

Why distinguish Type.Integer from Type.Number?

JSON has a single number type, but JSON Schema (and therefore TypeBox) separates integers from arbitrary numbers via the "integer" type keyword. Type.Integer() rejects 3.5 while Type.Number() accepts it. The generator inspects each numeric value and emits Type.Integer() when the sample value has no fractional part, giving you a tighter contract — adjust to Type.Number() if that field can legitimately hold decimals.

Is my data sent to a server?

No. Schema generation runs entirely in your browser. Your JSON never leaves your device.

Real-world Examples

A Fastify route body schema

You want to validate a POST body and have Fastify serialize the response fast. Paste the request shape, generate the TypeBox schema, and use it directly as the route body schema — Fastify validates incoming requests against it with no Ajv boilerplate.

Input
{
  "email": "[email protected]",
  "displayName": "Ada",
  "age": 36,
  "marketingOptIn": true,
  "referralCode": null
}
Output
import { Type, Static } from '@sinclair/typebox';

export const CreateUser = Type.Object({
  email: Type.String(),
  displayName: Type.String(),
  age: Type.Integer(),
  marketingOptIn: Type.Boolean(),
  referralCode: Type.Null(),
});

export type CreateUser = Static<typeof CreateUser>;

// In a Fastify route:
// fastify.post('/users', { schema: { body: CreateUser } }, handler)

Compiling with Ajv for standalone validation

Outside Fastify you validate with Ajv. The generator produces the schema; you compile it once and reuse the validator. Note the nested object becomes a nested Type.Object and array elements are inferred from the first item.

Input
{
  "id": 1024,
  "tags": ["a", "b"],
  "owner": {
    "name": "Grace",
    "verified": true
  }
}
Output
import { Type, Static } from '@sinclair/typebox';
import Ajv from 'ajv';

export const Record = Type.Object({
  id: Type.Integer(),
  tags: Type.Array(Type.String()),
  owner: Type.Object({
    name: Type.String(),
    verified: Type.Boolean(),
  }),
});

export type Record = Static<typeof Record>;

const ajv = new Ajv();
const validate = ajv.compile(Record); // compile ONCE
export function isRecord(data: unknown): data is Record {
  return validate(data);
}

Related Tools