LoginSignup
0
0

TypeScript用ORM「Drizzle ORM」をDBファーストで使う試み(PostgreSQL編)

Last updated at Posted at 2024-01-21

本記事で行うこと

TypeScript用のORM「Drizzle ORM」をデータベースファーストで使う試みのPostgreSQL編です。
WebフレームワークはHonoを使用します。

関連記事

テーブルを作る

では作業に移ります。
DBファーストですので、まずDDLを書きます。

CREATE TABLE foo
(
    col_serial                 SERIAL                   NOT NULL,
    col_bigserial              BIGSERIAL                NOT NULL,
    col_boolean                BOOLEAN                  NOT NULL,
    col_boolean_null           BOOLEAN,
    col_smallint               SMALLINT                 NOT NULL,
    col_smallint_null          SMALLINT,
    col_int                    INTEGER                  NOT NULL,
    col_int_null               INTEGER,
    col_bigint                 BIGINT                   NOT NULL,
    col_bigint_null            BIGINT,
    col_numeric                NUMERIC                  NOT NULL,
    col_numeric_null           NUMERIC,
    col_real                   REAL                     NOT NULL,
    col_real_null              REAL,
    col_double                 DOUBLE PRECISION         NOT NULL,
    col_double_null            DOUBLE PRECISION,
    col_text                   TEXT                     NOT NULL,
    col_text_null              TEXT,
    col_char                   CHAR(1)                  NOT NULL,
    col_char_null              CHAR(1),
    col_date                   DATE                     NOT NULL,
    col_date_null              DATE,
    col_time_with              TIME WITH TIME ZONE      NOT NULL,
    col_time_with_null         TIME WITH TIME ZONE,
    col_time                   TIME                     NOT NULL,
    col_time_null              TIME,
    col_timestamp_with         TIMESTAMP WITH TIME ZONE NOT NULL,
    col_timestamp_with_null    TIMESTAMP WITH TIME ZONE,
    col_timestamp              TIMESTAMP                NOT NULL,
    col_timestamp_null         TIMESTAMP,
    col_uuid                   UUID                     NOT NULL,
    col_uuid_null              UUID,
    col_json                   JSONB                    NOT NULL,
    col_json_null              JSONB,
    col_array                  INTEGER[]                NOT NULL,
    col_array_null             INTEGER[]
);

上記DDLの意図

列について、私がプロジェクトで使うかもしれない型を広めに試します。
JSON型と配列型も試します。
複合型も試しましたが、SELECT文ではBun同様にjsonを経由することで使えたものの、更新系コマンドが未サポートの様子だったので、本記事では省きました。

テスト用データをINSERTする

作成したfooテーブルに、以下のSQLでテスト用データを2件INSERTしました。

INSERT INTO foo
(
    col_boolean,
    col_boolean_null,
    col_smallint,
    col_smallint_null,
    col_int,
    col_int_null,
    col_bigint,
    col_bigint_null,
    col_numeric,
    col_numeric_null,
    col_real,
    col_real_null,
    col_double,
    col_double_null,
    col_text,
    col_text_null,
    col_char,
    col_char_null,
    col_date,
    col_date_null,
    col_time_with,
    col_time_with_null,
    col_time,
    col_time_null,
    col_timestamp_with,
    col_timestamp_with_null,
    col_timestamp,
    col_timestamp_null,
    col_uuid,
    col_uuid_null,
    col_json,
    col_json_null,
    col_array,
    col_array_null
)
VALUES
(
    TRUE,
    NULL,
    12345,
    NULL,
    123456789,
    NULL,
    1234567890123456789,
    NULL,
    3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117,
    NULL,
    3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117,
    NULL,
    3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117,
    NULL,
    'text_data',
    NULL,
    'c',
    NULL,
    '2023-01-01',
    NULL,
    '12:00:00',
    NULL,
    '12:00:00',
    NULL,
    '2023-12-31 23:59:59',
    NULL,
    '2023-12-31 23:59:59',
    NULL,
    GEN_RANDOM_UUID(),
    NULL,
    '{"a": 10, "b": "Json"}'::JSONB,
    NULL,
    ARRAY[10, NULL, 30]::INTEGER[],
    NULL
),
(
    FALSE,
    FALSE,
    32767,
    32767,
    987654321,
    987654321,
    876543210987654321,
    876543210987654321,
    3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117,
    3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117,
    3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117,
    3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117,
    3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117,
    3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117,
    'text_data',
    'text_data',
    'c',
    'c',
    '2023-01-01',
    '2023-01-01',
    '12:00:00',
    '12:00:00',
    '12:00:00',
    '12:00:00',
    '2023-12-31 23:59:59',
    '2023-12-31 23:59:59',
    '2023-12-31 23:59:59',
    '2023-12-31 23:59:59',
    GEN_RANDOM_UUID(),
    GEN_RANDOM_UUID(),
    '{"a": 20, "b": null}'::JSONB,
    '{"a": null, "b": "Json"}'::JSONB,
    ARRAY[10, NULL, 30]::INTEGER[],
    ARRAY[10, NULL, 30]::INTEGER[]
);

