はじめに
前回までにTrello風にタスクを管理するフロントエンド部分が何となくできてきたので、今回からはバックエンド部分も含め作成していこうかと思います。
前回までのあらすじ
今までの内容
- Tailwindを導入できた
- ComponentにPropsを渡せた
- Componentの中でPropsを使用できた
- Componentをループを使って表示できた
- React Iconsを導入できた
- useStateを使って状態管理ができた
- setStateを使って状態の更新ができた
- 状態に対するイミュータブル(不変)な操作の必要性が分かった
- 子側で親のイベントをトリガーできた
Next.jsでTrello風タスク管理アプリを作成する日記③
- filterをかけて条件に一致したデータでComponentを表示できた
- Dnd kikを導入できた
- Dnd kikのDndContextとソート機能に関するSortableContextでカラム内ソートを実装できた
Next.jsでTrello風タスク管理アプリを作成する日記④
- Dnd kitのDropableでカラム間を移動した時に対応したidをhandlerに渡す事ができた
- onDragEndとonDragOverを使い分けてhandlerを作成できた
- カラム間を横断するドラック&ドロップアイテムを作成できた
Next.jsでTrello風タスク管理アプリを作成する日記⑤
- Redux toolkitを導入してグローバルな状態管理ができるようになった
- 新しい機能実装に対応できるディレクトリ構成を検討できた
- Material UIを導入してModalを実装できた
- onDragとonClickイベントを持つDOM要素のイベントを制御できた
Trello風タスク管理アプリのアプリケーション構成を検討する日記⑥
- アプリケーション構成を検討できた
実装したい機能
今後フロントエンドとバックエンドを組み合わせて以下の機能を実装したいと思います。
認証機能
- ユーザーは自分のアカウントを作成し認証情報を使ってログインできる
- ユーザーはログイン後、自分のアカウントに紐づいた情報を管理できる
- ユーザーはログアウトできる
タスク管理機能
- ユーザーは自分のタスクを作成、削除、更新できる
今回の内容
- DockerでPostgreSQL環境を作成する
- Prisma(ORM)を使用しモデルを介してDB操作ができるようにする
- データ構造を定義しPrismaでテーブルを作成する
- Todo,Statusのテストレコードを作成しPrismaで挿入する
- 認証の基礎について理解を深める
PostgreSQLの作成
こちらを参考に作成しました。
DB、インフラ関連については別途きちんと勉強内容をまとめたいと思います。
services:
postgres:
# Alpine LinuxベースのPostgreSQLバージョン12
image: postgres:12-alpine
container_name: postgres
environment:
- POSTGRES_USER=※ユーザー名
- POSTGRES_PASSWORD=※パスワード
- POSTGRES_DB=※DB名
# データの永続化
volumes:
# DB情報は/var/lib/postgresql/dataに保存されるが、永続化のためpostgresディレクトリを/var/lib/postgresql/dataにマウント
- postgres:/var/lib/postgresql/data
# ポートの指定(HOST:CONTAINER)
ports:
- 5432:5432
# データの永続化
volumes:
postgres:
Prismaの導入
構築したDBとNextを連携でORMを利用する事を検討し下記記事を参考にPrisamを選択しました。
Prisma公式ドキュメントにTypescript × PostgreSQLのセットアップ手順がありましたので従いました。
ドキュメントに従ってターミナルで下記を実行
npm install prisma --save-dev
npx prisma
npx prisma init
DB接続に関するサンプル情報を含む.envが作成されました
# 初期のサンプルです
DATABASE_URL="postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public"
prismaのschemaファイルも作成され上の方にDB接続に関する設定があるため.envをDockerのローカル設定に合わせ記載する事で接続ができます。
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
PrismaでMigration実行
試しにTodoに関するデータ構造をmodelとして追記します。
model Todo {
id Int @id @default(autoincrement())
title String
content String
statusId Int
satus Status @relation(fields: [statusId], references: [id])
createdAt DateTime @default(now())
}
model Status {
id Int @id @default(autoincrement())
label String
todos Todo[]
}
上記テーブルをDBに反映しMigrateファイルを作成
npx prisma migrate save --name init(migrateの名前)
migrateの履歴はprisma/migrationディレクトリに実行されたSQLの形で保持されます。
例えばTodoとStatusテーブル作成のmigrate情報は下記のようになります。
-- CreateTable
CREATE TABLE "Todo" (
"id" SERIAL NOT NULL,
"title" TEXT NOT NULL,
"content" TEXT NOT NULL,
"statusId" INTEGER NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "Todo_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Status" (
"id" SERIAL NOT NULL,
"label" TEXT NOT NULL,
CONSTRAINT "Status_pkey" PRIMARY KEY ("id")
);
-- AddForeignKey
ALTER TABLE "Todo" ADD CONSTRAINT "Todo_statusId_fkey" FOREIGN KEY ("statusId") REFERENCES "Status"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
Prismaはブラウザで接続先のDB情報を表示する事もできます。
作成したデータ構造があるかブラウザで確認します。
npx prisma studio
Environment variables loaded from .env
Prisma schema loaded from prisma\schema.prisma
Prisma Studio is up on http://localhost:5555
# .envとschema.prismaをもとにdb情報をlocalhost:5555に出すよ!
http://localhost:5555 にアクセス
確認のためDB管理ツールでもローカルに立てたPostgreSQLの内容を見てみます。
DBeaverを使ってみます。
TodoとStatusテーブル、リレーションが定義されていました。
PrismaでSeed実行
作成したテーブルに初期データを作成したいと思います。
今回は初期データとして規定のseedファイルを作成しました。
// prisma/seed.ts
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()
async function seed() {
// Todoテーブルの全レコードを削除
await prisma.todo.deleteMany()
// Statusテーブルの全レコードを削除
await prisma.status.deleteMany()
// サンプルStatusデータの追加
const status1 = await prisma.status.create({
data: {
label: 'InProgress',
},
})
const status2 = await prisma.status.create({
data: {
label: 'Completed',
},
})
// サンプルTodoデータの追加
const todo1 = await prisma.todo.create({
data: {
title: 'Task 1',
content: 'Do something important',
statusId: status1.id,
},
})
const todo2 = await prisma.todo.create({
data: {
title: 'Task 2',
content: 'Complete the project',
statusId: status2.id,
},
})
console.log('Seed data added successfully')
}
seed()
.catch((error) => {
throw error
})
.finally(async () => {
await prisma.$disconnect()
})
prismaが提供する各種modelを使用し簡潔に記載できます。
prismaは上記規定ファイル:/prisma/seed.tsに定義された初期データの挿入をコマンドで実行可能です。
npx prisma db seed
上記コマンドを使用しなくても、tsファイルにprismaによるレコード操作を記述しnodeで実行する事は可能です。
認証の仕組み
全然説明が書けないくらい勉強不足なのでみなさんの記事を参考に認証について理解に努めました。
今回、Githubのトークンを使用した認証方法を実装したいと思いますが仕組みや関連する用語を整理してから実装に臨みたいと思います。
認証/認可
Webアプリにおいて認証とはサーバーへのリクエストを送ったクライアントは誰(何)なのか確認する作業と説明されています。
また確認ができたら誰(何)に許可されたリソースを返すのが一般的だと思います(認証に基づく認可)
下記の記事では認証と認可の関係性について分かりやすく説明されています。
またOAuth認証における認証/認可の注意点についても非常に分かりやすかったです。
家の住人である事を確認してもらうために合鍵を提出する(認可に基づく認証)
セッション認証/トークン認証
クライアントが認証された後、その情報をどうやって保持するか、下記の記事ではセッション認証、トークンベース認証の特徴を分かりやすく説明されていました。
セッション認証を使用した通信はサーバーのメモリ等にセッション情報を保持するためクライアントとサーバー間のやり取りに応じて状態を保持するステートフルな認証方式と認識しました。
一方、トークン認証はクライアントに返却されるトークンに認可スコープが設けられ、サーバー側はトークンの検証のみを行いスコープに応じてアクセス可能なリソースを返す、サーバーとクライアント間のやり取りの情報を保持しないステートレスな認証方式と認識しました。
OAuth2.0
下記の記事ではOAuthで登場する単語やキーワードの概要を参考にさせて頂きました。
認可サーバーとやり取りする際のAPIパラメーターやトークン、エンドポイントなど大変参考になりました。
また、下記の記事ではOAuthを認証のためのプロトコルとして使用する場合のセキュリティ上の注意点をまとめて頂いています。
認証について概要を確認しましたが難しいです。
これから実際Nextを使用して認証機能を実装する事で理解していこうと思います。
まとめ
今回バックエンド側のセットアップとしてDockerでローカル環境に作成したPostgreSQLにNextからPrismaを使用して接続しデータ構造とレコードの反映を行う事ができました。
認証については基本的な仕組みを確認する内容となりましたが、次回は実装する中でトークンや認可APIをどのように利用していくか記事にまとめたいと思います。
今回できた内容
- DockerでPostgreSQL環境を作成する
- Prisma(ORM)を使用しモデルを介してDB操作ができるようにする
- データ構造を定義しPrismaでテーブルを作成する
- Todo,Statusのテストレコードを作成しPrismaで挿入する
- 認証の仕組みを確認する