TypeScript の便利そうなライブラリを使って「つもり貯金」のWebアプリを作る

いろいろなキャッチアップが目的
使ってみる
- Auth0
- react-hook-form
- zod
- Next.js(App Router)
あたりをキャッチアップしていく

データベース周りについて。
普段は Rails と Ridgepole を使っているけど、今回はサーバサイドもTypeScript で。
ActiveRecord から離れるのやだなぁなど。
データベースについて悩んだけど、アプリをVercel にホスティングするので Vercel Postgres を使ってみる。
スキーマ定義は sqldef を使っていく

つもり貯金の目標金額をデータベースでは次のように定義した
create table objectives
(
id serial primary key,
name varchar(255) not null,
description text,
target_value integer not null,
user_id text,
archived_at timestamp,
created_at timestamp not null default now(),
updated_at timestamp not null default now()
);
一方でTypeScript で扱うため以下のように定義している。
const objectiveSchema = z.object({
id: z.number(),
name: z.string(),
description: z.string(),
targetValue: z.number(),
userId: z.string(),
archivedAt: z.date().optional(),
createdAt: z.date(),
updatedAt: z.date(),
});
この場合、 archived_at と archivedAt が一致しないため zod でパースできなくて不便
ORM も含めて勉強しようとするとちょっと荷が重いかなぁと思ったのでコンバータのような関数を入れていくことにする。
ちなみに ChatGPT に聞いたら最初に言われたのは ORM 入れろだった。嫌どす。

Zod での消耗がある……。
たとえば DB から取り出した値は先程の通り。type Objective = z.infer<typeof objectiveSchema>;
とできる。
一方これは DB -> API のレイヤで、 API -> React でまた変わってしまう。
たとえば createdAt が String 型になってしまう。そのためそのまま objectiveSchema.parse(data)
みたいなことはできないわけだ。
正しいけどそこまでちゃんとやらんでもなぁという気がしつつ。
DTOみたいなのがあれば良いんだろうと思いながらも一旦は schema.parse({ ...data, createdAt: ..., })
と手で直していく。

API層で認証したいので Auth0 の getSession
を使いながらやっていく。
ところで package.json
はこんな感じ。
TSは 5.2 で動いている。
"dependencies": {
"@auth0/nextjs-auth0": "^3.2.0",
"@vercel/postgres": "^0.5.1",
"bcrypt": "^5.1.1",
"next": "^13.5.6",
"react": "^18",
"react-dom": "^18",
"react-hook-form": "^7.48.2",
"result-type-ts": "^2.1.3",
"ts-pattern": "^5.0.5",
"zod": "^3.22.4"
},
"devDependencies": {
"@types/bcrypt": "^5.0.2",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"eslint": "^8",
"eslint-config-next": "14.0.1",
"prettier": "3.0.3",
"typescript": "^5"
}
このとき、どうやらauth0 のバージョンの問題か型が不整合で route.ts をやれなかったので一旦 route.js にした。
次のようなエラーが出ていた。
⨯ TypeError: Cannot read properties of undefined (reading 'set')
https://nextjs.org/docs/app/building-your-application/routing/route-handlers を読むと
TypeScript Warning: Response.json() is only valid from TypeScript 5.2. If you use a lower TypeScript version, you can use NextResponse.json() for typed responses instead.
とかあるので、まぁこの辺の話なのかなと思う。深掘りせずさっと .js
に逃げてきたらうまく動いた。
後この辺は middleware でなんとかする領域な気もする。

一通り作業をしてデプロイすると次のエラーが…。
Failed to compile.
src/app/layout.tsx
Type error: Module '"next/dist/lib/metadata/types/metadata-interface.js"' has no exported member 'ResolvingViewport'.
error: script "build" exited with code 1 (SIGHUP)
Error: Command "bun run build" exited with 1