INSERTしたら、fooテーブルにSELECT文を発行してみます。

SELECT
    *
FROM
    foo;

SELECT文の結果です。

image.png

image.png

image.png

image.png

image.png

image.png

HonoとDrizzle ORMを使って、fooテーブルにCRUD操作するWeb APIサーバーを作ってみる

それでは、TypeScriptからDrizzle ORMを使っていきます。

プロジェクト作成

プロジェクト名は「exercise_drizzle_postgres」にしました。
どのテンプレートを使うか聞かれたので、「bun」を選択しました。
参考ページ: https://hono.dev/getting-started/basic

bunx create-hono exercise_drizzle_postgres

パッケージインストール

プロジェクトディレクトリ直下で、以下を実行しました。
参考ページ: https://orm.drizzle.team/docs/get-started-postgresql#supabase

bun add drizzle-orm postgres
bun add -D drizzle-kit

DBからTypeScriptのスキーマを自動生成

DBファーストなので、コードからDBスキーマを生成するのではなく、DBスキーマからコードを生成する順になりました。
プロジェクトディレクトリ直下で、以下のdrizzle-kitコマンドを実行しました。
connectionStringオプションには、PostgreSQLの接続URLを指定します。

drizzle-kit introspect:pg --driver=pg --connectionString=postgresql://postgres:secret@localhost:5432/postgres

ディレクトリ構造とファイル

drizzle/ディレクトリ配下のファイルは、drizzle-kitコマンドを実行したときに自動生成されたものです。

Project Root
  ├── drizzle/
  │     ├── meta/
  │     │     ├── 0000_snapshot.json
  │     │     └── journal.json
  │     ├── 0000_fixed_toxin.sql
  │     └── schema.ts
  ├── src/
  │     └── index.ts
  ├── package.json
  ├── README.md
  └── tsconfig.json

drizzle/schema.ts

自動生成されたコードです。
src/index.tsからimportして使用します。

drizzle/schema.ts
import { pgTable, serial, bigserial, boolean, smallint, integer, bigint, numeric, real, doublePrecision, text, char, date, time, timestamp, uuid, jsonb } from "drizzle-orm/pg-core"
  import { sql } from "drizzle-orm"



