데이터데이터베이스
@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·date는Date,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 는 곁에 실어야 하는 파일입니다.