import { Pool } from 'pg'; import { Kysely, PostgresDialect } from 'kysely'; import type { DB } from './db-kysely/schema'; export const pool = new Pool({ host: process.env.PGHOST, port: parseInt(process.env.PGPORT || '5432', 10), user: process.env.PGUSER, password: process.env.PGPASSWORD, database: process.env.PGDATABASE }); /** * Kysely over the same pool, alongside `pool` rather than instead of it. * * Both have to work at once: the conversion is file by file across 238 call * sites, so for a long time most queries will still be raw `pg` and the two * must share one set of connections. Handing Kysely the existing pool rather * than letting it open its own is what makes that true — otherwise a * transaction started on one would be invisible to the other, and the pool * limits would silently double. * * The value of this over raw `pg` is not brevity. In a Kysely `sql` template * `${value}` emits a bind parameter, never text, so there is no way to spell * "interpolate this as SQL" by accident. That makes the #202 invariant * structural instead of a comment plus two mutation tests, and it is the main * reason a builder is here at all. * * Kysely rather than Drizzle since #305. The safety property above was true of * both; what decided it is that three of the four hazards in the old * CONVENTIONS.md — an array needing sql.param(), a column reference silently * losing its table inside a raw fragment, and a camelCase mirror that had to be * mapped back at every select — were properties of Drizzle rather than of * type-safe query building. See #297 for the SQL each one actually emitted. */ export const db = new Kysely({ dialect: new PostgresDialect({ pool }) }); /** * The single row a query is guaranteed to have returned. * * For `INSERT ... RETURNING` and `UPDATE ... WHERE id = $1 RETURNING` after the * row's existence has already been established: Postgres returns exactly one * row, so there is nothing to branch on, but `noUncheckedIndexedAccess` is right * that `rows[0]` is `T | undefined` and the compiler cannot know better. * * A thrown error rather than a non-null assertion. If the assumption is ever * wrong the assertion would hand `undefined` to the next line and fail somewhere * unrelated, whereas this fails here and says which query. `asyncRoute` turns it * into a 500, which is the right answer for "the database did not do what the * statement says it does". * * Reads that legitimately might find nothing do not use this — they destructure * and branch, so the check and the use are the same thing. */ export function requireRow(rows: T[], what: string): T { const row = rows[0]; if (!row) { throw new Error(`expected ${what} to return a row, got none`); } return row; }