export const foo = pgTable("foo", {
	colSerial: serial("col_serial").notNull(),
	colBigserial: bigserial("col_bigserial", { mode: "bigint" }).notNull(),
	colBoolean: boolean("col_boolean").notNull(),
	colBooleanNull: boolean("col_boolean_null"),
	colSmallint: smallint("col_smallint").notNull(),
	colSmallintNull: smallint("col_smallint_null"),
	colInt: integer("col_int").notNull(),
	colIntNull: integer("col_int_null"),
	// You can use { mode: "bigint" } if numbers are exceeding js number limitations
	colBigint: bigint("col_bigint", { mode: "number" }).notNull(),
	// You can use { mode: "bigint" } if numbers are exceeding js number limitations
	colBigintNull: bigint("col_bigint_null", { mode: "number" }),
	colNumeric: numeric("col_numeric").notNull(),
	colNumericNull: numeric("col_numeric_null"),
	colReal: real("col_real").notNull(),
	colRealNull: real("col_real_null"),
	colDouble: doublePrecision("col_double").notNull(),
	colDoubleNull: doublePrecision("col_double_null"),
	colText: text("col_text").notNull(),
	colTextNull: text("col_text_null"),
	colChar: char("col_char", { length: 1 }).notNull(),
	colCharNull: char("col_char_null", { length: 1 }),
	colDate: date("col_date").notNull(),
	colDateNull: date("col_date_null"),
	colTimeWith: time("col_time_with", { withTimezone: true }).notNull(),
	colTimeWithNull: time("col_time_with_null", { withTimezone: true }),
	colTime: time("col_time").notNull(),
	colTimeNull: time("col_time_null"),
	colTimestampWith: timestamp("col_timestamp_with", { withTimezone: true, mode: 'string' }).notNull(),
	colTimestampWithNull: timestamp("col_timestamp_with_null", { withTimezone: true, mode: 'string' }),
	colTimestamp: timestamp("col_timestamp", { mode: 'string' }).notNull(),
	colTimestampNull: timestamp("col_timestamp_null", { mode: 'string' }),
	colUuid: uuid("col_uuid").notNull(),
	colUuidNull: uuid("col_uuid_null"),
	colJson: jsonb("col_json").notNull(),
	colJsonNull: jsonb("col_json_null"),
	colArray: integer("col_array").array().notNull(),
	colArrayNull: integer("col_array_null").array(),
});

src/index.ts

メインのコードです。

src/index.ts
import { Hono } from "hono"
import postgres from "postgres"
import { drizzle } from "drizzle-orm/postgres-js"
import { sql, eq } from "drizzle-orm"
import { foo } from "../drizzle/schema"
import {execute} from "drizzle-kit/orm-extenstions/d1-driver/wrangler-client";

const client = postgres("postgresql://postgres:secret@localhost:5432/postgres")
const db = drizzle(client, { logger: true })

const app = new Hono()
app.notFound((c) => c.json({ message: 'Not Found', ok: false }, 404))

app.get("/api/selectFoo", async (c) => {
    const result = await db.select(
    {
        colSerial: foo.colSerial,
        colBigserial: sql`CAST(col_bigserial AS TEXT)`,
        colBoolean: foo.colBoolean,
        colBooleanNull: foo.colBooleanNull,
        colSmallint: foo.colSmallint,
        colSmallintNull: foo.colSmallintNull,
        colInt: foo.colInt,
        colIntNull: foo.colIntNull,
        colBigint: sql`CAST(col_bigint AS TEXT)`,
        colBigintNull: sql`CAST(col_bigint_null AS TEXT)`,
        colNumeric: sql`CAST(col_numeric AS TEXT)`,
        colNumericNull: sql`CAST(col_numeric_null AS TEXT)`,
        colReal: foo.colReal,
        colRealNull: foo.colRealNull,
        colDouble: foo.colDouble,
        colDoubleNull: foo.colDoubleNull,
        colText: foo.colText,
        colTextNull: foo.colTextNull,
        colChar: foo.colChar,
        colCharNull: foo.colCharNull,
        colDate: sql`CAST(col_date AS TEXT)`,
        colDateNull: sql`CAST(col_date_null AS TEXT)`,
        colTimeWith: foo.colTimeWith,
        colTimeWithNull: foo.colTimeWithNull,
        colTime: foo.colTime,
        colTimeNull: foo.colTimeNull,
        colTimestampWith: foo.colTimestampWith,
        colTimestampWithNull: foo.colTimestampWithNull,
        colTimestamp: sql`CAST(col_timestamp AS TEXT)`,
        colTimestampNull: sql`CAST(col_timestamp_null AS TEXT)`,
        colUuid: foo.colUuid,
        colUuidNull: foo.colUuidNull,
        colJson: foo.colJson,
        colJsonNull: foo.colJsonNull,
        colArray: foo.colArray,
        colArrayNull: foo.colArrayNull
    }
    ).from(foo)
    .execute()

    return c.json(result)
})

