Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
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
580
GoでORMを自作してみる
harrisoneagle
5
2.7k
Featured
See All Featured
I Don’t Have Time: Getting Over the Fear to Launch Your Podcast
jcasabona
35
2.9k
How to Ace a Technical Interview
jacobian
281
24k
A brief & incomplete history of UX Design for the World Wide Web: 1989–2019
jct
2
510
Leveraging LLMs for student feedback in introductory data science courses - posit::conf(2025)
minecr
1
410
Chasing Engaging Ingredients in Design
codingconduct
0
340
How To Speak Unicorn (iThemes Webinar)
marktimemedia
1
590
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
52
6.1k
A Soul's Torment
seathinner
8
3.6k
Jess Joyce - The Pitfalls of Following Frameworks
techseoconnect
PRO
1
420
Ethics towards AI in product and experience design
skipperchong
2
390
How to Align SEO within the Product Triangle To Get Buy-In & Support - #RIMC
aleyda
2
1.8k
Rebuilding a faster, lazier Slack
samanthasiow
85
9.7k
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からインポートすると 動かないことがあるので要注意
ご清聴ありがとうございました!