Next.js14とmicroCMSでCRUDの実装
概要
Next.js14がリリースされたので、CRUDの動作確認までやってみました。
安定版になったServerActionsもお試しで使ってます。
内容はNext.js × microCMSでシンプルなTodoAppの作成です。
環境
macOS: 14.1
Next.js: 14.0.2
Node.js: 18.18.2
プロジェクトの作成
下記のコマンドを実行してプロジェクトを作成します。
npx create-next-app@latest
ServerActionsを試すため、AppRouterを使用します。
✔ What is your project named? … next_microcms_crud
✔ Would you like to use TypeScript? … Yes
✔ Would you like to use ESLint? … Yes
✔ Would you like to use Tailwind CSS? … Yes
✔ Would you like to use `src/` directory? … Yes
✔ Would you like to use App Router? (recommended) … Yes
✔ Would you like to customize the default import alias (@/*)? … No
今回はmicroCMSを使用します。
microCMSはAPIベースのヘッドレスCMSです。
DBの設定をせずに簡単にCRUDの実装をするため採用してます。
また、公式でNext.jsとの連携に関するドキュメントがあるのも採用理由です。
以下のコマンドでSDKをインストールすることで、microCMSのAPIが扱いやすくなります。
npm install microcms-js-sdk
microCMSの設定
microCMSのアカウント作成などについての詳細は、公式ドキュメントを参照してください。
今回は、コンテンツAPIを以下のようなスキーマで作成しました。
{
"title": "タスクの追加"
}
idやcreatedAtなどは自動で生成されます。
作成できたら、APIキーの設定をします。
今回はCRUDの実装なので、POST,PATCH,DELETEの権限にチェックを入れておいてください。
/src
直下に/lib
を作成し、内部にclient.js
を作成してSDKの初期化をします。
import { createClient } from 'microcms-js-sdk';
export const client = createClient({
serviceDomain: 'service-domain', // XXXX.microcms.io の XXXX 部分
apiKey: 'api-key',
});
これでmicroCMSの準備はOKです。
ページの作成
/app
の直下に/todo
を作成し、内部にpage.tsx
を作成します。
ついでにglobals.css
を必要な記述のみにします。
@tailwind base;
@tailwind components;
@tailwind utilities;
データの取得
microCMSからデータを取得し、リスト表示します。
作成したコンテンツAPIに数件適当なデータを作成しておいてください。
/todo/page.tsx
を編集します。
スタイルはお好みで。
import { client } from "@/lib/client"
type dataType = {
contents: contentsType[]
totalCount: number
offset: number
limit: number
}
type contentsType = {
id: string
title: string
}
export default async function todo(){
//microCMSからデータを取得する処理
const data: dataType = await client.get({
endpoint: 'XXXX', //microCMSで設定したもの
queries: { fields: 'id,title' },
})
return(
<>
<div className="mt-[1rem] w-full mx-[2rem]">
<h2 className="text-[1.6rem]">List</h2>
{data?.contents.map((value, index) => (
<div key={index} className="flex my-[0.5rem]">
<li className="w-[20rem]">{value.title}</li>
</div>
))}
</div>
</>
)
}
npm run dev
を実行してhttp://localhost:3000/todo
にアクセスするとリストが表示ができました。
データの作成
リスト表示をしたページに、フォームを作成しデータを作成する処理を書きます。
~~~
export default async function todo(){
const data: dataType = await client.get({
endpoint: 'XXXX', //microCMSで設定したもの
queries: { fields: 'id,title' },
})
+ const createTodo = async (formData: FormData) => {
+ 'use server'
+ const title = formData.get('title') as string
+ const sendData = `{"title":"${title}"}`
+ // microCMSへの登録処理
+ await client.create({
+ endpoint: 'XXXX', //microCMSで設定したもの
+ content: JSON.parse(sendData),
+ })
+ // 登録処理後、/todoをrevalidateする
+ revalidatePath('/todo','page')
+ }
return(
<>
<div className="mt-[1rem] w-full mx-[2rem]">
<h2 className="text-[1.6rem]">List</h2>
{data?.contents.map((value, index) => (
<div key={index} className="flex my-[0.5rem]">
<li className="w-[20rem]">{value.title}</li>
</div>
))}
</div>
+ <div className="mt-[1rem] w-full mx-[2rem]">
+ <h2 className="text-[1.6rem] mb-[0.5rem]">Create</h2>
+ <form action={createTodo}>
+ <input type="text" name="title" placeholder="入力してください" defaultValue={""} className="border-solid border-[0.1rem] mr-[0.5rem]"/>
+ <input type="submit" value="送信" className="bg-blue-500 text-white rounded-[0.8rem] px-[1rem] cursor-pointer"/>
+ </form>
+ </div>
</>
)
}
これでフォームの作成ができました。
送信ボタンを押すと、formタグで設定されているaction
のcreateTodo
が実行され、microCMSへの登録処理をします。
revalidatePath
を設定しているため、送信後にリロードをしなくても最新のデータがリストに表示されます。
データの編集
以下の流れでデータの編集をします。
URLからIDの取得
↓
IDをもとにデータを取得し表示
↓
編集ボタンクリックでmicroCMSへデータの送信
↓
`/todo`へリダイレクト
まずは/todo/page.tsx
に編集ボタンを追加します。
import { client } from "@/lib/client"
import { revalidatePath } from "next/cache"
+ import Link from "next/link"
~~~
return(
<>
<div className="mt-[1rem] w-full mx-[2rem]">
<h2 className="text-[1.6rem]">List</h2>
{data?.contents.map((value, index) => (
<div key={index} className="flex my-[0.5rem]">
<li className="w-[20rem]">{value.title}</li>
+ <Link className="bg-green-500 text-white rounded-[0.8rem] px-[1rem] mr-[0.5rem] cursor-pointer" href={`/todo/${value.id}`}>
+ 編集
+ </Link>
</div>
))}
</div>
<div className="mt-[1rem] w-full mx-[2rem]">
<h2 className="text-[1.6rem] mb-[0.5rem]">Create</h2>
<form action={createTodo}>
<input type="text" name="title" placeholder="入力してください" defaultValue={""} className="border-solid border-[0.1rem] mr-[0.5rem]"/>
<input type="submit" value="送信" className="bg-blue-500 text-white rounded-[0.8rem] px-[1rem] cursor-pointer"/>
</form>
</div>
</>
)
}
こんな感じで編集ボタンが追加できました。
次に編集ページにフォームを作成します。
/todo
直下に/[id]
を作成し、内部にpage.tsx
を作成します。
import { client } from "@/lib/client"
import { revalidatePath } from "next/cache"
import { redirect } from "next/navigation"
type dataType = {
contents: contentsType[]
totalCount: number
offset: number
limit: number
}
type contentsType = {
id: string
title: string
}
type Props = {
id:string
}
export default async function updateTodo({params}:{params:Props}){
//microCMSからデータを取得する処理
const data: dataType = await client.get({
endpoint: 'XXXX', //microCMSで設定したもの
queries: {
fields: 'id,title' ,
filters:`id[equals]${params.id}`
},
})
//編集処理
const updateTodo = async (formData: FormData) => {
'use server'
const title = formData.get('title') as string
const id = formData.get('id') as string
const sendData = `{"title":"${title}"}`
await client.update({
endpoint: 'XXXX', //microCMSで設定したもの
content: JSON.parse(sendData),
contentId: id
})
revalidatePath('/todo/[id]',"page")
revalidatePath(`/todo`,"page")
redirect('/todo')
}
return(
<>
<div className="mx-[2rem]">
<h2 className="text-[1.6rem] mt-[1rem]">編集</h2>
<div className="flex mt-[0.5rem]">
<form action={updateTodo}>
<input type="hidden" name="id" value={data.contents[0].id} />
<input type="text" name="title" placeholder="編集する" defaultValue={data.contents[0].title} className="border-solid border-[0.1rem] mr-[0.5rem]"/>
<input type="submit" value="編集" className="bg-green-500 text-white rounded-[0.8rem] px-[1rem] cursor-pointer" />
</form>
</div>
</div>
</>
)
}
http://localhost:3000/todo
にアクセスし編集ボタンを押すと、ページ遷移し以下のような挙動でデータの編集ができます。
データの削除
最後に削除機能の実装です。
/todo/page.tsx
のリストに削除ボタンと処理を追加します。
~~~
+ const deleteTodo = async (formData:FormData)=>{
+ 'use server'
+ const id = formData.get('id') as string
+ await client.delete({
+ endpoint: 'XXXX', //microCMSで設定したもの
+ contentId: id
+ })
+ revalidatePath('/todo','page')
+ }
return(
<>
<div className="mt-[1rem] w-full mx-[2rem]">
<h2 className="text-[1.6rem]">List</h2>
{data?.contents.map((value, index) => (
<div key={index} className="flex my-[0.5rem]">
<li className="w-[20rem]">{value.title}</li>
<Link className="bg-green-500 text-white rounded-[0.8rem] px-[1rem] mr-[0.5rem] cursor-pointer" href={`/todo/${value.id}`}>
編集
</Link>
+ <form action={deleteTodo}>
+ <input type="hidden" name="id" value={value.id} />
+ <input type="submit" value="削除" className="bg-red-500 text-white rounded-[0.8rem] px-[1rem] cursor-pointer"/>
+ </form>
</div>
))}
</div>
<div className="mt-[1rem] w-full mx-[2rem]">
<h2 className="text-[1.6rem] mb-[0.5rem]">Create</h2>
<form action={createTodo}>
<input type="text" name="title" placeholder="入力してください" defaultValue={""} className="border-solid border-[0.1rem] mr-[0.5rem]"/>
<input type="submit" value="送信" className="bg-blue-500 text-white rounded-[0.8rem] px-[1rem] cursor-pointer"/>
</form>
</div>
</>
)
これで削除機能の実装完了、CRUD処理ができました。
参考
Discussion