app.put("/api/updateFoo", async (c) => {
    await db.update(foo)
        .set({
            colTextNull: "ABC",
            colArray: sql`ARRAY[111, NULL, 333]::INTEGER[]`,
            colArrayNull: [444, 555, 666],
            colJson: sql`${{a: 777, b: null, c: "Hello"}}::JSONB`,
            colJsonNull: sql`'{}'::JSONB`,
        })
        .where(eq(foo.colSerial, 1))

    return c.json({ message: "ok", ok: true })
})

app.post("/api/insertFoo", async (c) => {
    await db.insert(foo)
        .values({
            colBoolean: false,
            colBooleanNull: null,
            colSmallint: 9999,
            colSmallintNull: null,
            colInt: 999999999,
            colIntNull: null,
            colBigint: 999999999999999999n,
            colBigintNull: null,
            colNumeric: sql`${"2.718281828459045235360287471352662497757247093699959574966967627724076630353547594571382178"}::NUMERIC`,
            colNumericNull: null,
            colReal: 2.71828182845904523536028747135266,
            colRealNull: null,
            colDouble: 2.71828182845904523536028747135266,
            colDoubleNull: null,
            colText: "Hello",
            colTextNull: null,
            colChar: "z",
            colCharNull: null,
            colDate: "2024-01-21",
            colDateNull: null,
            colTimeWith: "13:00",
            colTimeWithNull: null,
            colTime: "13:00",
            colTimeNull: null,
            colTimestampWith: "2024-01-21 13:00:00.000000",
            colTimestampWithNull: null,
            colTimestamp: "2024-01-21 13:00:00.000000",
            colTimestampNull: null,
            colUuid: sql`GEN_RANDOM_UUID()`,
            colUuidNull: null,
            colJson: sql`${{a: 777, b: null, c: "Hello"}}::JSONB`,
            colJsonNull: sql`${{}}::JSONB`,
            colArray: sql`ARRAY[111, NULL, 333]::INTEGER[]`,
            colArrayNull: [444, 555, 666]
        })

    return c.json({ message: "ok", ok: true })
})

app.delete("/api/deleteFoo", async (c) => {
    const subQuery = db.select(
        {
            _: sql`max(${foo.colSerial})`
        }
        ).from(foo)
    await db.delete(foo)
        .where(eq(foo.colSerial, subQuery))

    return c.json({ message: "ok", ok: true })
})

export default app

package.json

package.json
{
  "scripts": {
    "dev": "bun run --hot src/index.ts"
  },
  "dependencies": {
    "drizzle-orm": "^0.29.3",
    "hono": "^3.12.6",
    "postgres": "^3.4.3"
  },
  "devDependencies": {
    "@types/bun": "^1.0.0",
    "drizzle-kit": "^0.20.13"
  }
}

tsconfig.json

tsconfig.json
{
  "compilerOptions": {
    "esModuleInterop": true,
    "strict": true,
    "jsx": "react-jsx",
    "jsxImportSource": "hono/jsx"
  }
}

CRUD操作

それでは、CRUD操作について一つ一つ見ていきます。

SELECT文

まずはSELECT文です。

SELECT用ハンドラの中身

ハンドラの中身を見ていきます。
SQLが透けて見えますね。
私みたいにSQLを書きたい人には、嬉しいORMだと思いました。

