Quick Start

Install Turbine, point it at your database, generate a typed client, and run your first query.

Requirements#

  • Node.js ≥ 20 (the package engines field). The optional SQLite engine uses Node's built-in node:sqlite and needs Node ≥ 22.5.
  • PostgreSQL 14+ for the default path (Neon, Vercel Postgres, Supabase, or local all work). Other engines are documented under Database Engines.
  • "type": "module" in your project package.json is recommended. Turbine is an ESM-first package, but the CLI also works in CommonJS projects (the default from npm init -y): it loads turbine.config.ts and schema files correctly either way.
  • tsx as a dev dependency so the CLI can load TypeScript config and schema files (turbine.config.ts, turbine/schema.ts, TypeScript seeds).

1. Install#

npm install turbine-orm
npm install --save-dev tsx

Turbine has exactly one runtime dependency (pg). ESM and CommonJS both work for importing the library; the CLI still needs tsx for .ts project files.

If your package.json does not already say so, set:

{
  "type": "module"
}

2. Set DATABASE_URL#

Set the standard Postgres connection string in your environment. For local development, use a .env file:

# .env
DATABASE_URL=postgres://user:pass@localhost:5432/mydb

3. Initialize the project#

npx turbine init

This creates turbine.config.ts at the project root and a turbine/ directory for your schema and migrations. The config file looks like this:

// turbine.config.ts
import type { TurbineCliConfig } from 'turbine-orm/cli';
 
const config: TurbineCliConfig = {
  url: process.env.DATABASE_URL,
  out: './generated/turbine',
  schema: 'public',
  migrationsDir: './turbine/migrations',
  schemaFile: './turbine/schema.ts',
};
 
export default config;

schema vs schemaFile:

  • schema is the Postgres schema name (the namespace, almost always 'public').
  • schemaFile is the path to your defineSchema() TypeScript file (used by turbine push and auto-diff migrations).

Never put a file path in schema. A value like './turbine/schema.ts' makes introspection look for WHERE table_schema = './turbine/schema.ts', which matches zero tables. Put the path in schemaFile only.

4. Generate the typed client#

After init, pick the path that matches your database.

Path A, Existing tables#

If the database already has tables, introspect them and emit a fully-typed client:

npx turbine generate
# or: npx turbine pull

Path B, Empty database#

If the database is empty (or you want to own the schema in TypeScript first), define tables, push them, then generate:

  1. Edit turbine/schema.ts with defineSchema(...), see Schema & Migrations.
  2. Apply it to the database:
npx turbine push
  1. Generate the typed client from the live schema:
npx turbine generate

push is the fast local path. For production, prefer SQL migrations (turbine migrate) instead of push, details on the schema page.

What gets written#

Either path writes three files to ./generated/turbine/ (or your config out):

  • types.ts, entity interfaces and Create / Update input types
  • metadata.ts, runtime schema metadata
  • index.ts, a typed TurbineClient subclass with table accessors

5. Your first query#

import { turbine } from './generated/turbine';
 
// No arguments needed: turbine() reads DATABASE_URL from the environment.
// (Pass { connectionString } explicitly if you'd rather not rely on the env var.)
const db = turbine();
 
const users = await db.users.findMany({
  where: { role: 'admin' },
  orderBy: { createdAt: 'desc' },
  limit: 10,
});
 
console.log(users);
 
await db.disconnect();

Loading .env in your app: the turbine CLI auto-loads a local .env, but your application does not (the library never reads files). Populate process.env.DATABASE_URL the standard way, e.g. run the script with Node's built-in flag: node --env-file=.env app.js (Node 20.12+), or call process.loadEnvFile() at startup. Any framework that already loads .env (Next.js, etc.) works too.

db.users is typed, autocompletion surfaces every column, every operator, every relation. users[0].createdAt is a Date, users[0].email is a string, everything is inferred from the generated types.

6. Add nested relations#

const usersWithPosts = await db.users.findMany({
  where: { orgId: 1 },
  with: {
    posts: {
      with: { comments: { with: { author: true } } },
      orderBy: { createdAt: 'desc' },
      limit: 5,
    },
  },
});
 
// One statement, one round-trip, fully typed all the way down:
usersWithPosts[0].posts[0].comments[0].author;

Turbine compiles the with clause to a single SQL statement using PostgreSQL's json_agg and correlated subqueries. No client-side stitching, no N+1. The default 'auto' strategy keeps that plan unless it can prove the join would be the slower one for a particular relation (an unindexed correlation column, say), in which case it moves that one relation to a follow-up statement and returns identical rows. Pin relationLoadStrategy: 'join' for the single statement unconditionally, see Load strategies.

Next steps#

Migrating an existing app? Start with one of these rather than the API reference:

  • Migrate from Prisma, turbine migrate-from-prisma reads your schema.prisma and emits a typed mapping, and createPrismaCompatClient gives you a PrismaClient-shaped surface so your existing include / take / cursor call sites keep working while you port.
  • Migrate from Drizzle, the API mapping, the schema translation, and the behavioural differences worth auditing before you cut over.
  • Why Turbine, if you are still deciding: what is genuinely different, and what is table stakes in 2026.

Otherwise, keep going:

  • Vector Search, typed pgvector KNN ranking and distance filters, no raw SQL.
  • Realtime, Postgres LISTEN/NOTIFY pub/sub with $listen / $notify.
  • Transactions & RLS Sessions, SAVEPOINTs, isolation levels, and sessionContext for Row-Level Security multi-tenancy.
  • Studio, a local, read-only database UI: npx turbine studio.
  • Observability, query lifecycle events and built-in metrics aggregation.
  • API Reference, every query method, WHERE operator, and nested with option.
  • Schema & Migrations, define your schema in TypeScript and ship migrations.
  • Database Engines, SQLite, MySQL, SQL Server, and PowDB behind the same typed API.
  • CLI, every command with examples.
  • Benchmarks: honest numbers against Prisma 7 and Drizzle 0.45 on a local PostgreSQL 17 database.