TS環境でデータベースを使うなら

TS環境でのリレーショナルデータベース利用について、最近考えていることをまとめておきたい。
特に結論ではない。

参考

個人的TS環境でのデータベース利用

2023~2025年、Deno TypeScript 環境で、kyselyを使いデータべースを触ることを試していた。
その他、Railsもさわるので、ActiveRecord もTSではないが触る機会も多い。
kysely、悪くなかったがちょっとクエリが複雑になると構文自体の複雑さが出てしまい、使いたい意欲がだいぶ落ちてしまった。
マイグレーションとしては使い心地は良かったのだけども。

そんな中で、「データ志向プログラミング」に書いてあること

データ志向プログラミング ソフトウェアがもつ複雑さの軽減に向けて」に、載っているjsonスキーマを利用した方針が示されている。

それを自分なりに解釈し、落とし込むと以下のようになるはず。
ただし、jsonスキーマではなくzodを使っている。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import postgres from "postgres";
import * as z from "zod";

const sql = postgres({
host: "localhost",
port: Deno.env.get("POSTGRES_PORT")
? Number(Deno.env.get("POSTGRES_PORT"))
: 5432,
database: Deno.env.get("POSTGRES_DB") || "app_db",
username: Deno.env.get("POSTGRES_USER") || "app",
password: Deno.env.get("POSTGRES_PASSWORD") || "app_password",
transform: postgres.toCamel,
});

///
// テーブルの用意など
///

// 以下、今回の主題

const userSchema = z.object({
id: z.number(),
name: z.string(),
age: z.number(),
createdAt: z.date(),
});

type User = z.infer<typeof userSchema>;

function isUsers(
obj: unknown,
): obj is User[] {
const result = z.array(userSchema).safeParse(obj);

if(!result.success) {
console.error("Validation error:", result.error.message);
return false;
}
return true;
}

async function getUsers(): Promise<
User[]
> {
const result = await sql`SELECT * FROM users`;

if (!isUsers(result)) {
throw new Error("Unexpected result format");
}

return result;
}

console.log(await getUsers());

sql.end();

プログラムとデータを分離。
システム境界でデータ検証することで、システム内での再検証は不要とする、ということになる。
今回の実装は、これへの準拠を意識した。


本を読んだ時に、「あぁ、これくらいでいいんだな」と感じた時の感覚をちゃんと書いておきたかった。
簡単なサンプルでしかないが、書けてよかった。

では。