「sql``」で囲った部分は、Drizzle ORMがSQLを生成するときにそのまま使われるので、DB側の構文やファンクションを使用できます。

  • DBでBIGSERIAL型、BIGINT型、NUMERIC型で定義された列は、TypeScriptのnumber型で受け取ると誤差が生じることがあるので、TEXT型にキャストしました。
  • DBでタイムゾーンなしの日付時間型で定義された列は、TypeScriptで受け取ったらタイムゾーン付きになる現象が起きたので、TEXT型にキャストしてそれを防ぎました。
    const result = await db.select(
    {
        colSerial: foo.colSerial,
        colBigserial: sql`CAST(col_bigserial AS TEXT)`,
        colBoolean: foo.colBoolean,
        colBooleanNull: foo.colBooleanNull,
        colSmallint: foo.colSmallint,
        colSmallintNull: foo.colSmallintNull,
        colInt: foo.colInt,
        colIntNull: foo.colIntNull,
        colBigint: sql`CAST(col_bigint AS TEXT)`,
        colBigintNull: sql`CAST(col_bigint_null AS TEXT)`,
        colNumeric: sql`CAST(col_numeric AS TEXT)`,
        colNumericNull: sql`CAST(col_numeric_null AS TEXT)`,
        colReal: foo.colReal,
        colRealNull: foo.colRealNull,
        colDouble: foo.colDouble,
        colDoubleNull: foo.colDoubleNull,
        colText: foo.colText,
        colTextNull: foo.colTextNull,
        colChar: foo.colChar,
        colCharNull: foo.colCharNull,
        colDate: sql`CAST(col_date AS TEXT)`,
        colDateNull: sql`CAST(col_date_null AS TEXT)`,
        colTimeWith: foo.colTimeWith,
        colTimeWithNull: foo.colTimeWithNull,
        colTime: foo.colTime,
        colTimeNull: foo.colTimeNull,
        colTimestampWith: foo.colTimestampWith,
        colTimestampWithNull: foo.colTimestampWithNull,
        colTimestamp: sql`CAST(col_timestamp AS TEXT)`,
        colTimestampNull: sql`CAST(col_timestamp_null AS TEXT)`,
        colUuid: foo.colUuid,
        colUuidNull: foo.colUuidNull,
        colJson: foo.colJson,
        colJsonNull: foo.colJsonNull,
        colArray: foo.colArray,
        colArrayNull: foo.colArrayNull
    }
    ).from(foo)
    .execute()

    return c.json(result)

Drizzle ORMが生成したSQL

上記コードからDrizzle ORMが生成したSQLを、整形して見やすくしました。

select
    "col_serial",
    CAST(col_bigserial AS TEXT),
    "col_boolean",
    "col_boolean_null",
    "col_smallint",
    "col_smallint_null",
    "col_int",
    "col_int_null",
    CAST(col_bigint AS TEXT),
    CAST(col_bigint_null AS TEXT),
    CAST(col_numeric AS TEXT),
    CAST(col_numeric_null AS TEXT),
    "col_real",
    "col_real_null",
    "col_double",
    "col_double_null",
    "col_text",
    "col_text_null",
    "col_char",
    "col_char_null",
    CAST(col_date AS TEXT),
    CAST(col_date_null AS TEXT),
    "col_time_with",
    "col_time_with_null",
    "col_time",
    "col_time_null",
    "col_timestamp_with",
    "col_timestamp_with_null",
    CAST(col_timestamp AS TEXT),
    CAST(col_timestamp_null AS TEXT),
    "col_uuid",
    "col_uuid_null",
    "col_json",
    "col_json_null",
    "col_array",
    "col_array_null"
from
    "foo"

selectFoo APIの実行結果

selectFoo APIを呼び出した結果です。
レスポンスは、ハンドラの最後の一行で、結果表をそのままjsonにして返しているだけですが、きれいなレスポンスが生成できていますね。
シンプルなコードできれいなレスポンスを返せているのはポイントが高いです。

