From 817ca6e8a70b87c29e3bf66433b9e71b7e47f311 Mon Sep 17 00:00:00 2001 From: willbot Date: Mon, 27 Jul 2026 12:12:04 +0200 Subject: [PATCH] fix(auth): set search_path per connection, not only via startup options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The auth module's pool set search_path via the libpq `options` startup param. Prisma Postgres honors it, but the `prisma-composer dev` local Postgres emulator (and poolers such as pgbouncer) drop the startup `options` param, so Better Auth's schema-unqualified queries resolved against `public` and failed with `relation "user" does not exist`. Also issue `SET search_path` on each new pool connection, which is honored anywhere an ordinary query runs — the emulator, pgbouncer, and Prisma Postgres alike. Verified end-to-end: a fresh `prisma-composer dev` stack now completes email+password signup with no manual DB changes. Signed-off-by: willbot Signed-off-by: Will Madden --- .../2-shared-modules/auth/src/auth-options.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/1-prisma-cloud/2-shared-modules/auth/src/auth-options.ts b/packages/1-prisma-cloud/2-shared-modules/auth/src/auth-options.ts index 00abec6e..db052bd5 100644 --- a/packages/1-prisma-cloud/2-shared-modules/auth/src/auth-options.ts +++ b/packages/1-prisma-cloud/2-shared-modules/auth/src/auth-options.ts @@ -53,11 +53,23 @@ function hardenedPool(databaseUrl: string): pg.Pool { connectionString: databaseUrl, // Better Auth is schema-unqualified; every query runs against the auth // schema via search_path — the same posture the conformance test pins. + // `options` applies it at connection startup on Prisma Postgres, but the + // local `prisma-composer dev` emulator (and poolers such as pgbouncer) + // drop the startup `options` param, so it is also set per connection below. options: `-c search_path=${AUTH_SCHEMA}`, connectionTimeoutMillis: 20_000, idleTimeoutMillis: 5_000, }); pool.on('error', (err) => console.error('pg pool idle client error', err)); + // Portable search_path: an in-session SET runs on every new connection, so + // it holds even where the startup `options` param is ignored (the dev + // emulator). Without it, Better Auth's unqualified queries resolve against + // `public` there and fail with `relation "user" does not exist`. + pool.on('connect', (client) => { + void client + .query(`SET search_path TO ${AUTH_SCHEMA}`) + .catch((err) => console.error('auth: failed to set search_path', err)); + }); return pool; }