Documentation

JSON, CSV, OpenAPI, GraphQL → TypeScript interfaces or Zod schemas. In one command.

Installation

npm install -D snaptype

Requires Node.js 20+. ESM only.

Quick start

# TypeScript interfaces from a JSON file
snaptype from-json users.json -o src/types/user.ts

# Zod schema from a REST API response
snaptype from-url https://api.example.com/users -o src/schemas/user.ts --zod

# Both TypeScript and Zod at once
snaptype from-json data.json --zod --ts -o src/types.ts

from-json

Generate TypeScript types or Zod schemas from one or more JSON files.

snaptype from-json <file> [options]

Examples

# Single file
snaptype from-json data.json -o src/types/data.ts

# Override the root type name
snaptype from-json response.json --name ApiResponse -o src/types.ts

# Zod schema output
snaptype from-json data.json --zod -o src/schemas/data.ts

# Both TypeScript and Zod
snaptype from-json data.json --zod --ts -o src/types.ts

# Multiple files — merged into one unified type (Pro)
snaptype from-json sample1.json sample2.json sample3.json -o src/types.ts

Options

FlagDescription
--name <name>Override the root type name
--watchRegenerate automatically when the file changes (Pro)

When multiple JSON files are provided (Pro), snaptype merges all samples into a single schema. Fields that appear in some samples but not all become optional, giving you the most accurate type when a real-world API returns slightly different shapes across calls.

from-url

Fetch JSON from one or more HTTP(S) URLs and generate types.

snaptype from-url <url> [options]

Examples

# From a REST API
snaptype from-url https://api.github.com/users/octocat -o src/github.ts

# Custom root name
snaptype from-url https://api.example.com/me --name CurrentUser -o src/me.ts

# With authentication header
snaptype from-url https://api.example.com/me \
  -H "Authorization: Bearer $TOKEN" \
  -o src/me.ts

# Multiple endpoints merged — absent fields become optional (Pro)
snaptype from-url https://api.example.com/users/1 \
  https://api.example.com/users/2 \
  -o src/user.ts

Options

FlagDescription
--name <name>Override the root type name
-H, --header <header>HTTP header, repeatable. Format: "Name: Value"
--watchRe-fetch and regenerate on a schedule (Pro)

Passing multiple URLs requires a Pro licence. Same merge rules as multi-file JSON: absent fields become optional, type conflicts fall back to string.

from-csv

Generate types from the columns of a CSV file. Column headers become property names. Types are inferred from the values in each column. Semantic hints (email, URL, ISO date) are applied automatically.

snaptype from-csv <file> [options]

Examples

# TypeScript interfaces from CSV
snaptype from-csv data.csv -o src/types/row.ts

# Snake case property names
snaptype from-csv data.csv --naming snake -o src/types/row.ts

# File without a header row — provide column names manually
snaptype from-csv data.csv \
  --columns id,name,score,active \
  -o src/types/row.ts

# Watch mode — regenerates on every file save (Pro)
snaptype from-csv data.csv --watch -o src/types/row.ts

Options

FlagDescription
--columns <col1,col2,...>Column names when the file has no header row
--watchRe-run on file change (Pro)

Nullable fields

Empty cells are normalized to null and mark the field as nullable. A column where at least one row is empty becomes string | null (or the equivalent inferred type).

from-stdin

Read JSON from standard input (pipe) and generate types. --name is required because there is no filename to derive the type name from.

snaptype from-stdin --name <TypeName> [options]

Examples

# Pipe from curl
curl https://api.example.com/me | snaptype from-stdin --name CurrentUser

# Pipe from another command
cat response.json | snaptype from-stdin --name ApiResponse -o src/types.ts

# With Zod output
curl https://api.example.com/products | snaptype from-stdin --name Product --zod

Options

FlagDescription
--name <name>Required. Root type name

from-openapi

Parse an OpenAPI 3.x specification (YAML or JSON, local file or URL) and generate types for all schemas. By default, one file is generated per schema — provide a directory as output. Use --single-file to combine all schemas into a single file.

snaptype from-openapi <source> [options]

Examples

# One file per schema (default) — output must be a directory
snaptype from-openapi ./openapi.yaml -o ./src/types/

# All schemas in a single file
snaptype from-openapi ./openapi.yaml --single-file -o ./src/api-types.ts

# Remote spec
snaptype from-openapi https://petstore3.swagger.io/api/v3/openapi.json -o ./types/

# With Zod schemas
snaptype from-openapi ./openapi.yaml --zod -o ./src/schemas/

Options

FlagDescription
--single-fileOutput all schemas into one file instead of one file per schema

