[Part1] TypeScript, PostgreSQL, Next.js, Prisma & GraphQLでApp作成
この記事は、Next.js, GraphQL, TypeScript, Prisma, PostgreSQLを使ってフルスタックアプリを構築する講座のPart1です。この記事では、データモデルを作成し、Prismaのさまざまなコンポーネントを探ります。
はじめに
このコースでは、ユーザーがキュレーションされたリンクのリストを閲覧し、お気に入りのリンクをブックマークできるフルスタックアプリ「awesome-links」を構築する方法を学びます。
アプリは以下の技術で構築されています。
- Next.js:Reactフレームワーク
- Apollo Server:GraphQLサーバー
- Nexus:GraphQLスキーマを構築
- Apollo Client:GraphQLクライアント
- Prisma :マイグレーションとデータベースアクセスのためのORMである
- PostgreSQL:データベース
- AWS S3:画像アップロード用
- Auth0:認証
- TypeScript:プログラミング言語
- TailwindCSS:ユーティリティ優先のCSSフレームワーク
- Vercel:デプロイ
コースで扱う内容
- Prismaを用いたデータモデリング
- Apollo ServerとNexusを使ったNext.js APIルートのGraphQL APIレイヤーの構築
- Apollo Clientを使用したGraphQLのページネーション
- Auth0を用いた認証
- 認証
- AWS S3による画像アップロード
- Vercelへのデプロイメント
今日学べること
本講座のPart1では、まずアプリの要件定義と、Prismaを使ったデータベース層の設定を行います。
前提条件
想定される知識
このコースは、以下を前提としています。
-
Node.jsの基本的な知識
-
リレーショナルデータベースの基本的な理解 このコースに参加する前にデータベースについてもっと学びたい方は、Prismaのデータガイドでデータベースの仕組み、正しいデータベースの選び方、アプリケーションでデータベースを最大限に活用する方法について詳しく学んでください。
-
Next.jsを使用するため、Reactの知識があることを強く推奨します。
このコースでは以下は、必要ありません。
- TypeScriptの知識。(ただし、すでにJavaScriptの経験があることを前提とします)
- Prismaの知識は、本講座で説明します。
最後に、このコースではデータベースとしてPostgreSQLを使用しますが、ほとんどのコンセプトはMySQLなど他のリレーショナルデータベースにも適用できます。
この記事の終わりには、データベースに接続されたNext.jsアプリが完成していることでしょう。
開発環境
このコースに従うには、あなたのマシンにNode.jsがインストールされている必要があります。また、PostgreSQLのインスタンスが動作している必要があります。
リポジトリのクローン
このコースの完全なソースコードは、GitHubで見ることができます。
git clone -b part-1 https://github.com/m-abdelwahab/awesome-links.git
クローンしたディレクトリに移動して、依存関係をインストールし、開発サーバーを起動することができます。
cd awesome-links
npm install
npm run dev
スタータープロジェクトはこんな感じです。
プロジェクトの構成と依存関係を見る
awesome-links/
┣ components/
┃ ┗ Layout/
┣ data/
┃ ┗ links.ts
┣ pages/
┃ ┣ _app.tsx
┃ ┣ about.tsx
┃ ┗ index.tsx
┣ public/
┣ styles/
┃ ┗ tailwind.css
┣ .gitignore
┣ README.md
┣ next-env.d.ts
┣ next.config.js
┣ package-lock.json
┣ package.json
┣ postcss.config.js
┣ tailwind.config.js
┗ tsconfig.json
このスタータープロジェクトは、TypeScriptとTailwindCSSをインストールしたNext.jsのアプリです。
Next.jsはフルスタックのReactフレームワークで、さまざまなデータ取得ストラテジーをサポートしています。ひとつはサーバーサイドレンダリングで、リクエストごとにデータをフェッチします。また、ビルド時にデータをフェッチして、CDNで提供できる静的なウェブサイトを用意することもできます。このアプリでは、サーバーサイドでデータを取得することになります。
Next.jsではファイルベースのルーティングを使用しており、pagesディレクトリ内の各ファイルがルートとなります。現在、http://localhost:3000 にインデックスページがあり、http://localhost:3000/about にアバウトページがあります。
_app.tsxファイルは、デフォルトのAppの動作をオーバーライドするために使用されます。このファイルでは、ページ変更時のレイアウトを保持したり、グローバルCSSを追加したりすることができます。
// pages/_app.tsx
import '../styles/tailwind.css' // import Tailwind globally
import { Layout } from '../components/Layout' // header layout persists between page changes
function MyApp({ Component, pageProps }) {
return (
<Layout>
<Component {...pageProps} />
</Layout>
)
}
export default MyApp
http://localhost:3000 にナビゲートするときに見るデータは、/data/links.ts ファイルにハードコードされています。今後のパートでは、データはGraphQL APIを使用してデータベースから動的に取得される予定です。
アプリのデータモデルを作成する
データベースは以下のエンティティを持ち、各エンティティはデータベースのテーブルにマッピングされます。
-
User: アカウントを持つ人。お気に入りのリンクをブックマークすることができ、管理者にも一般ユーザーにもなることができます。
-
Link: タイトル、説明、URLなど、リンクのさまざまな属性を表現する。
-
UserとLinkのエンティティの間には、多対多(m-n)の関係があります。このようにして、ユーザーは多くのリンクを持つことができ、リンクは多くのユーザーを持つことができます。
プロジェクトにPrismaを追加する
データベースのテーブルを作成するためにPrismaを使用することにします。これは、データベースと対話するために使用できるORMです。
はじめに、PrismaのCLIを開発依存でインストールします。
npm install prisma -D
これで、Prisma CLIを使用して、Prismaの基本的な設定を実行することで作成できます。
npx prisma init
新しい/prismaディレクトリが作成され、その中にschema.prismaファイルが作成されます。これはメインのPrisma設定ファイルで、データベーススキーマを格納します。.env (dotenv) ファイルもプロジェクトのルートに追加されます。ここで、データベース接続URLやアクセストークンなどの環境変数を定義します。
.envファイルを開き、ダミーの接続URLをPostgreSQLデータベースの接続URLで置き換えます。例えば、データベースがHerokuでホストされている場合、URLは以下のようになります。
// .env
DATABASE_URL="postgresql://giwuzwpdnrgtzv:d003c6a604bb400ea955c3abd8c16cc98f2d909283c322ebd8e9164b33ccdb75@ec2-54-170-123-247.eu-west-1.compute.amazonaws.com:5432/d6ajekcigbuca9"
先ほど追加したデータベースURLは、以下のような構造になっています。
NAME | PLACEHOLDER | DESCRIPTION |
---|---|---|
Host | HOST | IP address/domain of your database server, e.g. localhost |
Port | PORT | Port on which your database server is running, e.g. 5432 |
User | USER | Name of your database user, e.g. janedoe |
Password | PASSWORD | Password for your database user |
Database | DATABASE | Name of the database you want to use, e.g. mydb |
Prismaでデータベーススキーマを作成する
/prisma/schema.prisma ファイルを開くと、次のようなスキーマが表示されます。
// prisma/schema.prisma
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
datasourceフィールドでは、PostgreSQLを使用していることと、.envファイルからデータベースのURLを読み込むことを指定しました。
次に、generatorブロックでは、データモデルに基づいてPrisma Clientを生成することを指定しています。
Prisma Clientは自動生成されるタイプセーフのクエリビルダで、データベースの操作をどのように簡略化するか見ていきます。
モデルの定義
Userモデルを作成しましょう。
// code above unchanged
model User {
id String @id @default(uuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
email String? @unique
image String?
role Role @default(USER)
}
enum Role {
USER
ADMIN
}
ここでは、いくつかのフィールドを持つUserモデルを定義しています。各フィールドは、名前の後に型とオプションのフィールド属性があります。
例えば、id フィールドは String 型で、@id フィールド属性を持ち、これがテーブルのプライマリーキーであることを指定します。default(uuid())属性はUUIDのデフォルト値を設定します。
デフォルトでは、すべてのフィールドが必須です。フィールドをオプションにするには、フィールドタイプの後に「?」をつけます
Role enum は、ユーザーが管理者であるか否かを示すために使用され、その後、User モデルで参照されます。
次に、Linkモデルを作成します。
// prisma/schema.prisma
// code above unchanged
model User {
id String @id @default(uuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
email String? @unique
image String?
role Role @default(USER)
}
enum Role {
USER
ADMIN
}
model Link {
id String @id @default(uuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
title String
description String
url String
imageUrl String
category String
}
リレーションの定義
最後に、UserモデルとLinkモデルの間に多対多の関係を作り、ユーザーが多くのリンクを持つことができ、リンクが多くのユーザーを持つことができるようにする必要があります。これは、リレーションの両側で、リレーションフィールドをリストとして定義することによって行います。
Userモデルにbookmarksフィールドを追加し、タイプはLink[]とします。次に、Linkモデルにusersフィールドを追加し、型はUser[]とします。
// prisma/schema.prisma
// code above unchanged
model User {
id String @id @default(uuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
email String? @unique
image String?
role Role @default(USER)
+ bookmarks Link[]
}
enum Role {
USER
ADMIN
}
model Link {
id String @id @default(uuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
title String
description String
url String
imageUrl String
category String
+ users User[]
}
これは暗黙の多対多の関係であり、基礎となるデータベースに関係テーブルがある。このリレーションテーブルはPrismaによって管理される。
最終的なスキーマはこんな感じです。
// prisma/schema.prisma
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model User {
id String @id @default(uuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
email String? @unique
image String?
role Role @default(USER)
bookmarks Link[]
}
enum Role {
USER
ADMIN
}
model Link {
id String @id @default(uuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
title String
description String
url String
imageUrl String
category String
users User[]
}
マイグレーションと変更をデータベースにプッシュ
これらのテーブルをデータベースに作成するには、次のコマンドを実行します。
npx prisma db push
ターミナルに次のような出力が表示されるはずです。
Environment variables loaded from /Users/mahmoud/Desktop/awesome-links/.env
Prisma schema loaded from prisma/schema.prisma
🚀 Your database is now in sync with your schema. Done in 2.10s
データベースのシード
現在、データベースは空っぽなので、データを入れたいと思います。まず最初に、データベースと対話するための型安全なクエリビルダーであるPrisma Clientをインストールする必要があります。
Prisma Clientをインストールするには、次のコマンドを実行します。
npm install @prisma/client
次に、/prisma/seed.tsというファイルを新規に作成します。このファイルによって、Prismaの統合されたシード機能を使用することができます。このファイルの中で、Prisma Clientをインポートし、インスタンス化し、いくつかのレコードを作成します。
// prisma/seed.ts
import { PrismaClient } from '@prisma/client'
import { data } from '../data/links'
const prisma = new PrismaClient()
async function main() {
await prisma.user.create({
data: {
email: `testemail@gmail.com`,
role: 'ADMIN',
},
})
await prisma.link.createMany({
data: data,
})
}
main()
.catch(e => {
console.error(e)
process.exit(1)
})
.finally(async () => {
await prisma.$disconnect()
})
まず、create()関数を使用してユーザーを作成し、新しいデータベースレコードを作成します。
次に、createMany() 関数を使用して複数のレコードを作成しています。パラメータとして、ハードコードされたデータを渡しています。
デフォルトでは、Next.jsはESNextモジュールを強制的に使用します。この動作をオーバーライドしないと、シードスクリプトを実行することができません。これを行うには、まずts-nodeを開発依存としてインストールします。
npm install ts-node -D
これで、以下のコマンドを実行して、データベースをシードすることができます。
npx prisma db seed
すべてが正しく動作していれば、次のような出力が表示されるはずです。
Environment variables loaded from .env
Prisma schema loaded from prisma/schema.prisma
Running seed: ts-node --compiler-options '{"module":"CommonJS"}' "prisma/seed.ts" ...
🌱 Your database has been seeded.
Prisma Studioを使用してデータベースを調査する
Prismaには、データを探索・操作するためのGUIであるPrisma Studioが付属しています。Prisma Studioを使用すると、データベースのデータを表示、作成、更新、削除できます。
Prisma Studioを起動するには、次のコマンドを実行します。
npx prisma studio
すべての手順が正しく行われたなら、データベース内にLinkモデルとUserモデルができているはずです。Linkモデルには4つのレコードがあり、Userモデルには1つのレコードがあります。
まとめと次のステップ
この記事では、問題領域の説明と、Prismaを使用したデータのモデリングを行いました。また、データベースのシードを作成し、Prisma Studioを使用してそれを調査しました。これで、PostgreSQLデータベースに接続されたNext.jsアプリができました。
次のパートでは、次のことを学びます。
- GraphQLと、APIを構築する際にRESTよりも優れている点
- ApolloサーバーとNexusを使用したアプリのためのGraphQL APIの構築。
- Apollo Clientを使用したクライアントでのAPIの使用。
- GraphQLのページネーションにより、すべてのリンクを一度に読み込まず、より良いパフォーマンスを実現します。
Discussion
翻訳ありがとうございます。
多分元記事が更新されたと思いますが、下記2つの設定が追加されました。
これがないと、seed のコマンド実行がエラーになっていました。