Database Compatibility
Turbine targets PostgreSQL, so it also runs on databases that speak the PostgreSQL wire protocol. They use the default PostgreSQL dialect, with at most a small adapter for migration locking and catalog quirks.
Looking for SQLite, MySQL or SQL Server? Those are separate engines. See Database Engines.
Compatibility matrix#
| Database | Adapter | Status | Notes |
|---|---|---|---|
| PostgreSQL 14+ | None | Full | CI runs the full suite on 14, 15, 16 and 17. Older servers may work but are untested. |
| AlloyDB | alloydb (no-op) | Full | Same wire protocol, catalogs and SQL as PostgreSQL. |
| TimescaleDB | timescale (no-op) | Full | Hypertables introspect as tables. |
| Neon | None | Full | Use turbine-orm/serverless for its HTTP driver. |
| Supabase | None | Full | Standard PostgreSQL; connect directly. |
| YugabyteDB | yugabytedb | Experimental | CI runs only a statement-timeout check, nightly. |
| CockroachDB | cockroachdb | Experimental | CI runs a statement-timeout and row-lock check on v24.1. Results below are from v26.2.5. |
Connect to any of them with a standard connection string:
import { TurbineClient } from 'turbine-orm';
import { SCHEMA } from './generated/turbine/metadata.js';
const db = new TurbineClient(
{
connectionString: process.env.DATABASE_URL,
ssl: { rejectUnauthorized: true }, // most managed services require TLS
},
SCHEMA,
);TimescaleDB#
turbine generate reads hypertables like any other table. It reads tables only by default, so continuous aggregates are left out: pass --include-views to add views and materialized views as read-only entities, or query them with db.sql:
type DailyViews = { day: Date; views: number };
const rows = await db.sql<DailyViews>`
SELECT day, views FROM daily_page_views
WHERE site_id = ${siteId}
ORDER BY day DESC
LIMIT 30
`;Migration locks on distributed databases#
turbine migrate serializes concurrent runs with pg_try_advisory_lock(). On YugabyteDB that lock is scoped to one node, and on CockroachDB v26.2.5 it returns true to every session that asks. Two concurrent runs can both apply the same migrations.
The yugabytedb and cockroachdb adapters replace it with a row in a _turbine_lock table, taken with SELECT ... FOR UPDATE NOWAIT:
// turbine.config.ts
import { cockroachdb } from 'turbine-orm/adapters';
export default {
url: process.env.COCKROACH_URL,
adapter: cockroachdb, // or yugabytedb
};turbine migrate up, deploy and down take the lock through the configured adapter (since 0.92.0).
YugabyteDB#
YugabyteDB reuses the PostgreSQL query layer, so the query surface should behave the same. Apart from the nightly timeout check, nothing here has run against a live cluster. Check these on your own:
- Whether
pg_try_advisory_lock()serializes two sessions (see above). NULLordering in an ordered relation with alimit. CockroachDB inverts it (see below).- Hash indexes are not supported; B-tree, GIN and GiST are.
pg_class.reltuplesmay lag on new tables, which only affects Studio's row estimates.
CockroachDB#
Measured once against CockroachDB v26.2.5 (single node). Behavior changes between CockroachDB versions, so treat this as one data point.
These work through Turbine: nested relations with per-relation orderBy and limit (measured with jsonEncoding: 'object'), introspect(), findMany, count, groupBy, create with RETURNING, atomic increment, relation filters, $transaction with savepoints and sessionContext, statement timeouts, pgvector with an hnsw index, Studio, and migrate deploy and migrate status apart from the lock.
Not tried: the default positional JSON encoding, pipelines, streaming and the flatten strategy.
| Area | Behavior on v26.2.5 | What to do |
|---|---|---|
| Advisory locks | Return true to every caller | Serialize migration runs yourself (see above). |
LISTEN / NOTIFY | Not supported | $listen and $notify fail with a database error, not UnsupportedFeatureError. |
| Default isolation | SERIALIZABLE | Expect more retryable errors under contention. Use withRetry. |
SERIAL columns | Large, non-sequential ids from unique_rowid() | Type them as a 64-bit-safe number or string. |
pg_class.reltuples | Always NULL | Studio's sidebar shows 0 rows; the Data tab counts correctly. |
crdb_internal | Access denied, even to root | The cockroachdb adapter's row-estimate override fails. |
ivfflat index | Unsupported | Use hnsw. |
json_build_object | Returns jsonb, which sorts keys | With jsonEncoding: 'object', join and batched return nested keys in different orders. Compare parsed objects, not strings. |
CockroachDB runs on PostgreSQL's dialect, so Turbine's PostgreSQL capability flags apply as they are. plan_cache_mode, DISTINCT ON and the batched loader's per-parent limit all work on v26.2.5.
Other PostgreSQL-compatible databases#
Turbine will likely work if the database supports:
json_agg,json_build_objectandjson_build_array- Correlated subqueries
$1, $2, ...parametersinformation_schema.tablesandinformation_schema.columns
Run npx turbine generate to check that introspection works. If something breaks, open an issue.