What gets generated

  • Request/response body schemas
  • Reusable component schemas (#/components/schemas/*)
  • oneOf / anyOf → TypeScript union types
  • allOf → merged flat object

from-graphql Pro

Generate TypeScript interfaces or Zod schemas from a live GraphQL API. Requires SNAPTYPE_LICENSE_KEY to be set.

snaptype from-graphql <endpoint> [options]

How it works

The CLI does not run the introspection locally. It sends the endpoint, headers, and generation options to the snaptype server (api.snaptype.dev), authenticated with your licence key. The server runs the introspection, maps the schema, generates the files, and returns their contents. One file is written per user-defined type in the output directory. Deprecated fields and built-in introspection types (__ prefix) are excluded.

Examples

# Public endpoint
snaptype from-graphql https://api.example.com/graphql -o src/graphql-types/

# With authentication header
snaptype from-graphql https://api.example.com/graphql \
  -H "Authorization: Bearer $TOKEN" \
  -o src/graphql-types/

Options

FlagDescription
-H, --header <header>HTTP header forwarded to the GraphQL endpoint. Repeatable. Format: "Name: Value"

Type mapping

GraphQL typeOutput
String, IDstring
Int, Floatnumber
Booleanboolean
Custom scalarunknown
OBJECT, INPUT_OBJECTinterface with fields
ENUMtype Foo = "A" | "B"
UNION, INTERFACEtype Foo = A | B | C
NON_NULL wrapperfield is not optional, not nullable
LIST wrapperfield: ItemType[]

barrel

Generate an index.ts file that re-exports every .ts file in a directory.

snaptype barrel <dir> [options]

Examples

# Default: outputs <dir>/index.ts
snaptype barrel ./src/types

# Custom output path
snaptype barrel ./src/types -o src/types/index.ts

# Watch mode — regenerates when files are added or removed
snaptype barrel ./src/types --watch

Generated output

// Generated by snaptype — do not edit manually
export * from './user.js';
export * from './post.js';
export * from './product.js';

Options

FlagDescription
-o, --output <file>Output path (default: <dir>/index.ts)
--watchRegenerate when .ts files are added or removed

diff Pro

Detect breaking changes between two snaptype-generated TypeScript files.

snaptype diff <old> <new> [options]

Examples

# Compare two versions
snaptype diff types-v1.ts types-v2.ts

# CI mode — exits with code 1 if breaking changes are found
snaptype diff old-types.ts new-types.ts --ci

Output format

Breaking changes (2):
  [breaking] User.email field removed
  [breaking] User.role type changed: string "admin" | "editor"

Non-breaking changes (1):
  [ok]       User.nickname field added

Detected change types

ChangeBreaking
Type removedYes
Field removedYes
Field type changedYes
Nullability changedYes
Enum values changedYes
Optional field became requiredYes
Required field addedYes
Type addedNo
Optional field addedNo
Required field became optionalNo

CI/CD integration

# GitHub Actions example
- name: Check for breaking type changes
  run: npx snaptype diff types/v1.ts types/v2.ts --ci
  env:
    SNAPTYPE_LICENSE_KEY: ${{ secrets.SNAPTYPE_LICENSE_KEY }}

mock Pro

Generate representative mock data from a JSON file.

snaptype mock <file> [options]

Examples

# JSON output (default)
snaptype mock schema.json -o mocks/data.json

# TypeScript const export
snaptype mock schema.json --ts -o mocks/data.ts

TypeScript output (--ts)

export const mockUser = {
  id: 0,
  email: "user@example.com",
  createdAt: "2024-01-01T00:00:00.000Z",
  active: true,
} as const;

Options

FlagDescription
-o, --output <file>Output file path
--tsEmit a TypeScript as const export instead of raw JSON

to-zod Pro

Convert existing TypeScript interfaces and type aliases to Zod schemas.

snaptype to-zod <file> [options]

Examples

# Convert a file of interfaces to Zod
snaptype to-zod src/types/user.ts -o src/schemas/user.ts

# With readonly
snaptype to-zod src/types/user.ts --readonly -o src/schemas/user.ts

Input / output

src/types/user.ts
export interface User {
  id: number;
  email: string;
  role: "admin" | "user";
  address?: Address;
}

export type Status = "active" | "inactive" | "pending";
src/schemas/user.ts
import { z } from 'zod';

export const UserSchema = z.object({
  id: z.number(),
  email: z.string(),
  role: z.enum(["admin", "user"]),
  address: AddressSchema.optional(),
});
export type User = z.infer<typeof UserSchema>;

export const StatusSchema = z.enum(["active", "inactive", "pending"]);
export type Status = z.infer<typeof StatusSchema>;

Options

FlagDescription
-o, --output <file>Output file path
-n, --naming <convention>Naming convention: camel, pascal, snake
--readonlyEmit .readonly() on all object schemas
-c, --config <path>Path to .snaptyperc config file

Supported types

TypeScriptZod output
stringz.string()
numberz.number()
booleanz.boolean()
nullz.null()
unknown, anyz.unknown()
T[], Array<T>z.array(...)
{ ... } (inline object)z.object({...})
"a" | "b" | "c" (string literal union)z.enum(["a", "b", "c"])
A | B (type union)z.union([...])
T | nullz.nullable(...)
prop? (optional property).optional()
Reference to another typeSchema name (e.g. AddressSchema)

Limitations

  • Only exported interfaces and type aliases are processed — non-exported types are ignored.
  • Generic types (Foo<T>) are not supported — the field is emitted as z.unknown().
  • readonly properties and index signatures are ignored.
  • Intersection types (A & B) are not supported.
  • No semantic hints — email: string stays z.string(), not z.email(). Use from-json / from-url on actual data to get semantic validation.

Output options

TypeScript vs Zod

By default, snaptype generates TypeScript interfaces. Use --zod to generate Zod schemas, and combine with --ts to get both.

FlagsOutput
(none)TypeScript only
--zodZod only
--zod --tsBoth TypeScript and Zod

When generating both, the Zod file is placed next to the TS file with a .zod.ts suffix.

TypeScript output

export interface User {
  readonly id: number;
  email: string;       // email
  createdAt: string;   // ISO 8601
  role: "admin" | "user";
}

Zod output

import { z } from 'zod';

export const UserSchema = z.object({
  id: z.number(),
  email: z.email(),
  createdAt: z.iso.datetime(),
  role: z.enum(["admin", "user"]),
}).readonly();

export type User = z.infer<typeof UserSchema>;

Emit style

// --emit interface (default)
export interface User { ... }

// --emit type
export type User = { ... }

Shared flags

Available on all from-* commands and to-zod.

FlagDefaultDescription
-o, --output <file>stdoutOutput file path. For from-openapi without --single-file, provide a directory.
--zodfalseGenerate Zod schemas
--tsfalseAlso generate TypeScript when --zod is set
-n, --naming <convention>camelNaming convention: camel, pascal, snake
--emit <kind>interfaceDeclaration style: interface or type
--readonlyfalseEmit readonly on all properties
-c, --config <path>auto-detectedPath to .snaptyperc config file
--watchfalseRe-run when source file(s) change (Pro)

Configuration file

Create a .snaptyperc file at the root of your project to set default options. CLI flags always take priority over config values. The config file is discovered by walking up the directory tree from the current working directory.

.snaptyperc
{
  "naming": "camel",
  "emit": "interface",
  "zod": false,
  "outDir": "./src/types"
}
FieldTypeDefaultDescription
naming"camel" | "pascal" | "snake""camel"Naming convention for generated property and type names
emit"interface" | "type""interface"TypeScript declaration style
zodbooleanfalseGenerate a Zod schema (in addition to or instead of TypeScript)
tsbooleanfalseAlso emit the TypeScript file when zod is true
readonlybooleanfalseAdd readonly to every property (TS) or .readonly() on the root schema (Zod)
zodImport"named" | "namespace" | "cjs""named"Import style in generated Zod files
outDirstringOutput directory when -o is not provided
baseUrlstringPrefix prepended to relative URLs in from-url and from-graphql
headersRecord<string, string>Default HTTP headers for from-url and from-graphql requests

Semantic inference

snaptype inspects actual values in your data and applies semantic hints to produce more precise types and Zod validators. No configuration required. For each string field, snaptype samples up to 50 values — if ≥ 60% match a known pattern, the hint is applied. This allows fields that mix valid values with empty strings or nulls to still be detected.

Detected patternExample valueTS outputZod output
ISO 8601 date"2024-01-15T10:30:00.000Z"string // ISO 8601z.iso.datetime()
Email address"hello@example.com"string // emailz.email()
URL"https://example.com/img.png"string // urlz.url()

Semantic hints take priority over enum detection — a field detected as email or datetime is never emitted as a union literal, even if it has low cardinality. When merging multiple files or URLs, a hint is kept only if all sources agree on the same pattern; conflicting hints are dropped and the field stays as string.

Enum detection

When a string field has a small number of distinct values (≤ 10) appearing in at least 80% of samples, snaptype automatically promotes it to a union literal. A minimum of 2 distinct values is required — a field with only one unique value stays as string.

input
[
  { "role": "admin" },
  { "role": "user" },
  { "role": "user" },
  { "role": "moderator" }
]
output
export interface Record {
  role: "admin" | "user" | "moderator";
}

export const RecordSchema = z.object({
  role: z.enum(["admin", "user", "moderator"]),
});

Naming conventions

Applied to both type names and property names at generation time. Raw field names from the source are always preserved internally.

ConventionExample inputOutput
camel (default)user_id, UserIDuserId
pascaluser_id, userIdUserId
snakeuserId, UserIDuser_id

Interface and type alias names are always PascalCase regardless of the --naming flag. The convention only affects property names.

Environment variables

VariableDescription
SNAPTYPE_LICENSE_KEYJWT license key. Required for Pro features.
SNAPTYPE_API_URLOverride the Snaptype API endpoint. Defaults to https://api.snaptype.dev.

Setting the license key

# Temporary (current session)
export SNAPTYPE_LICENSE_KEY=your-key-here

# Permanent (add to your shell profile)
echo 'export SNAPTYPE_LICENSE_KEY=your-key-here' >> ~/.zshrc