runlot

@runlot/pg

node-postgres 와 같은 모양의 Pool·Client 입니다. env.db 위에 얹혀 있고, 연결 문자열이 없습니다.

env.db 는 낮은 층입니다. 익숙한 pg 모양으로 쓰시려면 @runlot/pg 를 씁니다.

import { Pool } from "@runlot/pg";

export default {
  async fetch(req: Request, env: Env): Promise<Response> {
    const pool = new Pool({ db: env.db });
    const { rows } = await pool.query("select id, name from users where id = $1", [1]);
    return Response.json(rows);
  },
};

설정은 db 하나입니다

new Pool({ db: env.db });
new Client({ db: env.db });

호스트·포트·비밀번호가 없습니다. pg 호환을 위해 다른 키를 받긴 하지만 무시합니다. db 를 빠뜨리면 이렇게 알려 줍니다.

@runlot/pg: `db` 가 없어요 — new Pool({ db: env.db }) 로 만드세요.

무엇이 되나요

  • pool.query(text, values) — 호출마다 새 세션입니다. 문장 하나짜리 질의에 알맞습니다.
  • pool.connect() / new Client({ db }) — 세션 하나를 쥡니다. BEGIN … COMMIT, prepared statement, SET 이 그 안에서 유지됩니다. release()end() 로 돌려주세요.
  • 오류는 DatabaseError 로 던지고 err.code 가 SQLSTATE 입니다.
  • types.setTypeParser(oid, fn) 으로 타입 변환을 바꿀 수 있습니다. 기본은 pg 와 같습니다 — timestamptz · dateDate, int8 · numeric 은 문자열입니다.
import { Pool, DatabaseError } from "@runlot/pg";

const pool = new Pool({ db: env.db });
try {
  await pool.query("insert into users (email) values ($1)", [email]);
} catch (e) {
  if (e instanceof DatabaseError && e.code === "23505") {
    return new Response("이미 있는 이메일입니다", { status: 409 });
  }
  throw e;
}

트랜잭션

const client = await pool.connect();
try {
  await client.query("begin");
  await client.query("update accounts set balance = balance - $1 where id = $2", [100, 1]);
  await client.query("update accounts set balance = balance + $1 where id = $2", [100, 2]);
  await client.query("commit");
} catch (e) {
  await client.query("rollback");
  throw e;
} finally {
  client.release();
}

pool.query() 는 호출마다 새 세션이라 그 사이에 트랜잭션이 이어지지 않습니다. 트랜잭션에는 반드시 connect() 를 쓰세요.

풀이라고 부르지만 풀링하지 않습니다

이름은 Pool 이지만 연결을 재사용하지 않습니다. connect() 를 부를 때마다 새 엔진 세션이 열립니다. 이유는 세션 에 적었습니다.

nodejs_compat 없이도 뜹니다

Node 내장 모듈을 import 하지 않습니다. EventEmitter 대신 최소 emitter 를 씁니다. 그래서 이 패키지만 쓰는 워커는 호환 플래그가 필요 없습니다.

배포할 때 할 일

없습니다. runlot deploy 의 번들러가 pg 를 이 패키지로 풀고, ORM 이 Node 내장 모듈을 쓰면 nodejs_compat 을 자동으로 켜고, Prisma 의 WASM 쿼리 컴파일러는 별도 모듈로 매니페스트에 적어 줍니다.

직접 번들하시는 경우는 번들러를 그대로 쓰실 수 있습니다.

import { buildWorker } from "@runlot/pg/bundler";

const { code, report } = await buildWorker({ entry: "src/index.ts", absWorkingDir: process.cwd() });
// report.nodeImports 가 비어 있지 않으면 nodejs_compat,
// report.wasmModules 는 곁에 실어야 하는 파일입니다.

이 페이지에서