サーバレスDBのNeonからDBファーストでPrismaを使ってスキーマを作りzodを連携して型解決をしたlambda(Node.js)を作成したメモ

## 概要
[前回は](https://qiita.com/hibohiboo/items/827f0e99afd0597d6425)Neonに繋いでSQLを発行することを試した。
今回は、型を自動的に作成して使用することを試す。

大まかな手順

1. Prismaでスキーマファイルを作成
1. スキーマファイルにzodの連携情報を追記
1. zodファイルをgenerate
1. zodで型解決
1. lambdaから使用

[ソースコード](https://github.com/hibohiboo/kartagraph-monorepo/tree/662909d018ca81a70058a20d4eeb9739edf76284/prisma)

なお、この手順だとバンドルファイルが肥大化する。対応は[Prisma + zod を使用したLambdaのファイルサイズ肥大を抑えるために Lambda Layersを利用したメモ](https://qiita.com/hibohiboo/items/a024126d8b44362405c3)に追記。

## 準備

### ライブラリのインストール

```
npm i zod @prisma/client 
npm i -D prisma zod-prisma-types
```


### スキーマファイルの作成

```prisma:schema.prisma
generator zod {
  provider = "zod-prisma-types"
}

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}
```

### 環境変数ファイルの設定

※ `?sslmode=require&pgbouncer=true`のクエリが必要なことに注意 [*](https://www.prisma.io/docs/orm/overview/databases/neon)

```env:.env
DATABASE_URL=postgresql://___user___:___password___@hoge.us-west-2.aws.neon.tech/main?sslmode=require&pgbouncer=true
```

## DBファーストでスキーマの作成

```
npx prisma db pull
```

成功すると、`schema.prisma` ファイルが更新される。

## zodの定義ファイルの作成

```
 npx prisma generate
```

成功すると、`generate/zod/index.ts`ファイルが作成される。

```ts:generate/zod/index.ts
import { z } from 'zod';
import { Prisma } from '@prisma/client';

// 省略
export const scenario_listSchema = z.object({
  id: z.string(),
  title: z.string(),
  src: z.string(),
  summary: z.string(),
  detail: z.string(),
  s3_key: z.string(),
  created: z.coerce.date(),
  updated: z.coerce.date(),
})

// 省略

```

## 型解決

```ts
import { NeonQueryFunction, neon } from '@neondatabase/serverless';
import { scenario_listSchema } from '@/prisma/generate/zod/index.ts';
import { z } from 'zod';

type ScenarioListItem = z.infer<typeof scenario_listSchema>;

export const getScenarioList = async (): Promise<ScenarioListItem> => {
  const ret = await execQuery(
    `SELECT id
          , title
          , src
          , summary
          , detail
          , s3_key
          , created
          , updated
       FROM scenario_list
   ORDER BY updated desc
    `,
  );
  const retJson = ret.map((r) => scenario_listSchema.parse(r));
  return retJson;
};

let sql: NeonQueryFunction<false, false> | null = null;
function getClient () {
  if (sql) return sql;
  if (!process.env.DATABASE_URL) {
    throw new Error('environment variable:DATABASE_URL is not defined');
  }
  sql = neon(process.env.DATABASE_URL);
  return sql;
};

async function execQuery (query: string) {
  const client = getClient();
  const result = await client(query);
  return result;
};


```




## 参考

[Prisma Neon](https://www.prisma.io/docs/orm/overview/databases/neon)
[【NextJs14】NextJs14 と 便利なライブラリ【#２７ Neon Serverless Postgres】](https://zenn.dev/web_life_ch/articles/549289858bd26b)
[NestJSに最適のORMを考えてみる（Prisma VS TypeORM）](https://ap-ep.com/node-orm/)
[TypescriptのSQLクエリビルダーのkyselyが快適](https://qiita.com/mont_blanc/items/0564a946b0d6919ad3a0)
[TypeScript用クエリービルダー「Kysely」でトランザクション、UPSERT、JOINを使ってみた](https://qiita.com/kanedaq/items/2202b700533e9ca15f9e)
[schema](https://www.prisma.io/docs/orm/reference/prisma-schema-reference)
[zodを使ってJSON.parseしたオブジェクトをvalidationして型安全にしたい](https://qiita.com/YudaiTsukamoto/items/37ee62d3a7ff6e2d52f9)
[zod-prisma-types](https://github.com/chrishoermann/zod-prisma-types)
[Zodを活用する](https://zenn.dev/kouji0705/articles/6598a7d3d5504c)
[DBスキーマファーストな Prisma の型定義に対する zod の使い方 と Fragment colocation ライクな実装](https://qiita.com/ki-tamago/items/75237c9474c02c1e11ff)