[
    {
        "colSerial": 1,
        "colBigserial": "1",
        "colBoolean": true,
        "colBooleanNull": null,
        "colSmallint": 12345,
        "colSmallintNull": null,
        "colInt": 123456789,
        "colIntNull": null,
        "colBigint": "1234567890123456789",
        "colBigintNull": null,
        "colNumeric": "3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117",
        "colNumericNull": null,
        "colReal": 3.1415927,
        "colRealNull": null,
        "colDouble": 3.141592653589793,
        "colDoubleNull": null,
        "colText": "text_data",
        "colTextNull": null,
        "colChar": "c",
        "colCharNull": null,
        "colDate": "2023-01-01",
        "colDateNull": null,
        "colTimeWith": "12:00:00+00",
        "colTimeWithNull": null,
        "colTime": "12:00:00",
        "colTimeNull": null,
        "colTimestampWith": "2023-12-31T23:59:59.000Z",
        "colTimestampWithNull": null,
        "colTimestamp": "2023-12-31 23:59:59",
        "colTimestampNull": null,
        "colUuid": "aa4c082e-b82c-4b8f-a7a6-411a92d3c75a",
        "colUuidNull": null,
        "colJson": {
            "a": 10,
            "b": "Json"
        },
        "colJsonNull": null,
        "colArray": [
            10,
            null,
            30
        ],
        "colArrayNull": null
    },
    {
        "colSerial": 2,
        "colBigserial": "2",
        "colBoolean": false,
        "colBooleanNull": false,
        "colSmallint": 32767,
        "colSmallintNull": 32767,
        "colInt": 987654321,
        "colIntNull": 987654321,
        "colBigint": "876543210987654321",
        "colBigintNull": "876543210987654321",
        "colNumeric": "3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117",
        "colNumericNull": "3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117",
        "colReal": 3.1415927,
        "colRealNull": 3.1415927,
        "colDouble": 3.141592653589793,
        "colDoubleNull": 3.141592653589793,
        "colText": "text_data",
        "colTextNull": "text_data",
        "colChar": "c",
        "colCharNull": "c",
        "colDate": "2023-01-01",
        "colDateNull": "2023-01-01",
        "colTimeWith": "12:00:00+00",
        "colTimeWithNull": "12:00:00+00",
        "colTime": "12:00:00",
        "colTimeNull": "12:00:00",
        "colTimestampWith": "2023-12-31T23:59:59.000Z",
        "colTimestampWithNull": "2023-12-31T23:59:59.000Z",
        "colTimestamp": "2023-12-31 23:59:59",
        "colTimestampNull": "2023-12-31 23:59:59",
        "colUuid": "8a4fa849-1c61-440f-bc6e-7c780f3afda6",
        "colUuidNull": "f014beb1-3bb3-4dbd-956c-6126d41f3692",
        "colJson": {
            "a": 20,
            "b": null
        },
        "colJsonNull": {
            "a": null,
            "b": "Json"
        },
        "colArray": [
            10,
            null,
            30
        ],
        "colArrayNull": [
            10,
            null,
            30
        ]
    }
]

image.png

UPDATE文

次はUPDATE文を見ていきます。

UPDATE用ハンドラの中身

ハンドラのクエリー部分を見ます。
col_serial列が1の行について、TEXT型、配列型、JSON型の列をそれぞれ更新する内容です。

    await db.update(foo)
        .set({
            colTextNull: "ABC",
            colArray: sql`ARRAY[111, NULL, 333]::INTEGER[]`,
            colArrayNull: [444, 555, 666],
            colJson: sql`${{a: 777, b: null, c: "Hello"}}::JSONB`,
            colJsonNull: sql`'{}'::JSONB`,
        })
        .where(eq(foo.colSerial, 1))

Drizzle ORMが生成したSQL

上記コードからDrizzle ORMが生成したSQLを、整形して見やすくしました。

update
    "foo"
set
    "col_text_null" = $1,
    "col_array" = ARRAY[111, NULL, 333]::INTEGER[],
    "col_array_null" = $2,
    "col_json" = $3::JSONB,
    "col_json_null" = '{}'::JSONB
where
    "foo"."col_serial" = $4

-- params: [
    "ABC",
    "{444,555,666}",
    {"a":777,"b":null,"c":"Hello"},
    1
]

updateFoo APIの実行結果

updateFoo APIを呼び出した結果を見てみます。

SELECT
    col_text_null,
    col_array,
    col_array_null,
    col_json,
    col_json_null
FROM
    foo
WHERE
    col_serial = 1;

5つの列が意図した値に更新されました。

image.png

image.png

INSERT文

次はINSERT文を見ていきます。
1行挿入する内容です。

INSERT用ハンドラの中身

