💨
Remix + Prisma でデータ表示・登録・削除機能を実装する
目的
Remix を使用したフルスタックアプリケーションを開発します。
この記事では、DB から取得したデータの一覧表示、登録、削除機能を実装します。
環境
環境 | バージョン |
---|---|
Node.js | 20.11.1 |
Yarn | 4.1.1 |
Prisma CLI | 5.11.0 |
Prisma Client | 5.11.0 |
データ取得用関数
概要
DB からデータ取得・登録・削除機能を実装にあたり、 DB アクセス用の Class を作成します。
作業
ファイルを作成します。
mkdir ./app/models/
touch ./app/models/post.server.ts
post.server.ts に以下の通り上書きします。
import { Post } from '.prisma/client';
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
class PostRepository {
async create(params: Pick<Post, 'title' | 'content'>) {
const { title, content } = params;
if (!title || !content) throw new Error('Title and content are required');
await prisma.post.create({
data: {
title: title,
content: content,
},
});
return;
}
async find(params: { id: Post['id'] }) {
return await prisma.post.findUnique({ where: { id: params.id } });
}
async findAll(): Promise<Post[]> {
return await prisma.post.findMany();
}
async delete(params: { id: Post['id'] }) {
await prisma.post.delete({
where: { id: params.id },
});
return;
}
}
const postRepository = new PostRepository();
export { postRepository };
上記コードは、以下の処理を担当します。
メソッド | 処理 |
---|---|
create | title と content をパラメータを使用して、ポストを DB に登録する |
find | 既に登録済みポストの id をパラメータに使用して、DB からデータを取得する |
findAll | 既に登録済みポストを DB から全て取得する |
delete | 既に登録済みポストの id をパラメータに使用して、DB からデータを削除する |
ポスト一覧画面
app/routes/_index.tsx ファイルを以下の通り作成します。
import type { MetaFunction } from '@remix-run/node';
import { Link } from '@remix-run/react';
export const meta: MetaFunction = () => {
return [
{ title: 'Remix fullstack blog' },
{ name: 'description', content: 'Remix fullstack blog' },
];
};
export default function Index() {
return (
<div>
<Link to='/posts'>ポスト一覧</Link>
</div>
);
}
app/routes/posts._index.tsx ファイルを以下の通り作成します。
作成するリンクとタイトルのリンクは、後述のセクションで使用します。
import { Post } from '@prisma/client';
import { json } from '@remix-run/node';
import { Link, useLoaderData } from '@remix-run/react';
import { postRepository } from '~/models/post.server';
export const loader = async () => {
const posts = await postRepository.findAll();
return json(posts);
};
export default function PostIndex() {
const posts: Post[] = useLoaderData();
return (
<div>
<h1>ポスト</h1>
<Link to='new'>ポストを作成する</Link>
<ul>
{posts.map((post: Post) => (
<li key={post.id}>
<Link to={post.id}>{post.title}</Link>
</li>
))}
</ul>
</div>
);
}
Remix を起動して以下の通り表示されると作業完了です。
ポスト登録画面
app/routes/posts.new.tsx ファイルを以下の通り作成します。
import { redirect } from '@remix-run/node';
import type { ActionFunctionArgs } from '@remix-run/node';
import { postRepository } from '~/models/post.server';
export const action = async ({ request }: ActionFunctionArgs) => {
const form = await request.formData();
const title = form.get('title');
const content = form.get('content');
if (typeof title !== 'string' || typeof content !== 'string') {
throw new Error('Title and content are required');
}
await postRepository.create({ title: title, content: content });
return redirect('/posts');
};
export default function PostNew() {
return (
<div>
<h1>ポスト新規作成</h1>
<form method='post'>
<input
style={{ display: 'block' }}
name='title'
placeholder='title'
type='text'
/>
<input
style={{ display: 'block' }}
name='content'
placeholder='content'
type='text'
/>
<button style={{ display: 'block' }} type='submit'>
作成する
</button>
</form>
</div>
);
}
Remix を起動すると、以下の通り表示されます。
title と content を入力して保存すると、一覧画面に遷移してポストが追加されると作業完了です。
ポスト詳細画面
app/routes/posts.$postId.tsx ファイルを以下の通り作成します。
削除ボタンの機能は後述のセクションで実装します。
import { Post } from '@prisma/client';
import { LoaderFunctionArgs, json } from '@remix-run/node';
import { Form, Link, useLoaderData } from '@remix-run/react';
import { postRepository } from '~/models/post.server';
export const loader = async ({ params }: LoaderFunctionArgs) => {
if (!params.postId)
throw new Response(null, {
status: 404,
statusText: 'Not Found',
});
const post = await postRepository.find({ id: params.postId });
return json(post);
};
export default function PostShow() {
const post: Post = useLoaderData();
return (
<div>
<Link to='/posts'>一覧に戻る</Link>
<h1>ポスト</h1>
<h2>{post.title}</h2>
<p>{post.content}</p>
<Form action='delete' method='post'>
<button>削除</button>
</Form>
</div>
);
}
Remix を起動すると、以下の通り表示されると作業完了です。
ポスト削除機能
app/routes/posts.$postId.delete.tsx ファイルを以下の通り作成します。
import { redirect, type ActionFunctionArgs } from '@remix-run/node';
import { postRepository } from '~/models/post.server';
export const action = async ({ params }: ActionFunctionArgs) => {
if (!params.postId) throw new Error('Parameter is missing');
await postRepository.delete({ id: params.postId });
return redirect('/posts');
};
削除ボタンを押下すると、一覧画面に遷移し、対象のポストが削除されると作業完了です。
まとめ
実装コードは以下 PR の通りです。
Discussion