TypeScript・Fastify・Prismaで簡単なAPI作成してみた


こんにちは。

FastifyとPrismaに関する記事があまりなかったので、書いてみました。

新しくキャッチアップする方は環境構築等々にご利用してみてください！

## 1.セットアップ
```bash
mkdir fastify-prisma-todo
cd fastify-prisma-todo
npm init -y
```

次に必要な依存関係をインストール
```bash
npm install fastify prisma @prisma/client fastify-plugin
```

次にtypescriptの設定を行う
```bash
npm install typescript ts-node @types/node --save-dev
```
```bash
npx tsc --init
```

tsconfig.jsonファイルが作成されるので以下のようにします
```json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "outDir": "./dist"
  },
  "include": ["src/**/*.ts"],
  "exclude": ["node_modules"]
}
```

## 2.Prismaの初期化
Prismaをセットアップして、データベースを扱えるようにします。

```bash
npx prisma init
```
これにより、prisma フォルダと prisma/schema.prisma ファイルが作成されます。
## 3.データベーススキーマの設定とデータベースのマイグレーション
prisma/schema.prismaファイルを開き、Todoモデルを定義

```js
datasource db {
  provider = "sqlite"
  url      = "file:./dev.db"
}

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

model Todo {
  id        Int      @id @default(autoincrement())
  title     String
  completed Boolean  @default(false)
  createdAt DateTime @default(now())
}
```
SQLiteを使う設定にしています

以下のコマンドでデータベースのマイグレーションを行い、データベースにテーブルを作成します
```bash
npx prisma migrate dev --name init  
```
## 4.Prismaクライアントの生成とFastifyの設定

srcディレクトリを作成し、index.tsファイルを作成してFastifyを設定する
```js
import Fastify, { FastifyRequest, FastifyReply } from 'fastify';
import { PrismaClient, Todo } from '@prisma/client';

const fastify = Fastify({ logger: true });
const prisma = new PrismaClient();

// 型定義
interface CreateTodoBody {
  title: string;
}

interface UpdateTodoBody {
  completed: boolean;
}

interface Params {
  id: string;
}

// エンドポイント
fastify.get('/', async (request: FastifyRequest, reply: FastifyReply) => {
  return { message: 'Hello from Fastify and Prisma!' };
});

fastify.get('/todos', async (request: FastifyRequest, reply: FastifyReply) => {
  const todos: Todo[] = await prisma.todo.findMany();
  return todos;
});

fastify.post('/todos', async (request: FastifyRequest<{ Body: CreateTodoBody }>, reply: FastifyReply) => {
  const { title } = request.body;
  const newTodo = await prisma.todo.create({
    data: { title },
  });
  return newTodo;
});

fastify.put('/todos/:id', async (request: FastifyRequest<{ Params: Params; Body: UpdateTodoBody }>, reply: FastifyReply) => {
  const { id } = request.params;
  const { completed } = request.body;
  const updatedTodo = await prisma.todo.update({
    where: { id: parseInt(id) },
    data: { completed },
  });
  return updatedTodo;
});

fastify.delete('/todos/:id', async (request: FastifyRequest<{ Params: Params }>, reply: FastifyReply) => {
  const { id } = request.params;
  await prisma.todo.delete({
    where: { id: parseInt(id) },
  });
  return { message: 'Todo deleted' };
});

const start = async () => {
  try {
    await fastify.listen({ port: 3000 });
    console.log('Server running at http://localhost:3000');
  } catch (err) {
    fastify.log.error(err);
    process.exit(1);
  }
};

start();
```

## 6.設定変更
package.jsonを開き、script部分を以下のように変更
```json
{
  "scripts": {
    "build": "tsc",
    "start": "ts-node src/index.ts",
    "dev": "ts-node-dev src/index.ts"
  },

```
## 7.サーバーの起動
ディレクトリ構成は以下のようになっていること

```
fastify-prisma-todo/
│
├── node_modules/        # npmモジュール
├── prisma/
│   ├── migrations/      # Prismaのマイグレーション履歴
│   └── schema.prisma    # Prismaのデータベーススキーマ
├── .env                 # データベース接続設定（必要に応じて）
├──src
|  ├──index.ts           # Fastifyサーバーのエントリーポイント
├── package.json         # プロジェクトの依存関係とスクリプト
└── package-lock.json    # npmパッケージのバージョン固定ファイル
```

以下のコマンドを実行しアプリケーションを起動する
```bash
npm run start
```

http://localhost:3000 を開くとサーバーが起動していることがわかります。

## 8.データベースの確認
以下のコマンドを開くとデータベースがhttp://localhost:5555 で作動し確認することができる。
Todosテーブルが存在していればOK
```bash
npx prisma studio
```

## 9.APIを叩いてみる
POST（作成）のAPIを叩き、データベースにデータを追加してみる
```bash
curl -X POST http://localhost:3000/todos -H "Content-Type: application/json" -d '{"title": "Test Todo"}'
```

その後、データが作成されているかどうかを以下のURLで確認してみる
http://localhost:3000/todos