Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Next.js 14+Cognito +DynamoDB+Amplifyで認証付きのCRUDア...
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
YingZhi "Harrison" Huang
January 29, 2024
1.5k
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Next.js 14+Cognito +DynamoDB+Amplifyで認証付きのCRUDアプリを構築してみる
YingZhi "Harrison" Huang
January 29, 2024
More Decks by YingZhi "Harrison" Huang
See All by YingZhi "Harrison" Huang
RustでWebフロント作れるらしい
harrisoneagle
0
560
GoでORMを自作してみる
harrisoneagle
5
2.7k
Featured
See All Featured
Between Models and Reality
mayunak
4
350
The Straight Up "How To Draw Better" Workshop
denniskardys
239
140k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
35
3.5k
Digital Projects Gone Horribly Wrong (And the UX Pros Who Still Save the Day) - Dean Schuster
uxyall
1
1.8k
What does AI have to do with Human Rights?
axbom
PRO
1
2.2k
Prompt Engineering for Job Search
mfonobong
0
350
StorybookのUI Testing Handbookを読んだ
zakiyama
31
6.8k
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
31
3.2k
ラッコキーワード サービス紹介資料
rakko
1
3.7M
The Illustrated Children's Guide to Kubernetes
chrisshort
51
52k
Lightning talk: Run Django tests with GitHub Actions
sabderemane
0
200
brightonSEO & MeasureFest 2025 - Christian Goodrich - Winning strategies for Black Friday CRO & PPC
cargoodrich
3
740
Transcript
Next.js 14+Cognito +DynamoDB+Amplifyで認証 付きのCRUDアプリを構築してみ る HarrisonEagle
自己紹介 • HarrisonEagle ◦ 本業はSRE ◦ 副業や趣味などでフロントエンドのコード書いたりする ▪ 最近はServer ActionとRSCを遊んでる
◦ 好きなプログラミング言語: TypeScript, Go, Rust, Kotlin ◦ ゴルフ、筋トレ、バードウォッチングが趣味 ◦ GitHub: https://github.com/HarrisonEagle
これから話す内容 • Next.js 14 + AWS Amplify + AWS DynamoDB
+ AWS Cognitoで認証つきの Webアプリを構築する方法について紹介します ◦ バックエンドの処理はAmplify SSRとServer Actionで完結させます。LambdaやAPI Gatewayなど他のAPIを作って繋ぎこむことはありません ◦ 最新のAmplify SDK v6の使い方についても紹介します ◦ サンプルアプリとして簡単な CRUDアプリをデプロイしました: ▪ https://deployment.ddprvnig7qphd.amplifyapp.com ◦ リポジトリ:https://github.com/HarrisonEagle/amplify-dynamodb-ssr-sample
AWS Amplify • 簡単に言うとAWS版のVercel ◦ GitHubリポジトリと連携するだけデプロイまで自動化できる ◦ 認証機能を提供するCognito、ストレージ機能を提供する S3などのAWS機能との連携が 簡単にできる
今までのAmplify+DynamoDB+Cognitoの構成 これはRESTの場合だが、GraphQLの場合はAPI GatewayがAppSyncになる
今回作ったアプリの構成
Amplifyの中身 SSRの部分はフルマネージドになり、AmplifyでのSSRとServer ActionはLambdaに当たる部分で実行される模様
これは何が嬉しいか • フロントエンドとバックエンドのロジックは両方とも同じリポジトリで管理できるので、管理しやす いし自動デプロイの恩恵が両方得られる ◦ API GatewayとLambdaを構築する手間を省ける ◦ 特にLambdaのコード管理とデプロイの自動化はめんどくなりがち •
CORS設定も必要ない • ローカルでの検証も楽 • これによってよりフロントエンド側に注力できる ただし、SSRとServer Actionの実行時間に制限があるかは不明
Amplify SDKの初期化 • Amplify SDKの機能の多くとAmplify Authはク ライアントサイドで実行するので、 Amplify SDK の初期化はクライアントサイドで行う
◦ Amplify SDK v5以前ではここでAuth.configure でAuthの初期化もする必要があったが、 v6か らAuthの初期化もAmplify .configureでまとめ られるようになった • 明示的にAmplify側でSSRを使用するに設定す る • TokenなどをCookieで管理するように設定(後 述) “use client” import { CookieStorage, parseAmplifyConfig } from "aws-amplify/utils"; import { cognitoUserPoolsTokenProvider } from "aws-amplify/auth/cognito"; import awsmobile from "../aws-exports"; const amplifyConfig = parseAmplifyConfig(awsmobile); cognitoUserPoolsTokenProvider.setKeyValueStorage(new CookieStorage()); Amplify.configure(amplifyConfig, { ssr: true });
Cognitoのロールベースアクセスコントロール • APIへの認証をつけない代わりに、 DynamoDBへのアクセスを認証済みのユー ザーだけできるようにする • 認証で使用するCognitoのIdentity Poolの ユーザーアクセスに、ユーザーが認証された 場合のAWSサービスへのアクセスをロールと
して設定できる ◦ このロールに、認証されたユーザーが使用で きるDynamoDBへのアクセス権限を付与す る
バックエンド認証とDynamoDBへの接続 "use server"; import awsmobile from "../aws-exports"; import { createServerRunner
} from "@aws-amplify/adapter-nextjs"; import { fetchAuthSession, getCurrentUser } from "aws-amplify/auth/server"; import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; import { cookies } from "next/headers"; const { runWithAmplifyServerContext } = createServerRunner({ config: awsmobile, }); const getCurrentSessionFromServer = async () => await runWithAmplifyServerContext({ nextServerContext: { cookies }, operation: async (contextSpec) => fetchAuthSession(contextSpec), }); export const getDynamoDBClient = async () => { const session = await getCurrentSessionFromServer(); return await new DynamoDBClient({ credentials: session.credentials, region: "ap-northeast-1", }); }; • AWS Amplify SDK v6とNext.js Adapterを利 用すると結構便利 • next/headersでリクエストヘッダー内に含まれ てるCookieを抽出し、その中に入っている認証 情報を使用してセッションを取得する • 有効なセッション情報内には DynamoDBやS3 など、SDKの認証に使用できるCredentialsを 抽出できる ◦ 認証されたユーザーのみ DynamoDBに接続 できるような構成になる • fetchAuthSessionによるSessionチェックは middleware.tsでも行う ◦ 今回の場合は、ログインページ以外全部認証 する必要あるので、 middlewareでセッションが 無効だったらログイン画面にリダイレクトする ように • getCurrentUserでユーザー情報を取得できる
Server ActionからのDynamoDBへの操作 • AWS Amplify SDK v6とNext.js Adapterで DynamoDBクライアントの初期化とユーザー情 報の取得行う共通メソッドを予め用意する
• Server ActionsでそのままDynamoDBクライア ントとユーザー情報を取得し、それを利用して DBへのCRUD操作を行う • Server Action自体はClient Componentと Server Component両方インポートして実行で きるので非常に便利。 ◦ バックエンドとフロントエンドとの連携は、 Component側からServer Actionをインポート するだけで済むので API GatewayとLambdaを 構築する手間を省ける "use server" import { Note } from "@/entities"; import { v4 as uuidv4 } from "uuid"; import { getCurrentUserFromServer, getDynamoDBClient } from "@/utils"; import { QueryCommand, QueryCommandInput, PutItemCommand, PutItemCommandInput, DeleteItemCommand, DeleteItemCommandInput, } from "@aws-sdk/client-dynamodb"; import { revalidatePath } from "next/cache"; export const putNote = async (data: FormData) => { const note_name = data.get("note_name") as string; const note_content = data.get("note_content") as string; let note_id = data.get("note_id") as string; if (!note_id) { note_id = uuidv4(); } const client = await getDynamoDBClient(); const user = await getCurrentUserFromServer(); const putItemRequest: PutItemCommandInput = { TableName: tableName, Item: { note_id: { S: note_id }, user_id: { S: user.userId }, note_name: { S: note_name }, note_content: { S: note_content }, }, }; await client.send(new PutItemCommand(putItemRequest)); revalidatePath("/"); };
ただし... • SDKを使った処理をServer Actionで記述し、それをそのまま Client Componentからインポート する実装にしていたが、Devモードで動くもののProduction向けのビルドではWebpack build が失敗してしまう ◦
Next.js公式の例だとそれで動くはずだった ... ◦ https://github.com/vercel/next.js/discussions/57535 ▪ 上記のDiscussionはまだ完全にResolveされてない模様(2024.1.28現在) ◦ 元々DynamoDBに繋げるAWS SDKもそうだったが、SDKのアップデートとNext.js 14の 更新に伴って現在は修正済み ◦ このエラーに遭遇した場合、 Client ComponentからServer Actionを直接インポートする のではなく、Server ComponentからServer Actionをインポートし、Props経由でClient Componentに渡すことで対策できる
トラブルが発生する可能性がある実装 "use client"; import { deleteNote } from "@/actions"; type
Props = { note: Note; }; export const NoteCard = ({ note }: Props) => { return ( <Card mt="3" w="lg"> ... <ButtonGroup spacing="2"> <form action={deleteNote}> <input hidden name="note_id" value={note.note_id} /> <Button type="submit" variant="ghost" colorScheme="red"> Delete </Button> </form> </ButtonGroup> </Card> ); };
対策 "use client"; type Props = { note: Note; deleteNote:
(data: FormData) => Promise<void> // Server Componentから渡す }; export const NoteCard = ({ note, deleteNote }: Props) => { return ( <Card mt="3" w="lg"> ... <ButtonGroup spacing="2"> <form action={deleteNote}> <input hidden name="note_id" value={note.note_id} /> <Button type="submit" variant="ghost" colorScheme="red"> Delete </Button> </form> </ButtonGroup> </Card> ); };
まとめと考察 • Server ActionとRSCは色々言われているが、うまく使いこなせると APIを(手動で)作らずにバッ クエンドに繋げられるので割と便利 ◦ 特にAmplify SSRとの相性は良かった •
Amplify SSRを活用することによって、API Gateway + LambdaでREST APIを構築してフロント 側に繋ぎこむ手間をかなり省けた ◦ 何かWebアプリを爆速で作って AWSで動かして検証したい !って場合は向いてそう ◦ ただしタイムアウト時間については未知なので、重い処理は Lambdaに載せて、サーバーサイドで連 携させる方が無難 • RSCとServer ActionでもSDKが動かないものもあったりするのでプロダクションとして使う際に は注意が必要 ◦ Amplify SDKとFirebase SDKの多くの機能はクライアント前提だったりするので ◦ SDKを使った処理をServer Actionで記述し、それをそのまま Client Componentからインポートすると 動かないことがあるので要注意
ご清聴ありがとうございました!