ハンドラのクエリー部分を見ます。

    await db.insert(foo)
        .values({
            colBoolean: false,
            colBooleanNull: null,
            colSmallint: 9999,
            colSmallintNull: null,
            colInt: 999999999,
            colIntNull: null,
            colBigint: 999999999999999999n,
            colBigintNull: null,
            colNumeric: sql`${"2.718281828459045235360287471352662497757247093699959574966967627724076630353547594571382178"}::NUMERIC`,
            colNumericNull: null,
            colReal: 2.71828182845904523536028747135266,
            colRealNull: null,
            colDouble: 2.71828182845904523536028747135266,
            colDoubleNull: null,
            colText: "Hello",
            colTextNull: null,
            colChar: "z",
            colCharNull: null,
            colDate: "2024-01-21",
            colDateNull: null,
            colTimeWith: "13:00",
            colTimeWithNull: null,
            colTime: "13:00",
            colTimeNull: null,
            colTimestampWith: "2024-01-21 13:00:00.000000",
            colTimestampWithNull: null,
            colTimestamp: "2024-01-21 13:00:00.000000",
            colTimestampNull: null,
            colUuid: sql`GEN_RANDOM_UUID()`,
            colUuidNull: null,
            colJson: sql`${{a: 777, b: null, c: "Hello"}}::JSONB`,
            colJsonNull: sql`${{}}::JSONB`,
            colArray: sql`ARRAY[111, NULL, 333]::INTEGER[]`,
            colArrayNull: [444, 555, 666]
        })

Drizzle ORMが生成したSQL

上記コードからDrizzle ORMが生成したSQLを、整形して見やすくしました。

insert into "foo" (
    "col_serial",
    "col_bigserial",
    "col_boolean",
    "col_boolean_null",
    "col_smallint",
    "col_smallint_null",
    "col_int",
    "col_int_null",
    "col_bigint",
    "col_bigint_null",
    "col_numeric",
    "col_numeric_null",
    "col_real",
    "col_real_null",
    "col_double",
    "col_double_null",
    "col_text",
    "col_text_null",
    "col_char",
    "col_char_null",
    "col_date",
    "col_date_null",
    "col_time_with",
    "col_time_with_null",
    "col_time",
    "col_time_null",
    "col_timestamp_with",
    "col_timestamp_with_null",
    "col_timestamp",
    "col_timestamp_null",
    "col_uuid",
    "col_uuid_null",
    "col_json",
    "col_json_null",
    "col_array",
    "col_array_null"
)
values (
    default,
    default,
    $1,
    $2,
    $3,
    $4,
    $5,
    $6,
    $7,
    $8,
    $9::NUMERIC,
    $10,
    $11,
    $12,
    $13,
    $14,
    $15,
    $16,
    $17,
    $18,
    $19,
    $20,
    $21,
    $22,
    $23,
    $24,
    $25,
    $26,
    $27,
    $28,
    GEN_RANDOM_UUID(),
    $29,
    $30::JSONB,
    $31::JSONB,
    ARRAY[111, NULL, 333]::INTEGER[],
    $32
)

-- params: [
    false,
    null,
    9999,
    null,
    999999999,
    null,
    999999999999999999,
    null,
    "2.718281828459045235360287471352662497757247093699959574966967627724076630353547594571382178",
    null,
    2.718281828459045,
    null,
    2.718281828459045,
    null,
    "Hello",
    null,
    "z",
    null,
    "2024-01-21",
    null,
    "13:00",
    null,
    "13:00",
    null,
    "2024-01-21 13:00:00.000000",
    null,
    "2024-01-21 13:00:00.000000",
    null,
    null,
    {"a":777,"b":null,"c":"Hello"},
    {},
    "{444,555,666}"
]

insertFoo APIの実行結果

insertFoo APIを呼び出した結果です。
3行目に、意図した行がINSERTされたことを確認できました。

image.png

image.png

DELETE文

次はDELETE文を見ていきます。

DELETE用ハンドラの中身

ハンドラのクエリー部分を見ます。
col_serial列==最大値の行を削除する内容です。

    const subQuery = db.select(
        {
            _: sql`max(${foo.colSerial})`
        }
        ).from(foo)
    await db.delete(foo)
        .where(eq(foo.colSerial, subQuery))

Drizzle ORMが生成したSQL

上記コードからDrizzle ORMが生成したSQLを、整形して見やすくしました。

delete from
    "foo"
where
    "foo"."col_serial" = (
        select
            max("col_serial")
        from
            "foo"
    )

deleteFoo APIの実行結果

deleteFoo APIを呼び出した結果です。
意図した行(3行目)が正常に削除されました。

image.png

image.png

まとめ

Drizzle ORMをDBファーストで使ってみたところ、PostgreSQLでも問題なく動いてくれました。
ただし本記事執筆時点では、PostgreSQLの複合型(composite type)は、Drizzle ORMでサポートされていないようでした。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0