Casing
snake_case or camelCase models, mapped at the edge of the database.
Set casing in the config. A config without it uses 'snake', which keeps
database names as they are; better-supabase init writes casing: "camel"
unless you pass --casing snake.
export default defineConfig({ casing: "camel" });With 'camel', models, filters, includes and returned rows use camelCase.
The mapping happens in the query itself, not in a post-processing pass:
await db.customers.findMany({ select: ["id", "organizationId"] });
// GET /customers?select=id,organizationId:organization_idPostgREST renames the columns in its response, so rows need no transform and nested includes keep their shape.
Per-table overrides
export default defineConfig({
casing: "camel",
tables: { legacy_events: { casing: "snake" } },
});Function results
db.$rpc returns rows of a table (returns setof customers) and records
(returns table (customer_id uuid, total numeric)) in the configured casing,
with the same codecs as reads. gen records which functions need it in the
metadata, so other results, such as a scalar, json or jsonb, come back
as Postgres sent them:
const rows = await db
.$rpc("customers_by_status", { p_status: "lead" })
.orThrow();
rows[0].organizationId;returns validates the decoded value, so a schema for the cased row works.
{ raw: true } skips the decoding and returns database names, typed as
unknown. PostgREST sends int8 as a JSON number in function results, so
values above 2^53 lose precision before a bigint codec sees them; cast
them to text in the function when they can be that large.
What stays in database names
- Constraint names (
onConflict: 'customers_organization_id_kvk_key'). - Plugin config columns (
softDelete: { column: 'archived_at' }): these name database columns, and the generated flags hold the app names. - Raw SQL, RPC argument names and the keys inside
jsonresults.
Error details keep the database name in constraint and columns, since
they come from Postgres.
Last updated on