[Cloudflare Pages+Next.js] AWS SDK for JavaScript v3を使ってDynamoDBからデータ取得して表示してみる
どうも!オペレーション部の西村祐二です。
最近、Cloudflare、Next.jsをさわっています。
Next.jsをCloudflare Pagesでホスティングして、DynamoDBからデータを取得するところまでいろいろハマりながら手順がわかったので、備忘録兼ねてまとめておきます。
DynamoDBからデータを取得する処理はNext.jsのAPI Routesを使ってAPIを実装し、Edgeランタイムの設定を行い、CloudflareのEdge側で動作させる方針です。
https://nextjs.org/docs/api-routes/introduction
前提条件
AWS側の初期設定やwranglerの初期設定は完了している前提で進めていきます。
環境
- Node.js:v18.15.0
- wrangler: 2.15.0
- @cloudflare/next-on-pages: 0.8.0
Next.jsの雛形プロジェクト作成
$ npx create-next-app --ts test-dynamo-pages 今回、設定は下記のようにしています。 ✔ Would you like to use ESLint with this project? … Yes ✔ Would you like to use Tailwind CSS with this project? … Yes ✔ Would you like to use `src/` directory with this project? … Yes ✔ Would you like to use experimental `app/` directory with this project? … No ✔ What import alias would you like configured? … @
cd test-dynamo-pages/
ライブラリインストール
$ npm install @aws-sdk/lib-dynamodb @aws-sdk/client-dynamodb swr
Next.jsのバージョンを変更
Cloudflare Pagesにデプロイする際に利用するビルドライブラリがNext.js13.2.4
までに対応しており、最新の13.3.0
(2023/4/18現在)
のままだと'Could not resolve "node:buffer"'
のエラーがでてしまうためです。
https://github.com/cloudflare/next-on-pages/blob/main/docs/supported.md
$ npm install next@13.2.4
AWS側の設定
IAMユーザの作成し、アクセスキーの取得をしておきます。今回、手順は省略します。
権限は今回AmazonDynamoDBReadOnlyAccess
を設定しています。
テスト用のDynamoDBテーブル作成し、テスト用のデータを作成
下記のテーブルとテストデータを作成しておきます。
- DynamoDBテーブル名:test-nextjs
-
テストデータ
- id:1
- content:test
これでAWS側の設定はおわりです。
Next.jsでDynamoDBからデータを取得する処理を実装
環境変数用のファイル作成
ローカルで動作確認するための設定として、アクセスキーを環境変数から読み込むため
.env
ではなく、.dev.vars
を作成します。
DynamoDBからデータを取得する処理はEdgeランタイムの設定を行いEdge上で動作させるためです。
https://developers.cloudflare.com/workers/platform/environment-variables/#secrets-in-development
先程、作成したAWS側の情報を設定したファイルをプロジェクト直下に作成します。
AWS_ACCESS_KEY_ID=xxxxxx AWS_SECRET_ACCESS_KEY=xxxxx AWS_REGION=xxxxx TABLE_NAME=xxxxx
間違えて、レポジトリにアップロードしないようにgitignore
に追記しておきましょう
. . . # typescript *.tsbuildinfo next-env.d.ts .dev.vars
DynamoDBの関連の実装
クライアントを作成する処理を実装していきます。
今回、環境変数からアクセスキーを取得し、クライアントを作成するようにしています。
import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb"; export const createDynamoDbClient = () => { const marshallOptions = { // Whether to automatically convert empty strings, blobs, and sets to `null`. convertEmptyValues: false, // Whether to remove undefined values while marshalling. removeUndefinedValues: true, // Whether to convert typeof object to map attribute. convertClassInstanceToMap: true, }; const unmarshallOptions = { // Whether to return numbers as a string instead of converting them to native JavaScript numbers. wrapNumbers: false, }; const translateConfig = { marshallOptions, unmarshallOptions }; const client: DynamoDBClient = new DynamoDBClient({ region: process.env.AWS_REGION, credentials: { accessKeyId: process.env.AWS_ACCESS_KEY_ID || "", secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY || "", }, }); return DynamoDBDocumentClient.from(client, translateConfig); };
DynamoDBからデータを取得する処理を実装していきます。
今回、DynamoDBのテーブルに保存されているデータをすべて取得するようにScanするようにしています。
import { NextRequest, NextResponse } from "next/server"; import { createDynamoDbClient } from "@/libs/dynamodb"; import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; import { ScanCommand, ScanCommandInput } from "@aws-sdk/lib-dynamodb"; export const config = { runtime: "edge", }; const handleGetRequest = async (client: DynamoDBClient) => { const params: ScanCommandInput = { TableName: process.env.TABLE_NAME, }; const { Items: allItems } = await client.send(new ScanCommand(params)); return new NextResponse(JSON.stringify(allItems)); }; const handler = async (req: NextRequest) => { const client: DynamoDBClient = createDynamoDbClient(); switch (req.method) { case "GET": return await handleGetRequest(client); default: return new NextResponse(JSON.stringify({ error: "error" })); } }; export default handler;
8-10行目がEdge Runtimeを使用するための設定になります。
export const config = { runtime: "edge", };
動作確認
ここで、一旦、API Routesが動作するか確認しておきます。
下記コマンドを実行しNext.jsのアプリケーションをビルドします。
.vercel/output/static
が生成され、wranglerで使用することができます。
$ npx @cloudflare/next-on-pages . . . ▲ Build Completed in .vercel/output [10s] ⚡️ Building Completed. ⚡️ Generated '.vercel/output/static/_worker.js'.
別ターミナルをひらき下記コマンドを実行します。
開発サーバが起動するので、URLにアクセスします。
$ npx wrangler pages dev .vercel/output/static --compatibility-flag=nodejs_compat . . . [mf:inf] Worker reloaded! (342.70KiB) [mf:inf] Listening on 0.0.0.0:8788 [mf:inf] - http://127.0.0.1:8788 [mf:inf] - http://100.64.1.17:8788 [mf:inf] Updated `Request.cf` object cache!
http://127.0.0.1:8788/api/dynamodb
に指定してアクセスしてみます。
ブラウザ上にDynamoDBに保存したテストデータ表示されたらOKです。
DynamoDBのデータをテーブルで表示
クライアント側でAPI Routesで実装したAPIを叩きDynamoDBのデータを取得しテーブル表示してます。
import useSWR from "swr"; type Data = { id: string; content: string; }[]; const fetcher = (url: string) => fetch(url).then((res) => res.json()); export default function Home() { const { data, error } = useSWR<Data>("/api/dynamodb", fetcher); if (error) return <div>An error has occurred.</div>; if (!data) return <div>Loading...</div>; return ( <main className="flex min-h-screen flex-col items-center justify-between p-24"> <table> <thead> <tr> <th> ID </th> <th> CONTENT </th> </tr> </thead> <tbody> {data.map((data) => ( <tr key={data.id}> <td>{data.id}</td> <td>{data.content}</td> </tr> ))} </tbody> </table> </main> ); }
index.tsxの修正が完了したらビルドを再度行います。
$ npx @cloudflare/next-on-pages
http://127.0.0.1:8788
にアクセスしDynamoDBのデータが表示されればOKです。
wranglerを使ってCloudflare Pagesにデプロイ
下記コマンドを実行してCloudflare Pagesにデプロイしていきます。
Pagesのプロジェクト名は今回、「test-dynamo-pages」としています。
$ wrangler pages publish .vercel/output/static ✔ Select an account › ✔ Enter the name of your new project: … test-dynamo-pages ✔ Enter the production branch name: … main . . . ✨ Deployment complete! Take a peek over at https://<your url>
デプロイが完了し、自動的にURLが払い出されますが、このままだと、アクセスしてもエラーとなってしまいますので、追加で、Cloudflare Pagesの設定をしていきます。
Cloudflare Pagesの設定
Pagesのプロジェクトで下記設定を行って行きます。
- 環境変数の設定
ローカルで設定していた環境変数(.dev.varsの内容)をPagesでも設定していきます。
- 互換性フラグの設定
設定のFunctionsに移動します。
互換性フラグの設定を行うところがあるので、そこに
nodejs_compat
を設定します。
設定が完了したら再度デプロイしてます。
$ wrangler pages publish .vercel/output/static
払い出されたURLにアクセスし、ローカルで確認した画面とおなじ表示がされたらOKです。
DynamoDBのテーブルにデータを追加するとPagesの画面も更新されるのがわかると思います。
さいごに
Next.jsをCloudflare Pagesでホスティングして、Next.jsのAPI Routesを使ってDynamoDBからデータ取得するAPIを実装し、CloudflareのEdge側で動作させてみました。
今回、DynamoDBのデータ取得にAWS SDK for JavaScript v3を利用しました。
Cloudflareには魅力的なDBのサービスがありますが、既存のデータはDynamoDBにあり、それを利用しないといけない場面もあるかとおもいためしてみました。
誰かの参考になれば幸いです。