Docker + Prisma + React でTodoアプリを作ってみた!
👋 はじめに
こんにちは!
最近、社内で 「今後の新規開発はTypeScriptを積極的に使っていこう!」 という流れが本格化してきました。
しかし、これまで自分はTypeScriptを業務でしっかり触った経験がありませんでした。「JavaScriptに型がついたものでしょ?」くらいのぼんやりとした知識しかありませんでした。
そこで、まずはTypeScriptに慣れるための第一歩として、定番の「Todoアプリ」開発に挑戦してみることにしました。どうせ作るなら、それっぽく実践的な構成にしたいと思い、以下の技術スタックを選びました。
- 環境構築: Docker / PostgreSQL
- バックエンド (API): TypeScript / Node.js (Express) / Prisma
- フロントエンド (UI): React / TypeScript
この記事では、TypeScript未経験だった自分が、実際にTodoアプリをフルスタック(バックエンドとフロントエンドの両方)で作ってみて感じた、率直な 「型の恩恵」 や 「開発体験(DX)」 について、感想を中心にまとめていきます。
1. 🐳 データベースの起動 (Docker)
まずはデータの入れ物であるPostgreSQLをDockerで起動します。
docker-compose.yml を書くだけで、コマンド一つ(docker compose up -d)で自分のPCを汚さずにデータベース環境が立ち上がるのは、やはり非常に快適でした。開発の「よーいドン」がとてもスムーズです。
2. 🤖 バックエンドAPI実装 (TypeScript + Prisma)
ここからが本題のバックエンド開発です。TypeScriptとPrismaの組み合わせが、想像以上に強力でした。
Prismaスキーマと「自動生成される型」
Prismaはschema.prisma というファイルにDBモデルを定義します。
// prisma/schema.prisma (抜粋)
model Todo {
id Int @id @default(autoincrement())
title String
completed Boolean @default(false)
createdAt DateTime @default(now())
}
npx prisma migrate dev を実行すると、DBにテーブルが作られると同時に、このモデルに対応するTypeScriptの型が自動生成されます。
DBのスキーマ定義(schema.prisma)が、そのままTypeScriptの型定義(Todo型など)に なります。 Goで使っていたSQLBoilerを思い出しました。
「型」がAPI実装をガイドしてくれる
ExpressでAPIサーバー(src/index.ts)を実装していきます。 SQLBoilerが「型安全なSQLビルダー/クエリ実行」だとしたら、Prismaは 「TypeScriptコードとDB操作がよりシームレスに融合している」 的な感覚です。
// src/index.ts (雰囲気の抜粋)
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient(); // この時点でDB接続の準備OK
const app = express();
// [POST] /todos : 新しいTodoを作成
app.post('/todos', async (req, res) => {
// req.body の中身が何か、コードを追わなくても型からわかる
const { title } = req.body;
// prisma.todo.create の引数 (data) にも型がついている!
const todo = await prisma.todo.create({
data: {
title: title,
// completed: ... や createdAt: ... を書き忘れても、
// schema.prisma の @default が効くのがわかる
},
});
// レスポンス (todo) も、Prismaが生成した Todo 型だとわかる
res.json(todo);
});
findMany や create といったメソッドAPIが、よりJavaScriptのオブジェクト操作やメソッドチェーンに近い感覚で設計されています。エディタの補完も強力で、prisma.todo. と打つとメソッドが補完されるだけでなく、その引数オブジェクト(data: や where:、orderBy: など)の中身まで完全に型が効きます。
Go+SQLBoilerが「型安全にSQLを組み立てる」体験だとすれば、TypeScript+Prismaは 「型安全なJSオブジェクトを操作しているだけで、裏側でSQLが実行されている」 という、一段抽象化された体験でした。これはTypeScriptという言語の特性と非常にマッチしていると感じます。
3. 🎨 フロントエンドUI実装 (React + TypeScript)
次にReactでフロントエンドを作ります。バックエンドと同じくTypeScriptを採用しました。
フロントエンドのことは正直あまり詳しくなかったので、ここはAI(ChatGPTやGeminiなど)とペアプログラミングしながら進めました。
面白かったのは、AIが生成したコードに対しても、TypeScriptの型チェックが強力なレビューアとして機能したことです。
例えば、APIから取得するデータ(Todo[])の型を先に定義しておくと、AIが雑に any 型を使おうとしたり、存在しないプロパティ(todo.name など)にアクセスしようとしたりした瞬間に、エディタが「型が違う!」とエラーを出してくれます。
AIに実装を任せつつも、人間は「型」という設計図をレビューすることで、品質を担保できる感覚がありました。
TypeScriptフルスタックの真価(今回は試せなかったこと)
AIとペアプロする中で、TypeScriptでフルスタック開発を行う最大のメリットは、やはり**「型の共有」**にあるのだと痛感しました。(今回は別々のプロジェクトとして作成したため、この恩恵は試せませんでしたが…)
もし「モノレポ」(一つのリポジトリで管理する構成)などを採用していれば、以下のような大きな恩恵があるはずです。
-
型の共有: バックエンドのPrismaが生成した
Todo型を、フロントエンドでもそのままインポートして利用できます。これにより、APIのレスポンスとフロントが期待するデータ構造の間に「ズレ」がなくなることが保証されます。 -
API仕様の変更に強い: APIの仕様変更(例:
Todoにpriorityを追加)があった場合、共有された型定義を更新するだけで、フロント・バック両方でコンパイルエラーが検知できるため、修正漏れを防げます。 - 開発体験の一貫性: 同じ言語、同じ構文で開発できるため、フロントエンドとバックエンドを行き来する際の「脳のスイッチングコスト」が大幅に下がるようです。
🎉 まとめ: AIとペアプロして感じた『型』の価値
TypeScript未経験からでしたが、AIとも協力しつつ、一連の開発の流れを掴むことができました。
-
PrismaとTypeScriptの「直感的な」連携: Go+SQLBoilerで「スキーマからのコード生成」は経験済みでしたが、Prismaの体験は新鮮でした。まるでJSの配列を
filterするかのような直感的なAPIでDBを操作できる点は、SQLBoilerとはまた違った快適さがありました。 -
AI時代の「型」の重要性: フロント開発はAIとペアプロで進めましたが、そこで感じたのは「型の共有」の重要性です。モノレポで型を共有すれば、API仕様のズレをコンパイル時に検知できる安心感は、Go言語でバックエンドを書いていた時にはなかった大きな魅力です。
-
型は「AIのレビューア」になる: TypeScript自体の基本的な恩恵(エディタ補完)も強力ですが、それ以上に「AIが生成したコードの品質を担保する仕組み」として型が機能することを実感できました。
小さなTodoアプリでtypescriptでの開発の流れは体験できたかと思います!あざした!
💻 成果物

📕 参考
Discussion