Skip to main content
TypeBox is a TypeScript library where types are JSON Schema objects. No export step is needed; the schema you write is the schema you send.

Install

npm install @sinclair/typebox ai @ai-sdk/openai

Basic usage

TypeBox types produce JSON Schema directly. Pass them to the Vercel AI SDK’s jsonSchema() wrapper to use with dottxt:
import { Type, Static } from "@sinclair/typebox";
import { createOpenAI } from "@ai-sdk/openai";
import { generateObject, jsonSchema } from "ai";

const Contact = Type.Object({
  name: Type.String(),
  email: Type.String(),
  role: Type.String(),
}, { additionalProperties: false });

type Contact = Static<typeof Contact>;

const dottxt = createOpenAI({
  baseURL: "https://api.dottxt.ai/v1",
  apiKey: process.env.DOTTXT_API_KEY!,
});

const { object } = await generateObject({
  model: dottxt.chat("openai/gpt-oss-20b"),
  schema: jsonSchema<Contact>(Contact),
  schemaName: "contact",
  prompt: "Extract: John Smith <john@acme.com>, VP Engineering",
});

console.log(object.name);

Adding constraints

TypeBox methods map directly to JSON Schema keywords:
const UserProfile = Type.Object({
  username: Type.String({ minLength: 3, maxLength: 20, pattern: "^[a-z0-9_]+$" }),
  bio: Type.String({ maxLength: 200 }),
  role: Type.Union([
    Type.Literal("admin"),
    Type.Literal("editor"),
    Type.Literal("viewer"),
  ]),
}, { additionalProperties: false });

Optional and nullable fields

const Lead = Type.Object({
  name: Type.String(),
  email: Type.String(),
  company: Type.Optional(Type.Union([Type.String(), Type.Null()])),
  phone: Type.Optional(Type.Union([Type.String(), Type.Null()])),
}, { additionalProperties: false });
Type.Optional() removes the field from required. Type.Union([Type.String(), Type.Null()]) allows null.

Arrays

const Survey = Type.Object({
  question: Type.String(),
  options: Type.Array(Type.String(), { minItems: 2, maxItems: 6 }),
  tags: Type.Array(Type.String(), { maxItems: 5 }),
}, { additionalProperties: false });

Nested objects

const Address = Type.Object({
  street: Type.String(),
  city: Type.String(),
  country: Type.String(),
}, { additionalProperties: false });

const Customer = Type.Object({
  name: Type.String(),
  billing_address: Address,
  shipping_address: Address,
}, { additionalProperties: false });

Type mapping

TypeBoxJSON SchemaTypeScript
Type.String(){"type": "string"}string
Type.Number(){"type": "number"}number
Type.Integer(){"type": "integer"}number
Type.Boolean(){"type": "boolean"}boolean
Type.Null(){"type": "null"}null
Type.Array(T){"type": "array", "items": T}T[]
Type.Literal("x"){"const": "x"}"x"
Type.Union([A, B]){"anyOf": [A, B]}A | B
Type.Optional(T)removes from requiredT | undefined
Type.Object({...}){"type": "object", ...}{...}

Notes

  • Pass { additionalProperties: false } as the second argument to Type.Object() when you want strict object schemas. TypeBox does not set this by default, so omit it only when you intentionally want an open object.
  • TypeBox types are plain objects, so JSON.stringify(Contact) gives you the JSON Schema directly.
  • Static<typeof Schema> extracts the TypeScript type from a TypeBox schema, giving you type safety on both ends.

Next steps