初めまして。システムデザイン開発の高木です。
Prisma は、T3 Stackの技術の1つに採用されている今流行りのORMです。
今まで私はバックエンド依存のORM(GormやActiveRecord等)を使用していたので、
フロントエンドベースでDB操作を行えることがとても魅力的に感じました。
今回はNext.js(TypeScript) × Prisma × Ant Design の構成で、
簡単なメモアプリを作ってみようと思います。
Prismaとは
Node.js環境で動作するTypeScriptおよびJavaScript向けのORMです。
Prismaは次の3つの要素から構成されています。
・Prisma Client
データベースとのやり取りを行うためのライブラリ
・Prisma Migrate
データベースのスキーマ管理を行うためのCLIツール
・Prisma Studio
Web上でデータベース管理を行うためのGUIツール
また、PostgreSQL、MySQL、SQL Server等、主要なRDBMSに対応しています。
対応状況については以下を参照してください。
GraphQLやREST APIのようなデータアクセスの方法にも対応しているため、
Webアプリケーション開発において幅広く使用されています。
プロジェクトの作成
まずはNext.jsプロジェクトを作成します。
npx create-next-app@latest prisma_tutorial_app --ts
質問に対しては以下のように返しています。
今回はAnt Designを使用するため、Tailwind CSSは無効にしておきます。
Would you like to use ESLint with this project? ... No / Yes
Would you like to use Tailwind CSS with this project? ... No / Yes
Would you like to use `src/` directory with this project? ... No / Yes
Would you like to use experimental `app/` directory with this project? ... No / Yes
What import alias would you like configured? ... @/*
プロジェクト作成完了後、正常にデフォルトページが表示されるかどうかを確認してみましょう。
npm run dev
Prisma CLIの導入
prismaパッケージをインストールし、起動します。
npm install prisma --save-dev
npx prisma
次にPrisma の初期設定を行います。
今回はDBにSQLiteを使用するため、datasource-providerにsqliteを指定しています。
npx prisma init --datasource-provider sqlite
このコマンドを実行することで、prisma/schema.prismaとenvファイルが生成されます。
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}
DATABASE_URL="file:./dev.db"
Prisma Migrateを使用してmigrateを実行する
Prisma Migrate はschema.prismaのデータモデルを元にmigrateを行うため、
まずはデータモデルを定義します。
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}
model notes {
id Int @id @default(autoincrement())
content String
createdAt DateTime @default(now())
}
アノテーションを使用することで、スキーマに関する情報を定義することができます。
今回は最低限のものしか使用していないため、詳しく知りたい方は以下を参照してください。
migrateを実行します。
npx prisma migrate dev --name init
migrationファイルの生成 + スキーマの変更が実行されます。
CREATE TABLE "notes" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"content" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);
Prisma Studioを使用してDBの管理画面を表示する
以下のコマンドでデータベースの管理画面を表示できます。
npx prisma studio
Tableを選択する画面です。先ほど作成した「notes」をクリックします。
Tableの管理画面が表示されました。
今回は触れませんが、レコードの追加/変更/削除等、Tableに対して様々な操作が行えます。
Prisma Clientを使用してメモアプリを作成する
@prisma/clientパッケージをインストールします。
npm install @prisma/client
まずはエンドポイントを作成します。
PrismaClientを使用して、検索/登録/削除機能を実装しています。
import { NextRequest, NextResponse } from 'next/server';
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
export async function GET() {
const notes = await getAllNotes();
return NextResponse.json(notes);
}
export async function POST(request: NextRequest) {
const { content } = await request.json();
await prisma.notes.create({
data: {
content: content,
},
});
const notes = await getAllNotes();
return NextResponse.json(notes);
}
export async function DELETE(request: NextRequest) {
const id = parseInt(request.nextUrl.searchParams.get('id')!);
await prisma.notes.delete({
where: {
id: id,
},
});
const notes = await getAllNotes();
return NextResponse.json(notes);
}
async function getAllNotes() {
const notes = await prisma.notes.findMany();
return notes;
}
次に画面を作成します。
前述した通りAnt Designを使用しています。
'use client';
import { useState, useEffect, ChangeEvent } from 'react';
import { Card, Row, Col, Input, Button, Table } from 'antd';
import { ColumnsType } from 'antd/es/table';
interface DataType {
key: string;
id: number;
content: string;
createdAt: string;
}
export default function Home() {
const [content, setContent] = useState('');
const [dataSource, setDataSource] = useState<DataType[]>([]);
useEffect(() => {
const fetchNotes = async () => {
const response = await fetch('/api/notes');
const notes = await response.json();
setDataSource(notes);
};
fetchNotes();
}, []);
const handleInputChange = (event: ChangeEvent<HTMLInputElement>) => {
setContent(event.target.value);
};
const handleSaveClick = async () => {
const response = await fetch('/api/notes', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ content }),
});
const notes = await response.json();
setDataSource(notes);
setContent('');
};
const handleDeleteClick = async (id: number) => {
const response = await fetch(`/api/notes?id=${id}`, {
method: 'DELETE',
});
const notes = await response.json();
setDataSource(notes);
};
const columns: ColumnsType<DataType> = [
{
title: 'created_at',
dataIndex: 'createdAt',
width: '20%',
render: (date: Date) => new Date(date).toLocaleDateString(),
sorter: (a, b) => Date.parse(a.createdAt) - Date.parse(b.createdAt),
},
{
title: 'content',
dataIndex: 'content',
width: '75%',
},
{
width: '5%',
render: (record: DataType) => (
<Button danger onClick={() => handleDeleteClick(record.id)}>
Delete
</Button>
),
},
];
const centeredStyle: React.CSSProperties = {
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
height: '100vh',
};
return (
<div style={centeredStyle}>
<Card title='Note' style={{ width: 800 }}>
<Row>
<Col span={16}>
<Input
placeholder='content'
value={content}
onChange={handleInputChange}
/>
</Col>
<Col span={7} offset={1}>
<Button type='primary' onClick={handleSaveClick}>
Save
</Button>
</Col>
</Row>
<Table
dataSource={dataSource}
columns={columns}
rowKey={(record) => record.id}
pagination={{
pageSize: 5,
}}
style={{ marginTop: 20 }}
/>
</Card>
</div>
);
}
完成形がこちらになります。
まとめ
今回はPrismaを使用してみましたが、他のフレームワークに比べてmigrateが簡単に実行できたり、型の安全性が保証されるため、正確なDB操作が行えるな、と感じました。
フロントエンドにTypeScriptを使用している方や、T3 Stackを使用してType Safeな
フルスタックアプリケーションを作成したい方は、是非一度使用してみてはいかがでしょうか。
次回は、同じくT3 Stackの技術の1つであるtRPCについての記事を書こうと思っています。
システムデザイン開発は、北海道の地で35年以上の歴史があります。企業向けのシステム設計~開発・構築~保守運用までワンストップサービスを提供するシステム開発会社です。豊富な開発実績と高い技術力を強みとして、北海道から全国へ幅広い分野・業種へトータルにサポートいたします。
システムの導入やご検討、お困りごとがありましたら、お気軽にご相談・お問合せください