Show HN: Pg-typesafe – PostgreSQL 및 TypeScript용 강력한 타입 지정 쿼리

발행: (2026년 2월 18일 오전 03:15 GMT+9)
6 분 소요

Source: Hacker News

pg-typesafe

pg-typesafe는 PostgreSQL 쿼리를 위한 TypeScript 타입을 생성합니다.
런타임 의존성이 없으며 추가적인 코드 양전혀 늘어나지 않습니다.

const { rows } = client.query(
  "select id, name, last_modified from tbl where id = $1",
  [42],
);

쿼리는 일반 pg 쿼리와 동일하게 보이지만, 완전히 타입이 지정됩니다:

  • 파라미터는 필수이며 number여야 합니다.
  • rows의 타입은 { id: number; name: string; last_modified: Date }[] 입니다.

시작하기

pg-typesafe 설치:

npm i -D pg-typesafe

처음 실행:

npm exec pg-typesafe -- --connectionString postgres://postgres:example@localhost

이 명령은 src/defs.gen.ts 파일을 생성하며, 여기에는 pg‑typesafe 타입이 들어 있습니다.

Pool을 pg‑typesafe 타입으로 캐스팅:

import type { TypesafePool } from "./defs.gen.ts";

export const pool = new Pool() as TypesafePool;

이제 pg‑typesafe가 쿼리를 찾아 타입을 지정할 수 있습니다. 쿼리를 추가하거나 변경할 때마다 타입을 다시 생성하세요:

npm exec pg-typesafe -- --connectionString postgres://postgres:example@localhost

제한 사항

pg-typesafe는 SQL 문자열이 상수인 경우에만 쿼리 타입을 지정할 수 있습니다. 동적 쿼리는 분석할 수 없으므로 타입이 지정되지 않습니다. 상수 쿼리를 사용하면 SQL 인젝션을 방지하고 성능을 향상시킬 수 있습니다.

명령줄 옵션

--connectionString (기본값: undefined)

PostgreSQL 데이터베이스에 대한 연결 문자열입니다. pg-typesafe는 표준 node-pg 환경 변수(PGHOST, PGDATABASE 등)도 지원합니다.

--definitionsFile (기본값: src/defs.gen.ts)

생성된 타입 정의 파일의 경로입니다.

--tsConfigFile (기본값: tsconfig.json)

프로젝트의 tsconfig.json 파일 경로입니다. pg-typesafe는 이 파일을 사용해 분석할 소스 파일들을 찾습니다.

--configFile (기본값: pg-typesafe.config.ts)

pg‑typesafe 설정 파일의 경로입니다.

구성 파일

A basic pg-typesafe.config.ts:

import { defineConfig } from "pg-typesafe";

export default defineConfig({
  connectionString: "postgres://postgres:example@localhost",
});

전체 구성 매개변수 목록은 패키지의 JSDoc을 통해 문서화되어 있습니다.

레시피

BIGINT를 JavaScript bigint로 변환하기

기본적으로 pgBIGINT 값을 문자열로 반환합니다. 이는 JavaScript 숫자의 안전 정수 범위를 초과할 수 있기 때문입니다. 최신 Node.js 버전에서는 네이티브 bigint 타입을 지원합니다.

pg 측에서:

import { types } from "pg";

types.setTypeParser(20, (val) => BigInt(val));

pg‑typesafe 측에서: 파라미터와 결과 필드에 bigint를 사용하도록 변환기를 설정합니다.

import {
  defineConfig,
  defaultTransformParameter,
  defaultTransformField,
} from "pg-typesafe";

export default defineConfig({
  // 파라미터 변환
  transformParameter(param) {
    if (param.type_oid === 20) {
      return { type: "bigint" };
    }
    return defaultTransformParameter(param);
  },

  // 필드(결과 컬럼) 변환
  transformField(field) {
    if (field.type_oid === 20) {
      return { type: "bigint" };
    }
    return defaultTransformField(field);
  },
});

JSONB 컬럼을 적절한 TypeScript 타입으로 매핑하기

JSONB 컬럼이 알려진 스키마를 따르는 경우, 이를 특정 TypeScript 인터페이스에 매핑할 수 있습니다.

import { defineConfig, defaultTransformField } from "pg-typesafe";

export default defineConfig({
  transformField(field) {
    if (field.type_oid === 3802 && field.column) {
      const c = field.column;
      const typeName = `${c.table_name}_${c.column_name}`;
      return {
        type: typeName,
        imports: [{ name: typeName, path: "./jsonb_columns.ts" }],
      };
    }
    return defaultTransformField(field);
  },
});

jsonb_columns.ts 파일을 생성하고 해당 인터페이스들을 정의합니다. 예시:

export interface hello_data {
  foo: string;
  bar: number;
}

타입 전파

생성된 타입이 올바르게 적용되도록, 코드 전체에서 pg‑typesafe‑enhanced 타입을 사용하십시오. 이는 일반적으로 자동으로 작동하며, pool.connect를 통해 클라이언트를 획득할 때도 마찬가지입니다.

연결을 전달할 때는 제공된 타입으로 주석을 달 수 있습니다:

import type {
  TypesafePoolClient,
  TypesafeQuerier,
  TypesafeQueryFn,
} from "./defs.gen.ts";

async function fetchFoos(client: TypesafeQuerier) {
  const { rows } = await client.query("select id, name from foo");
  return rows;
}

대안

  • pgtyped.ts.sql 파일 모두에 대한 쿼리 타입을 생성합니다. .ts 파일의 경우 자체 헬퍼를 도입하고 더 자세하게 작성되지만, pg-typesafe는 추가 구문을 추가하지 않습니다.
  • kysely – SQL에 가깝지만 더 높은 추상화 수준에서 동작하는 타입‑안전 쿼리 빌더입니다.
0 조회
Back to Blog

관련 글

더 보기 »