SQL DDL·TypeScript·Free

SQL DDL to
TypeScript.

Paste CREATE TABLE statements, get typed TypeScript interfaces. Postgres ENUM types become literal unions. Nullable columns land as ?: T | null. Timestamps as Date. One file per table. No ORM, no runtime — just types you can use with any driver.

Postgres·MySQL·SQL Server·SQLite·MIT
schema.sqlInput
CREATE TYPE payment_status AS ENUM (
  'pending', 'authorized', 'captured', 'refunded', 'failed'
);

CREATE TABLE payments (
  id            UUID PRIMARY KEY,
  customer_id   UUID NOT NULL,
  amount_cents  INTEGER NOT NULL,
  currency      CHAR(3) NOT NULL,
  status        payment_status NOT NULL,
  description   TEXT,
  captured_at   TIMESTAMP,
  created_at    TIMESTAMP NOT NULL
);
compile
src/models/payments.ts tsc cleanOutput
// src/models/payment-status.enum.ts
export type PaymentStatus =
  | 'pending'
  | 'authorized'
  | 'captured'
  | 'refunded'
  | 'failed';

// src/models/payments.ts
import { PaymentStatus } from './payment-status.enum';

export interface Payments {
  id: string;
  customerId: string;
  amountCents: number;
  currency: string;
  status: PaymentStatus;
  createdAt: Date;
  description?: string | null;
  capturedAt?: Date | null;
}
What gets generated

Plain interfaces. One file per table.

Below: TypeScript emitted from a small Postgres schema with an ENUM, a foreign key, and a nullable column. Navigation properties are opt-in — enable the flag and you get typed parent and child references as optional fields (you populate them when you join, leave them out when you don't).

// src/models/order-state.enum.ts
export type OrderState =
  | 'pending'
  | 'paid'
  | 'shipped'
  | 'delivered'
  | 'cancelled';

// src/models/customers.ts
import { Orders } from './orders';

export interface Customers {
  id: string;
  email: string;
  createdAt: Date;
  displayName?: string | null;
  orders?: Array<Orders>;
}

// src/models/orders.ts
import { OrderState } from './order-state.enum';
import { Customers } from './customers';
import { OrderLines } from './order-lines';

export interface Orders {
  id: string;
  customerId: string;
  state: OrderState;
  totalCents: number;
  placedAt: Date;
  notes?: string | null;
  shippedAt?: Date | null;
  customer?: Customers;
  orderLines?: Array<OrderLines>;
}
Three things we get right

Grounded in how the DDL reads.

This generator is types-first: it produces TypeScript interfaces and literal unions, no runtime, no query builder. Each claim here is something you can verify by running the converter on your own schema.

01ProofENUM types become literal unions.Spec · InCode · Out
schema.sql fragment
CREATE TYPE payment_status AS ENUM (
  'pending', 'authorized', 'captured', 'refunded', 'failed'
);

CREATE TABLE payments (
  status payment_status NOT NULL,
  ...
);
emitted .ts files
// payment-status.enum.ts
export type PaymentStatus =
  | 'pending'
  | 'authorized'
  | 'captured'
  | 'refunded'
  | 'failed';

// payments.ts — the column is typed by the union, not `string`.
import { PaymentStatus } from './payment-status.enum';

export interface Payments {
  status: PaymentStatus;
  // ...
}
Node.js · node-postgres

Types for the queries you already write.

The generator emits interfaces; you write the SQL. Pass the emitted type as the row generic on pool.query and every column lands typed — including Postgres enums as literal unions and nullable columns as optional + nullable.

src/db/payment-repo.ts generated
// src/db/payment-repo.ts
import { Pool } from 'pg';
import { Payments } from '../models/payments';
import { PaymentStatus } from '../models/payment-status.enum';

// Types only — the generator never writes SQL. You do.
// Here: the emitted interface types a hand-written query.
export class PaymentRepo {
  constructor(private readonly pool: Pool) {}

  async findById(id: string): Promise<Payments | null> {
    const { rows } = await this.pool.query<Payments>(
      `SELECT
         id,
         customer_id    AS "customerId",
         amount_cents   AS "amountCents",
         currency,
         status,
         description,
         captured_at    AS "capturedAt",
         created_at     AS "createdAt"
       FROM payments WHERE id = $1`,
      [id],
    );
    return rows[0] ?? null;
  }

  async findByStatus(status: PaymentStatus): Promise<Payments[]> {
    const { rows } = await this.pool.query<Payments>(
      'SELECT ... FROM payments WHERE status = $1',
      [status],
    );
    return rows;
  }
}
Also available via MCP

Or ask Claude to do it.

The same generator ships as a Model Context Protocol server. It calls the same web API this converter uses, so the output is identical. Point Claude Desktop, Cursor, or any MCP-aware agent at a DDL file and it produces the TypeScript interfaces as a diff — free, no token, no leaving the chat.

// claude-desktop config · .mcp.json
{
  "mcpServers": {
    "metaengine": {
      "command": "npx",
      "args": ["-y", "@metaengine/mcp-server"]
    }
  }
}

// Then in Claude:
// "Load db/schema.sql and generate TypeScript
//  models with navigation properties."
See the MCP page
Questions

Things people ask.

This generator is types-only — no runtime, no query builder. The questions below reflect the shape of the output; if yours is not here, run the converter and see.

Plain TypeScript interfaces — one file per table, plus one file per Postgres ENUM type. Columns map to typed fields. That is it: no repository class, no query builder, no runtime dependency.
More converters

Same engine, other stacks.

Every converter runs the same spec-to-IR pipeline. Pick another source format or target stack.

OpenAPITypeScript
Angular httpResource, React TanStack Query, or framework-agnostic fetch.
Open converter
OpenAPIAngular
Typed services with httpResource, Signals, inject() DI, and interceptors.
Open converter
OpenAPIReact
TanStack Query hooks with stable query keys, or plain async functions.
Open converter
OpenAPIFetch
Framework-agnostic fetch client with ApiResult, middleware, and retries.
Open converter
OpenAPIJava Spring
RestClient services, Java records, sealed interfaces, Spring Boot 3.
Open converter
OpenAPIPython
Async httpx clients paired with Pydantic v2 models.
Open converter
OpenAPIGo
Idiomatic net/http client, context-aware, pointer-nullable fields.
Open converter
OpenAPIKotlin
Ktor client, kotlinx.serialization, sealed interfaces for oneOf.
Open converter
OpenAPIC#
Records, nullable reference types, JsonPolymorphic, HttpClient.
Open converter
OpenAPIRust
reqwest client with serde-tagged enums for oneOf and Option<T>.
Open converter
GraphQLAngular
Observable services with typed queries, mutations, and graphql-ws subscriptions.
Open converter
GraphQLReact
TanStack Query hooks with discriminated-union types and query keys.
Open converter
GraphQLKotlin
Ktor, kotlinx.serialization, Flow-based subscriptions.
Open converter
GraphQLC#
Records, nullable types, [JsonPolymorphic] unions, IAsyncEnumerable subscriptions.
Open converter
ProtobufKotlin
Ktor, coroutines, and sealed classes straight from .proto.
Open converter
ProtobufC#
Records, nullable types, Connect RPC over HttpClient.
Open converter
ProtobufGo
Plain structs with json tags, pointer presence, stdlib-only Connect client.
Open converter
SQLKotlin
CREATE TABLE scripts to Kotlin @Serializable data classes.
Open converter

Try it on your own schema.

The converter runs in the browser. Your DDL never leaves the page.

Open the converter