xschema

TypeScript

Set up xschema with TypeScript in minutes

Getting Started

Install the client and adapter

bun add @xschemadev/client @xschemadev/zod zod
npm install @xschemadev/client @xschemadev/zod zod
pnpm add @xschemadev/client @xschemadev/zod zod
yarn add @xschemadev/client @xschemadev/zod zod

The example uses Zod, but you can use any adapter.

Create a config file

user.xschema.jsonc
{
  "$schema": "https://xschema.dev/schemas/typescript.jsonc",
  "schemas": [
    {
      "id": "Profile",
      "sourceType": "file",
      "source": "./profile.schema.json",
      "adapter": "@xschemadev/zod"
    }
  ]
}

More on config files in Configuration Reference.

Generate

bunx xschema generate
npx xschema generate
pnpm exec xschema generate
yarn dlx xschema generate

This creates .xschema/xschema.gen.ts with your validators.

Use the client

import {  } from "@xschemadev/client";
import {  } from "./xschema.gen";

const  = ({  });

const profileSchema = ("user:Profile");
const profileSchema: ZodObject<{
    id: ZodString;
    name: ZodOptional<ZodString>;
    email: ZodString;
}, $loose>
const result = .({ : "123", : "alice@example.com" });
const result: ZodSafeParseResult<{
    [x: string]: unknown;
    id: string;
    email: string;
    name?: string | undefined;
}>
if (.) { .(.data., ..);
data: {
    [x: string]: unknown;
    id: string;
    email: string;
    name?: string | undefined;
}
}

See Using the Client for full documentation.

Using the Client

The xschema client provides type-safe runtime schema lookups with full TypeScript autocompletion.

createXSchemaClient

Creates a function for looking up schemas by key with compile-time type safety.

import { createXSchemaClient } from "@xschemadev/client";
import { schemas } from "./.xschema/xschema.gen";

const xschema = createXSchemaClient({
  schemas,
  defaultNamespace: "user", // optional
});

Options

OptionTypeDescription
schemasRecord<string, unknown>The generated schemas object from .xschema/xschema.gen
defaultNamespacestringOptional namespace to use when looking up schemas without a prefix

Schema lookup

Look up schemas using the namespace:id format:

import {  } from "@xschemadev/client";
import {  } from "./xschema.gen";

const  = ({  });

// Full key format
const profileSchema = ("user:Profile");
const profileSchema: ZodObject<{
    id: ZodString;
    name: ZodOptional<ZodString>;
    email: ZodString;
}, $loose>
const configSchema = ("app:Config");
const configSchema: ZodObject<{
    theme: ZodEnum<{
        light: "light";
        dark: "dark";
    }>;
    language: ZodString;
    debug: ZodOptional<ZodBoolean>;
}, $loose>

With defaultNamespace set, you can omit the namespace for that namespace's schemas:

import {  } from "@xschemadev/client";
import {  } from "./xschema.gen";

const  = ({
  ,
  : "user",
});

// Shorthand - resolves to "user:Profile"
const profile = ("Profile");
const profile: ZodObject<{
    id: ZodString;
    name: ZodOptional<ZodString>;
    email: ZodString;
}, $loose>
// Still need full key for other namespaces const config = ("app:Config");
const config: ZodObject<{
    theme: ZodEnum<{
        light: "light";
        dark: "dark";
    }>;
    language: ZodString;
    debug: ZodOptional<ZodBoolean>;
}, $loose>

XSchemaType

Extract TypeScript types from your schemas by key:

import type {  } from "@xschemadev/client";

type Profile = <"user:Profile">;
type Profile = {
    [x: string]: unknown;
    id: string;
    email: string;
    name?: string | undefined;
}
type Config = <"app:Config">;
type Config = {
    [x: string]: unknown;
    theme: "light" | "dark";
    language: string;
    debug?: boolean | undefined;
}

XSchemaType only accepts full namespace:id keys for explicitness. Use the client for shorthand lookups.

Using in function signatures

import type {  } from "@xschemadev/client";
import {  } from "@xschemadev/client";
import {  } from "./xschema.gen";

type  = <"user:Profile">;
const  = ({  });

function (: ): void {
  .(.email);
email: string
} function (: unknown): { const = ("user:Profile"); return .parse();
ZodType<any, any, $ZodObjectInternals<{ id: ZodString; name: ZodOptional<ZodString>; email: ZodString; }, $loose>>.parse(data: unknown, params?: ParseContext<$ZodIssue>): {
    [x: string]: unknown;
    id: string;
    email: string;
    name?: string | undefined;
}
}

Schema types explained

xschema adapters can generate three kinds of outputs:

Schema + Type (most common)

Adapters like Zod, ArkType, Effect, and Valibot generate both runtime validators and types. Both xschema() and XSchemaType<> work:

const schema = xschema("user:Profile"); // runtime validator
type Profile = XSchemaType<"user:Profile">; // extracted type

Type-only

Some adapters may generate only types without runtime validators:

import type {  } from "@xschemadev/client";
import {  } from "@xschemadev/client";
import {  } from "./xschema.gen";

// XSchemaType works - types are available
type  = <"user:Profile">;

// Compile error - no runtime validator for this key
const  = ({  });
const  = ("user:Profile");
Argument of type '"user:Profile"' is not assignable to parameter of type 'never'.

Schema-only

Some adapters may generate validators that don't expose inferred types. XSchemaType returns typeof the schema:

const schema = xschema("user:Profile");
type Profile = XSchemaType<"user:Profile">; // typeof schema

Developer Experience

The xschema client provides excellent TypeScript developer experience with autocomplete and compile-time error checking.

Autocomplete

Your IDE will autocomplete available schema keys as you type:

import {  } from "@xschemadev/client";
import {  } from "./xschema.gen";

const  = ({  });

// Start typing a schema key and see suggestions
const  = ("
  • user:Profile
  • app:Config
u

The valid keys are inferred from the generated schemas object, so you'll see suggestions like "user:Profile" and "app:Config".

Type Errors for Invalid Keys

Invalid schema keys cause compile-time errors:

import {  } from "@xschemadev/client";
import {  } from "./xschema.gen";

const  = ({  });

// Typo in schema key - caught at compile time!
const  = ("user:Proflie");
Argument of type '"user:Proflie"' is not assignable to parameter of type '"user:Profile" | "app:Config"'.

After modifying your JSON Schema files, run xschema generate to update the generated code and keep autocomplete in sync.

What's Generated

The .xschema/xschema.gen.ts file contains:

  • Validators: Native Zod schemas (or ArkType types, Effect schemas, etc.)
  • Types: TypeScript types inferred from the validators
  • Schema registry: An object mapping namespace:id to validators

Example output for a Zod adapter:

// Generated by xschema - DO NOT EDIT
import { z } from "zod"

const user_Profile = z.object({
  id: z.string(),
  name: z.string().optional(),
  email: z.string().email(),
});
type user_ProfileType = z.infer<typeof user_Profile>;

export const schemas = {
  "user:Profile": user_Profile,
} as const;

export type SchemaTypes = {
  "user:Profile": user_ProfileType;
};

On this page