Supabase
Supabase now fits through two honest paths:
- the PostgreSQL connection layer
- the direct Supabase JS client runtime
Both paths keep the same schema definition and the same ORM query surface.
Which path to use
Use the PostgreSQL path when:
- the app already owns a
pgpool or client - you want SQL-style setup helpers such as
pushSchema(...) - you want the ORM to behave like the normal PostgreSQL runtime
Use the direct Supabase JS path when:
- the app already owns a
createClient(...)instance - you want to accept that client directly instead of asking for a second DB client
- you want the ORM to speak the Supabase table API rather than a hidden raw
pgbridge
PostgreSQL path
If you already have a pg pool or client connected to Supabase, use the
PostgreSQL aliases in @farming-labs/orm-sql.
import { createOrm } from "@farming-labs/orm";
import { createSupabasePoolDriver } from "@farming-labs/orm-sql";
import { Pool } from "pg";
import { appSchema } from "./schema";
const pool = new Pool({
connectionString: process.env.SUPABASE_DB_URL,
});
const orm = createOrm({
schema: appSchema,
driver: createSupabasePoolDriver(pool),
});If you already have a connected PostgreSQL client instead of a pool:
import { createOrm } from "@farming-labs/orm";
import { createSupabaseClientDriver } from "@farming-labs/orm-sql";
const orm = createOrm({
schema: appSchema,
driver: createSupabaseClientDriver(pgClient),
});This path stays intentionally thin because it is still just PostgreSQL.
Direct Supabase JS path
If the app already has a Supabase client, use the dedicated runtime:
import { createOrm } from "@farming-labs/orm";
import { createSupabaseDriver } from "@farming-labs/orm-supabase";
import { createClient } from "@supabase/supabase-js";
import { appSchema } from "./schema";
const supabase = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_SERVICE_ROLE_KEY!);
const orm = createOrm({
schema: appSchema,
driver: createSupabaseDriver({
client: supabase,
}),
});Or let the runtime helpers detect it directly:
import { createOrmFromRuntime } from "@farming-labs/orm-runtime";
const orm = await createOrmFromRuntime({
schema: appSchema,
client: supabase,
});This runtime uses Supabase's own client API. It does not wrap a hidden raw PostgreSQL bridge.
Setup helpers
If the package or framework needs to prepare the database before tests or setup flows, use the PostgreSQL path with the runtime-aware setup helpers:
import { pushSchema } from "@farming-labs/orm-runtime/setup";
await pushSchema({
schema: appSchema,
client: pool,
});Or, if you want setup plus the ORM back:
import { bootstrapDatabase } from "@farming-labs/orm-runtime/setup";
const orm = await bootstrapDatabase({
schema: appSchema,
client: pool,
});For the direct Supabase JS runtime, pushSchema(...) and applySchema(...)
stay no-op. That path assumes the Supabase tables already exist.
Generated output
If the consuming app wants generated artifacts instead of only the live runtime, the same schema can still render:
import { renderDrizzleSchema, renderPrismaSchema, renderSafeSql } from "@farming-labs/orm";
const prismaSchema = renderPrismaSchema(appSchema, {
provider: "postgresql",
});
const drizzleSchema = renderDrizzleSchema(appSchema, {
dialect: "pg",
});
const sqlSchema = renderSafeSql(appSchema, {
dialect: "postgres",
});Connection guidance
Supabase's own docs are still the right place to choose the actual connection mode, TLS settings, and connection string for your environment:
Keep the provider-recommended TLS settings for your environment. Avoid disabling certificate verification in connection examples unless you are debugging a local-only setup and fully understand the tradeoff.
Why this matters
This lets a library or framework:
- keep one schema definition
- accept either a raw PostgreSQL client or a Supabase client
- reuse the same ORM query surface across both app styles
- avoid forcing every Supabase app into the same connection strategy
How is this guide?