How to Convert SQL CREATE TABLE to TypeScript, Pydantic, and Go Types
Convert database CREATE TABLE DDL statements into TypeScript interfaces, Python Pydantic models, and Go structs with type mappings, nullability rules, and in-browser conversion.
On this page
When developing modern web APIs, backend services, or frontend applications, keeping your application code types in sync with your relational database schema is essential. Manually recreating TypeScript interfaces, Python Pydantic models, or Go structs from SQL CREATE TABLE definitions is repetitive, time-consuming, and a frequent source of subtle runtime bugs—especially around nullability, timestamps, and numeric precisions.
You can convert any SQL schema into strong programming language types instantly using the free browser-based SQL to Types & Models Converter.
Below is a practical guide explaining how relational SQL data types map to TypeScript, Python Pydantic, and Go, along with common pitfalls to avoid.
Example SQL Schema
Consider a typical production e-commerce user and order schema in PostgreSQL:
CREATE TABLE users (
id UUID PRIMARY KEY,
email VARCHAR(255) NOT NULL UNIQUE,
full_name VARCHAR(128) NOT NULL,
is_active BOOLEAN NOT NULL DEFAULT true,
role VARCHAR(32) NOT NULL DEFAULT 'customer',
tags TEXT[] NOT NULL DEFAULT '{}',
metadata JSONB,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
last_login_at TIMESTAMPTZ
);
When building an API that queries this table, each language ecosystem represents these fields with different idioms:
Generated TypeScript Interface
In TypeScript, database rows are commonly represented as interfaces with optional or nullable types and camelCase property names:
export interface User {
/** SQL: UUID | PRIMARY KEY */
id: string;
/** SQL: VARCHAR(255) */
email: string;
/** SQL: VARCHAR(128) */
fullName: string;
/** SQL: BOOLEAN | DEFAULT: true */
isActive: boolean;
/** SQL: VARCHAR(32) | DEFAULT: 'customer' */
role: string;
/** SQL: TEXT[] | DEFAULT: '{}' */
tags: string[];
/** SQL: JSONB */
metadata: Record<string, unknown> | null;
/** SQL: TIMESTAMPTZ | DEFAULT: CURRENT_TIMESTAMP */
createdAt: string;
/** SQL: TIMESTAMPTZ */
lastLoginAt: string | null;
}
Generated Python Pydantic v2 Model
In Python backends (such as FastAPI), schemas are validated using Pydantic models with explicit optional types and default fallbacks:
from datetime import datetime
from typing import Any
from uuid import UUID
from pydantic import BaseModel, Field
class User(BaseModel):
id: UUID # SQL: UUID, PK
email: str # SQL: VARCHAR(255)
full_name: str # SQL: VARCHAR(128)
is_active: bool = True # SQL: BOOLEAN, default=true
role: str = 'customer' # SQL: VARCHAR(32), default='customer'
tags: list[str] = Field(default_factory=list) # SQL: TEXT[]
metadata: dict[str, Any] | None = None # SQL: JSONB
created_at: datetime # SQL: TIMESTAMPTZ, default=CURRENT_TIMESTAMP
last_login_at: datetime | None = None # SQL: TIMESTAMPTZ
Generated Go Struct
In Go, field names must be capitalized (PascalCase) to be exported across packages, while struct tags specify the serialized JSON and database column names:
package models
import (
"time"
)
type User struct {
ID string `json:"id" db:"id"` // UUID (PK)
Email string `json:"email" db:"email"` // VARCHAR(255)
FullName string `json:"full_name" db:"full_name"` // VARCHAR(128)
IsActive bool `json:"is_active" db:"is_active"` // BOOLEAN
Role string `json:"role" db:"role"` // VARCHAR(32)
Tags []string `json:"tags" db:"tags"` // TEXT[]
Metadata map[string]any `json:"metadata" db:"metadata"` // JSONB
CreatedAt time.Time `json:"created_at" db:"created_at"` // TIMESTAMPTZ
LastLoginAt *time.Time `json:"last_login_at" db:"last_login_at"` // TIMESTAMPTZ
}
Core Challenges in Mapping SQL Schemas to Code
1. Nullable Columns vs Optional Properties
In SQL, every column without an explicit NOT NULL constraint is nullable by default. However, programming languages treat “can be null” and “can be missing” differently:
- SQL
NULL: The column exists in the row, but its value is empty or unknown. - TypeScript
nullvsundefined: A database result typically contains{ last_login_at: null }, not{}. Therefore, typing the field asstring | nullmatches database driver output (such aspg,mysql2, or SQLite) better thanlast_login_at?: string. - Go pointers: Go primitive types cannot be
nil. To represent nullable columns, use pointer types (e.g.*string,*time.Time,*int64) or standard library wrappers likesql.NullString. - Python
None: In Python 3.10+, union syntaxtype | None = Nonecleanly expresses nullable fields.
2. Timestamps and Date Strings
Relational databases store timestamps with microsecond precision and timezone offsets. In application runtimes:
- When fetched over HTTP or JSON APIs, dates are almost always serialized as ISO 8601 strings (e.g.
"2026-10-11T08:30:00Z"). - In TypeScript frontend applications, typing timestamps as
stringavoids unintended timezone mutations. When using an ORM like Prisma or TypeORM, typing them asDatemay be preferred. - In Python,
datetime.datetimeis the standard type, and Pydantic automatically serializes and deserializes ISO strings.
3. Financial Decimals and Floating-Point Precision
SQL types like DECIMAL(12, 2) or NUMERIC(18, 4) are exact fixed-point numbers used for monetary values.
- JavaScript and TypeScript only have the IEEE-754 double-precision
numbertype, which can introduce rounding errors on operations like0.1 + 0.2. For financial applications, string representations or specialized libraries (such asdecimal.jsorbignumber.js) are recommended. - In Python, always map
DECIMAL/NUMERICtodecimal.Decimalto preserve exact precision. - In Go, map to
float64or a decimal package likeshopspring/decimal.
4. Semi-Structured JSON Columns
Modern databases frequently mix relational and document paradigms via JSON (MySQL, SQLite) and JSONB (PostgreSQL):
- TypeScript: Use
Record<string, unknown>rather thananyto enforce type narrowing before property access. - Python: Use
dict[str, Any]. - Go: Use
map[string]anyorjson.RawMessagewhen deferring deserialization.
Data Type Comparison Matrix
| Database Type | PostgreSQL / MySQL / SQLite | TypeScript | Python (Pydantic) | Go Struct |
|---|---|---|---|---|
| 32-bit Integer | INT, INTEGER, SERIAL |
number |
int |
int |
| 64-bit Integer | BIGINT, BIGSERIAL |
number |
int |
int64 |
| Fixed Decimal | DECIMAL(p, s), NUMERIC |
number (or string) |
Decimal |
float64 |
| Floating Point | FLOAT, DOUBLE, REAL |
number |
float |
float64 |
| Character String | VARCHAR(n), TEXT |
string |
str |
string |
| Boolean Flag | BOOLEAN, TINYINT(1), BIT |
boolean |
bool |
bool |
| Timestamp with TZ | TIMESTAMPTZ, DATETIME |
string (or Date) |
datetime |
time.Time |
| Calendar Date | DATE |
string (or Date) |
date |
time.Time |
| Semi-Structured | JSON, JSONB |
Record<string, unknown> |
dict[str, Any] |
map[string]any |
| UUID | UUID, UNIQUEIDENTIFIER |
string |
UUID |
string |
| Enumeration | ENUM('active', 'inactive') |
'active' | 'inactive' |
str |
string |
| PostgreSQL Array | TEXT[], INT[] |
string[], number[] |
list[str], list[int] |
[]string, []int |
Converting Schemas Online
To convert your database tables without installing CLI packages or command-line generators:
- Open the SQL to Types Converter.
- Select your target language format: TypeScript Interface, TypeScript Type, Python Pydantic v2, Python Dataclass, Go Struct, Rust Struct, or JSON Schema.
- Choose your property naming preference:
camelCase,snake_case, or preserve exact SQL names. - Paste your
CREATE TABLEDDL statement into the editor. - Click Convert to Types to inspect and copy your generated code.
You can also click Open DDL in SQLite Playground to verify table creation and execute queries in the browser using the SQLite Playground, or align syntax formatting with the SQL Formatter.