展示 HN:Pg-typesafe – 为 PostgreSQL 和 TypeScript 提供强类型查询
Show HN: Pg-typesafe – Strongly typed queries for PostgreSQL and TypeScript

原始链接: https://github.com/n-e/pg-typesafe

## pg-typesafe: 为 PostgreSQL 查询提供类型安全 pg-typesafe 为你的 PostgreSQL 查询生成 TypeScript 类型,*无需*运行时依赖或增加冗余。它通过分析代码中的常量 SQL 查询来工作,为查询参数和结果提供强类型。 **安装:** 通过 npm 安装,然后运行 `npm exec pg-typesafe -- --connectionString <你的连接字符串>` 来生成一个包含类型的 `src/defs.gen.ts` 文件。将你的 `Pool` 转换为 `TypesafePool`(从 `defs.gen.ts` 导入)以启用类型检查。 **主要特性:** * **类型生成:** 自动基于你的数据库模式创建 TypeScript 类型。 * **BIGINT 支持:** 可以将 PostgreSQL BIGINT 转换为 JavaScript `bigint` 类型。 * **JSONB 类型:** 允许根据其模式自定义 JSONB 列的类型。 * **配置:** 可通过 `pg-typesafe.config.ts` 自定义连接字符串、定义文件路径和类型转换。 **限制:** pg-typesafe 仅对常量 SQL 查询进行类型化;动态查询不受支持。但是,使用常量查询建议用于安全性(防止 SQL 注入)和性能。 **替代方案:** pgtyped(更冗长,支持 .sql 文件)和 kysely(类型安全的查询构建器)提供类似的功能。

## Pg-typesafe:TypeScript 的类型安全 PostgreSQL 查询 一位开发者在 Hacker News 上介绍了 **pg-typesafe**,一个用于查询 PostgreSQL 数据库的新 TypeScript 库。由于在使用 TypeScript 的原始 SQL 时,手动类型定义和反序列化问题(特别是整数类型处理不一致的问题)而感到沮丧,该库的创建者构建了 pg-typesafe,旨在提供完全类型化的查询,*而无需*更改调用现有 `node-pg` 查询的方式。 目标是简单性——保持熟悉的查询风格,同时利用 TypeScript 强大的类型系统尽早捕获错误。在一个大型项目中的早期采用已经发现了错误并减少了手动类型定义的需求,证明了它的实际好处。该项目在 GitHub 上可用:[github.com/n-e](https://github.com/n-e)。
相关文章

原文

pg-typesafe generates TypeScript types for PostgreSQL queries.

pg-typesafe does so with no runtime dependencies, and zero additional verbosity.

Here is a query with pg-typesafe:

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

This query looks the same as a normal query with pg, but is fully typed:

  • The parameter is required, and must be a number
  • rows has the type { id:number; name:string; last_modified: Date }[]

Install pg-typesafe:

Run it for the first time:

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

This will generate the file src/defs.gen.ts that contains the pg-typesafe types.

Now that the types are generated you can cast your Pool to the pg-typesafe type:

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

export const pool = new Pool() as TypesafePool;

This will allow pg-typesafe to find you queries, and also type them properly.

You can now run pg-typesafe again to generate the types:

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

pg-typesafe can only type queries where the SQL query is a constant, as dynamic queries cannot be analyzed. You should however, when possible, do so, as it avoids SQL injections and is faster.

--connectionString (default: undefined)

The connection string to the postgresql database. pg-typesafe also honors the node-pg environment variables (PGHOST, PGDATABASE...)

--definitionsFile (default: src/defs.gen.ts)

The path to the type definitions

--tsConfigFile (default: tsconfig.json)

The tsconfig file for the project. pg-typesafe uses this file to find the files to analyze

--configFile (default: pg-typesafe.config.ts)

The pg-typesasfe configuration file

Basic pg-typesafe.config.ts:

import { defineConfig } from "pg-typesafe";

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

The full list of parameters is available as JSDoc.

Convert BIGINTs to JavaScript bigints

By default, pg returns BIGINTs as strings, as very large values cannot be represented exactly by the JavaScript number type.

In "newer" node.js versions, the bigint type is supported, let's use it.

On the pg side, do the conversion:

import { types } from "pg";
types.setTypeParser(20, (val) => BigInt(val));

On the pg-typesafe side, generate the right types:

export default defineConfig({
  // when a query accepts a BIGINT as a parameter, type it as bigint
  transformParameter(param) {
    if (param.type_oid === 20) {
      return { type: "bigint" };
    }
    return defaultTransformParameter(param);
  },
  // when a query returns a BIGINT, type it as bigint
  transformField(field) {
    if (field.type_oid === 20) {
      return { type: "bigint" };
    }
    return defaultTransformField(field);
  },
});

Type JSONB columns to the right type depending on the context

If your JSONB columns contain that data that conform to a schema, it can be nice to type them to the "right" type.

To do this, we will type the columns as table_name_column_name, then define these types in another file.

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);
  },
});

Then, if you have a table hello with a JSONB column data, you can create a jsonb_columns.ts file with the right type:

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

For the types to work properly, the pg-typesafe enhanced types must be used. This usually works automatically, even if you use pool.connect to acquire clients.

If you pass connections to functions, you may use the types such as TypesafePoolClient, TypesafeQuerier, or TypesafeQueryFn for example:

async function fetchFoos(client: TypesafeQuerier) {
  const { rows } = await client.query("select id, name from foo");
  return rows;
}
  • pgtyped: pgtyped also generates types for queries, and supports queries in both .ts and .sql files. For queries in .ts files, it is more verbose and exposes its own helpers, while pg-typesafe adds no verbosity on top of pg

  • kysely: a type-safe query builder that is close to SQL

联系我们 contact @ memedata.com