プロジェクトを作成する
こんにちは。前回はGeminiに相談してざっくり言うと技術選定などを行いました。
前の記事→(https://qiita.com/no-bey/items/e23cb79901d119fa5aa7)
今回はGeminiから得られた手順に沿って進めていきたいと思います!
Next.jsプロジェクトの作成
最初にターミナルを開いてプロジェクトを作成したいディレクトリに移動し、以下のコマンドを実行します。
npx create-next-app@latest project --typescript --tailwind --eslint
projectというのが今回のプロジェクト名です。(好きな名前をつけましょう)
プロジェクト名の後ろにはオプションを設定してあります。
それぞれオプションで何を設定しているのかと言うと、
-
--typescript
: TypeScriptを使いたい時に設定します。(TypeScriptが何かわからない場合は検索すべし) -
--tailwind
: Tailwind CSS を使う設定です。簡単にスタイルを適用でき、デザインを整えるのに便利です。 -
--eslint
: ESLintを使う設定です。コードのチェックをしてくれます。
ちなみに設定し忘れてもこのコマンドで聞かれるのでtypescript使いますか?tailwind使いますか?eslintは?という風に聞かれるのでそこで答えれば使えるのでご心配なく。
完了したら、プロジェクトのフォルダに移動します。
cd project
Prismaのセットアップ
次に、データベースを操作するためのPrismaを導入します。
Prismaってなんやねん!
って思ってたことがあるので自分のためにも説明しておきます。
こういう時にAIに聞きましょう。
ORMとPrismaについて説明してください
Geminiからの全回答(長いです)
はい、承知いたしました。ORMとPrismaについて説明します。
ORMとPrismaは、どちらもアプリケーションとデータベース間のやり取りを簡単にするためのツールですが、
アプローチや特徴に違いがあります。
ORM (Object-Relational Mapping) とは?
ORMは「Object-Relational Mapping(オブジェクト関係マッピング)」の略で、
オブジェクト指向プログラミング言語(例: JavaScript, Python, Java)とリレーショナルデータベース
(例: MySQL, PostgreSQL)の間にある「考え方の違い」を吸収し、橋渡しをする技術やツールの総称です。
データベースの世界ではデータを「テーブルの行」として扱いますが、
プログラミングの世界では「オブジェクト」として扱います。
ORMを使うことで、開発者はSQLを直接書く代わりに、
使い慣れたプログラミング言語のコードで
データベースのデータをオブジェクトのように直感的に操作できるようになります。
主なメリット
生産性の向上: SQLを直接記述する必要がなく、
簡潔なコードでデータベース操作が可能なため、開発スピードが上がります。
データベースへの非依存: ORMがデータベースごとのSQL方言の違いを吸収してくれるため、
データベースの種類を変更しやすくなります。
セキュリティ: SQLインジェクションなどの脆弱性を防ぐ仕組みが組み込まれていることが多いです。
コードの可読性向上: データベース操作がオブジェクトの操作として表現されるため、
コードが読みやすく、メンテナンスしやすくなります。
主なデメリット
複雑なクエリのパフォーマンス: 複雑なデータ取得や特殊な処理を行おうとすると、
ORMが生成するSQLが非効率になり、パフォーマンスが低下することがあります。
学習コスト: ORM独自の概念や使い方を学ぶ必要があります。
SQLの知識: ORMに頼りすぎると、SQLの知識が身につきにくくなる可能性があります。内部で何が起きているかを理解しないと、問題解決が難しくなることがあります。
Prisma とは?
Prismaは、Node.jsとTypeScript環境向けに設計された、
次世代のオープンソースORMです。
従来のORMが抱えていたいくつかの課題を解決し、
特に「型安全性」と「優れた開発者体験」に重点を置いています。
Prismaの構成要素
Prismaは主に以下の3つのツールで構成されています。
Prisma Schema: schema.prisma という一つのファイルで
データベースのテーブル構造やリレーションを定義します。
このファイルがデータベースとアプリケーションコードの間の
「信頼できる情報源 (Single Source of Truth)」となります。
Prisma Client: Prisma Schemaを元に自動生成される、型安全なデータベースクライアントです。
このクライアントを通じて、補完機能の効いた直感的なAPIでデータベースを操作できます。
Prisma Migrate: Prisma Schemaの変更履歴を管理し、
データベースの構造を安全に変更(マイグレーション)するためのツールです。
Prismaの主な特徴
強力な型安全性: TypeScriptとの親和性が非常に高く、データベースのスキーマに基づいた型を自動生成します。
これにより、存在しないカラムにアクセスしようとしたり、間違ったデータ型を入れようとしたりすると、
コード実行前(コンパイル時)にエラーを発見できます。
スキーマ中心のアプローチ: schema.prisma ファイルでデータモデルを定義することが開発の出発点となります。
これにより、データベースの構造が明確になり、管理が容易になります。
直感的でパワフルなAPI: Prisma Clientが提供するAPIは、
「findUnique」「create」「update」など、
非常に分かりやすく設計されています。また、リレーションのあるデータを簡単に取得できます。
優れた開発者体験: データベースをGUIで直接編集できる
「Prisma Studio」など、開発をサポートする便利なツールが揃っています。
従来のORMとPrismaの違い
Prismaが「次世代ORM」と呼ばれるのは、従来のアプローチとは異なるいくつかの重要な点があるからです。
特徴 従来のORM (例: TypeORM, Sequelize)
型安全性
型定義は可能だが、実行時にエラーが起きる場合がある。完全な型安全性を保証するのは難しい。
アプローチ
Active RecordパターンやData Mapperパターンが主流。モデルクラスがデータベース操作の責務を持つ。
クエリ
複雑なクエリや細かいカスタマイズが得意な場合が多い。
開発体験
ライブラリ単体で提供されることが多い。
特徴 Prisma
型安全性
非常に強力。スキーマから完全に型安全なクライアントを生成し、コンパイル時にエラーを検出。
アプローチ
スキーマ中心。schema.prisma が唯一の情報源となり、そこからクライアントを生成する。
クエリ
直感的で簡潔なAPIを提供。一般的な用途では非常に書きやすい。
開発体験
スキーマ定義、マイグレーション、クライアント、GUIツールまで一貫したツールセットを提供。
簡単に言えば、従来のORMがSQLをオブジェクト指向の便利なメソッドで「ラップする」ことに主眼を置いている
のに対し、Prismaはスキーマ定義を基点として、
データベースとのやり取り全体を「型安全で快適にする」ことに焦点を当てています。
まとめ
ORMは、SQLを書かずにデータベースを操作するための便利な技術全般を指します。
Prismaは、特にTypeScript環境において、強力な型安全性と優れた開発体験を提供するモダンなORMです。
もしあなたがNode.js/TypeScriptで新しいプロジェクトを始めるなら、
Prismaは非常に有力な選択肢となるでしょう。
一方で、非常に複雑なSQLクエリを多用するシステムでは、
従来のORMやSQLを直接書くアプローチが適している場合もあります。
以下簡単にまとめると、
- ORMはSQLを書かずに、いつものプログラミング言語でデータベースを操作できるようにする翻訳ツール
- Prismaはデータベース操作を、バグが起きないよう安全かつ快適にしてくれる最新式のORM
説明が長くなりましたが本題に移りましょう。
Prismaをインストールし、初期設定ファイルを作成しましょう
# Prismaをインストール
npm install prisma --save-dev
# Prismaの初期設定ファイルを作成
npx prisma init --datasource-provider postgresql
このコマンドで、プロジェクト内に prisma というフォルダと .env というファイルが作成されます。
-
prisma/schema.prisma
: データベースの設計図を書くファイルです。 -
.env
: データベースの接続情報などを書く、大切な情報を隠しておくためのファイルです。
データベース(DB)の準備と接続設定
今回はSupabaseというサービスを使用してPostgreSQLデータベースを準備します。
私が行った手順としては、
- アカウント登録後、新しいプロジェクト作成。
- ヘッダーに存在する、Connect と書かれたボタンを押す。
- Direct ConnectionのURIをコピーする。
3.でコピーしたURIを、プロジェクトのルートにある.env
ファイルに貼り付けます。
[YOUR-PASSWORD]
の部分を、Supabaseで設定したデータベースのパスワードに置き換えます(私は[YOUR-PASSWORD]
←の括弧を消さずにパスワードを入力して他ので後でエラー出て困ったので気をつけてください笑)
# Environment variables declared in this file are automatically made available to Prisma.
# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema
# Prisma supports the native connection string format for PostgreSQL, MySQL, SQL Server, SQLite, MongoDB and CockroachDB.
# See the documentation for more detail: https://pris.ly/d/connection-strings
DATABASE_URL="ここにSupabaseからコピーしたURIを貼り付け"
データベースの設計図(スキーマ)を作成
DBは用意でき、接続もできる様になりました。
次にprisma/schema.prisma
ファイルを開いて大会(Tournament
)とチーム(Team
)の情報をDBに保存するための設計図を書いていきます。
DBにカラム(列)を作成するための設計図になります。
エクセルで表を作成した時の列の見出しみたいなものでこれがないと基本的にDBにデータを保存していくことができません。(NoSQLは例外)
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
// 大会モデル
model Tournament {
id String @id @default(cuid())
name String // 大会名
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
teams Team[] // この大会に所属するチーム一覧
}
// チームモデル
model Team {
id String @id @default(cuid())
name String // チーム名
strength Int // チームの強さ(1~5などで表現)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tournament Tournament @relation(fields: [tournamentId], references: [id])
tournamentId String
}
上記がGeminiが生成してくれた設計図です。
ただ、リーグ戦の勝ち点や得失点も計算したいという気持ちが出てきたのでこの設計図では十分ではありません。
そこでプロンプトを投げます。
schemaを作成しているところなのですが、勝ち点や得失点差はTeamに持たせなくても大丈夫なのでしょうか?
Geminiの回答
ご連絡ありがとうございます!そして、どちらも非常に重要なポイントです。素晴らしいご質問ですね!そこまで考えられているのはすごいです。一つずつお答えしますね。勝ち点や得失点差の扱いについて
鋭いご指摘です!おっしゃる通り、将来的に勝ち点や得失点差を管理するには、今のスキーマでは少し問題があります。
なぜか?
もしTeamモデルに直接「勝ち点」を持たせてしまうと、「どの大会での勝ち点なのか?」が分からなくなってしまいます。同じチームが複数の大会に参加した場合、データが混ざってしまいますよね。
どうすれば良いか?
そこで、「大会ごとのチーム成績」を保存するための、新しいモデル(設計図)を追加するのがおすすめです。Tournament(大会)とTeam(チーム)の間に、両者を繋ぐ「参加者名簿兼成績表」のようなものを作ります。ここではLeagueEntry(リーグ参加者)と名付けましょう。
このLeagueEntryに、勝ち点や得失点差など、その大会限定の成績情報を持たせるのです。
こちらが修正版のスキーマです。先ほどのものより少し複雑に見えますが、より本格的なアプリにするための重要なステップです!
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
// 大会モデル
model Tournament {
id String @id @default(cuid())
name String // 大会名
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// この大会の参加者リスト(成績情報も含む)
entries LeagueEntry[]
}
// チームモデル
model Team {
id String @id @default(cuid())
// チーム名は、どの大会でも共通のはずなので、重複しないようにします
name String @unique
strength Int // チームの強さ(1~5などで表現)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// このチームが参加した大会のリスト
entries LeagueEntry[]
}
// 大会ごとの参加チームと成績を管理する中間モデル
model LeagueEntry {
id String @id @default(cuid())
// 成績データ
points Int @default(0) // 勝ち点
wins Int @default(0) // 勝ち数
losses Int @default(0) // 負け数
draws Int @default(0) // 引き分け数
goalsFor Int @default(0) // 総得点
goalsAgainst Int @default(0) // 総失点
goalDifference Int @default(0) // 得失点差
// どの大会に参加しているか
tournament Tournament @relation(fields: [tournamentId], references: [id])
tournamentId String
// どのチームの情報か
team Team @relation(fields: [teamId], references: [id])
teamId String
// 同じ大会に同じチームが複数登録されるのを防ぐ設定
@@unique([tournamentId, teamId])
}
この設計にすることで、「A大会でのXチームの成績」と「B大会でのXチームの成績」をきちんと分けて管理できるようになります。
このスキーマで大丈夫だなと思ったら次に進みます。
データベースに設計図を反映
ここまでで、設計図が書けたのでデータベースに反映させます。以下のコマンドを実行します。
npx prisma migrate dev --name init
このコマンドが成功すると、Prismaがデータベースを操作するための専用コード(Prisma Client)も自動で生成してくれます。
以上で、プロジェクトの土台が完成したかなと思います!
次の記事では、Webアプリの見た目や機能を作成していきたいと思います〜