Your schema is the database

Write one .forge schema. ForgeDB compiles it into a real columnar database, a REST API, and typed clients for TypeScript, Python, Rust, and Go — every type, query, and route tailored to your schema at compile time. Not a runtime ORM. Nothing reflects your schema at runtime.

More ways to install — Homebrew, npm, pip, Docker, Nix, cargo →

Write the schema. Get the stack.

Every type, query, filter, relation, and route is generated and tailored to your models — then compiled. The database that runs already knows your data by name; nothing reflects a schema at runtime.

schema.forge
// schema.forge — the single source of truth
User {
  id: +uuid
  name: string @length(1, 100)
  email: &string @email
  created_at: +timestamp
  posts: [Post]
}

Post {
  id: +uuid
  title: string @length(1, 200)
  slug: &string
  views: ^u64
  published: bool
  author: *User
  created_at: +timestamp

  @projection(card: title, slug, views)
}

Typed clients in the languages you already ship

The same schema generates a network client for every language on your stack — dataclasses in Python, structs in Rust and Go, interfaces in TypeScript. Same methods, same shapes, no hand-written HTTP and no drift between them.

app.ts
import { ForgeDBClient } from "./generated/types";

const db = new ForgeDBClient("http://localhost:3000");

// createPost() is typed to PostCreate; returns the new id
const id = await db.createPost({
  title: "Hello, ForgeDB",
  slug: "hello-forgedb",
  views: 0,
  published: true,
  author: userId,
});

// listPost() → ListResult<Post> { data, total, limit, offset }
const { data, total } = await db.listPost({
  filter: { published: true },
  sort: "views",
  order: "desc",
  limit: 10,
});

Prefer to embed the database instead of calling it over HTTP? The same schema also generates in-process native bindings — PyO3 for Python, NAPI-RS for Node and Bun, and a WASM read-replica for the browser. See the generate command →

Your schema is a compile-time input to generation — never a runtime input to a generic engine.

That one decision is why there's no query planner to fight, no ORM to out-clever, and no schema drift to chase down. The generated code already knows your data; the substrate it links — storage, WAL, change feed, auth — knows nothing about it, and never will. Read the concepts →

A real database, batteries generated in

Durability, concurrency, real-time, multi-tenancy — the things you'd bolt on later are generated per schema over a small, published substrate. And every one is honest about exactly where its limits are.

Small, fast, and honest

Benchmarked fairly — matched durability across engines. At the fsync-barrier tier ForgeDB ties SQLite and redb; relaxed, it's the fastest of the group.

See the numbers
4 languages
typed REST clients from one schema — TypeScript, Python, Rust, Go
1.88 MB
on-disk footprint — smallest of the embedded four
0.37 s
group-commit bulk load of 10k rows
0 deps
on any runtime ORM — only schema-agnostic substrate

From schema to server

The whole workflow is three commands. Everything else is generated.

01

Write a schema

Describe your models, relations, and constraints in a declarative .forge file.

User {
  id: +uuid
  email: &string @email
  posts: [Post]
}
02

Generate

Transpile the schema into a tailored Rust database, a REST API, an OpenAPI spec, and typed clients for every language on your stack.

forgedb generate all --output ./generated
03

Build & serve

Compile the generated crate and run the server — a real REST API over columnar storage.

forgedb build && ./target/release/server

Stop writing the same data layer

Install the CLI, write one schema, and let ForgeDB generate the database, the API, and the typed clients — so you build features, not plumbing.

Search documentation

Find pages across the ForgeDB docs