Next.js 14でTodoアプリを作る – 5.CRUDを作成
投稿日:2024年03月12日
更新日:2024年03月22日
テーブルtodosのCRUDを作成します。
CRUD とは
CRUDとはCREATE、READ、UPDATE、DELETEの頭文字をとった略称です。
データの新規作成、読み取り、更新、削除の機能の総称です。
APIを実現するためにはGET、POST、PUT、DELETEのHTTPMethodsを使います。
事前準備
ドキュメントを参考に追加します。
https://developers.cloudflare.com/d1/examples/d1-and-hono/
またドキュメント通りにしてもエラーが発生します。
エラー解決には以下の記事を参考にしました。
https://zenn.dev/da1/articles/cloudflare-nextjs-hono-drizzle
インスタンスを作成するクラスHonoのBindings
プロパティに型を指定します。
以下のように、コードを変更します。
- const app = new Hono().basePath('/api')
+ const app = new Hono<{ Bindings: Bindings }>().basePath('/api')
指定する型Bindings
を作成します。
プロジェクトのルートにtypes
ディレクトリを作成します。
ファイルBindings.ts
を新規作成します。
以下のコードを追加します。
type Bindings = {
DB: D1Database;
};
グローバル変数を宣言します。
ファイルenv.d.ts
にDB: D1Database
を追加します。
declare global {
namespace NodeJS {
interface ProcessEnv {
// Add here the Cloudflare Bindings you want to have available in your application
// (for more details on Bindings see: https://developers.cloudflare.com/pages/functions/bindings/)
//
// KV Example:
// MY_KV: KVNamespace
+ DB: D1Database;
}
}
}
export {}
ついでにコードを詳しくみます。
declare global {}
このキーワードはグローバルスコープで変数を宣言するために使用されます。
namespace NodeJS {}
この名前空間はNode.js環境で使用されるグローバル変数を定義します。
interface ProcessEnv {}
このインターフェースはprocess.env
オブジェクトの型を定義します。
DB: D1Database;
このプロパティはDB
という名前の変数を宣言しD1Database
型のオブジェクトでデータベースへの接続を表します。
1件取得
ドキュメントに1件取得するコードが載っているので、それを参考に1件取得の機能を追加します。
https://developers.cloudflare.com/d1/examples/d1-and-hono/
次にHonoでGETリクエストのためのルーティングを設定します。
以下のコードをファイルroute.ts
のapp.get('/hello', (c) => {})
の下に追加します。
app.get('/todos/:id', async (c) => {
const id = c.req.param('id')
try {
const { results } = await process.env.DB.prepare(
'SELECT * FROM todos WHERE id = ?;'
)
.bind(id)
.all()
return c.json(results)
} catch (e) {
return c.json({ err: e }, 500)
}
});
ここから追加したコードを詳しく見ていきます。
app.get('/todos/:id', async (c) => { ... });
GETリクエストを使って/todos/:id
というURLにアクセスされたときにasync
関数が呼び出されます。
const id = c.req.param('id')
リクエストパラメータid
を取得します。
このパラメータはURLの/:id
部分に指定された値になります。
const { results } = await process.env.DB.prepare(
'SELECT * FROM todos WHERE id = ?;'
).bind(id).all();
データベースにクエリSELECT * FROM todos WHERE id = ?;
を実行します。
このクエリはid
が指定されたデータをtodos
テーブルをすべて取得します。
return c.json(results)
クエリが成功した場合はで結果をJSON形式で返します。
return c.json({ err: e }, 500)
クエリが失敗した場合はエラーメッセージとステータスコード500を返します。
全件取得
1件取得のコードを複製して改造します。
まずはapp.get('/todos/:id'
から/:id
を削除します。const id = c.req.param('id')
を削除します。
次にクエリSELECT * FROM todos WHERE id = ?;
からWHERE id = ?
を削除します。
またbind(id)
を削除します。
完成すると以下のようになります。
app.get('/todos', async (c) => {
try {
const { results } = await process.env.DB.prepare(
'SELECT * FROM todos'
).all()
return c.json(results)
} catch (e) {
return c.json({err: e}, 500)
}
})
削除
1件取得のコードを複製して改造します。
まずはapp.get
をapp.delete
に変更します。
次にクエリSELECT * FROM todos WHERE id = ?;
をDELETE FROM todos WHERE id = ?;
に変更します。bind(id)
の後のall()
をrun()
に変更します。c.json(results)
のresults
を{message:'Deleted'}
に変更します。
最後にexport const DELETE = handle(app)
を追加します。
完成すると以下のようになります。
app.delete('/todos/:id', async (c) => {
const id = c.req.param('id');
try {
await process.env.DB.prepare('DELETE FROM todos WHERE id = ?;')
.bind(id)
.run()
return c.json({ message: 'Deleted' }, 200)
} catch (e) {
return c.json({ err: e }, 500)
}
})
export const DELETE = handle(app)
新規登録
ドキュメントを参考に新規登録の機能を追加します。
https://developers.cloudflare.com/d1/tutorials/build-a-comments-api/#6-insert-data
app.post('/todos', async (c) => {
const { title, content } = await c.req.json()
try {
const res = await process.env.DB.prepare(
'INSERT INTO todos (title, content) VALUES (?, ?);'
).bind(title, content).run()
return c.json({ message: 'Created' }, 201)
} catch (e) {
return c.json({ err: e }, 500)
}
})
更新
削除のコードを複製して改造します。
まずはapp.delete
をapp.put
に変更します。const id = c.req.param("id")
の下にconst { title, content } = await c.req.json()
を追加します。
次にクエリDELETE FROM todos WHERE id = ?;
をUPDATE todos SET title = ?, content = ? WHERE id = ?;
に変更します。bind(id)
のid
の前にtitle
とcontent
を追加します。c.json({message: 'Deleted'})
のDeleted
をUpdated
に変更します。
最後にexport const PUT = handle(app)
を追加します。
完成すると以下のようになります。
app.put('/todos/:id', async (c) => {
const id = c.req.param('id')
const { title, content } = await c.req.json()
try {
await process.env.DB.prepare(
'UPDATE todos SET title = ?, content = ? WHERE id = ?;'
).bind(title, content, id).run()
return c.json({ message: 'Updated' })
} catch (e) {
return c.json({ err: e }, 500)
}
})
export const PUT = handle(app)
動作確認
CRUD機能を実装したので、APIの動作確認をします。
簡易サーバーを立ち上げます。
以下のコマンドを実行します。
npm run pages:preview
http://localhost:8788
/が立ち上がるのでAPIの動作確認をします。
動作確認には拡張機能Thunder Clientを使います。
もし導入されていないときは、拡張機能で検索して導入してください。
URLhttp://localhost:8788/api/todos/
を使って以下の各メソッドを実行します。
GETメソッドを使って、全件取得、1件取得できるか確認します。
POSTメソッドを使って、新規登録できるか確認します。
PUTメソッドを使って、更新できるか確認します。
DELETEメソッドを使って、削除できるか確認します。
デプロイ
APIの動作確認ができたので、本番環境にデプロイします。
まずは本番環境のtodo-app-d1
データベースにテーブルtodos
を作成します。
以下のコマンドを実行します。
本番環境でテーブルを作成するときはオプションlocal
をオプションremoteに変更して実行します。
npx wrangler d1 execute todo-app-d1 --remote --file=./schema.sql
本番環境のデータベースにテーブルtodos
が作成されたかどう確認します。
以下のコマンドを実行します。
npx wrangler d1 execute todo-app-d1 --remote --command='SELECT * FROM todos'
プロジェクトをデプロイします。
以下のコマンドを実行します。
npm run pages:deploy
URLhttps://todo-app-d1-base.pages.dev/api/todos/
を使って以下の各メソッドを実行します。
GETメソッドを使って、全件取得、1件取得できるか確認します。
POSTメソッドを使って、新規登録できるか確認します。
PUTメソッドを使って、更新できるか確認します。
DELETEメソッドを使って、削除できるか確認します。