AWS Amplify Gen2 フルスタック本番運用Vol1|data・auth・functions

目次

1. Amplify Gen 2 の全体像とフルスタックTypeScript本番運用の課題

fig01: AWS Amplify Gen 2全体アーキテクチャ(code-first TypeScriptのbackend定義→AWS CDK→CloudFormation→AppSync/DynamoDB/Cognito/Lambda/S3へ展開する流れと、フロントエンド/Amplify Hostingとの関係)
fig01: Amplify Gen 2全体像 — code-first TypeScript定義からAWSサービス展開まで

フルスタックWebアプリの本番運用では、認証・API・データベース・ファイルストレージ・サーバーレス処理を個別に設計・構築することが求められます。
それぞれのサービスをIaC(Infrastructure as Code)で管理しようとすると、CloudFormationテンプレートやCDKスタックが複数に分散し、
認証設定とAPIの認可ルールの整合性を手動で保つ必要が生じます。
開発チームが並行して作業すると設定ファイルのコンフリクトが起きやすく、
「ローカルのモック環境と本番クラウドの差異」によるデバッグコストも積み重なります。
本番・ステージング・開発の3環境を一貫した方法で管理できずに設定ずれが生じ、リリース障害につながるケースも少なくありません。
さらに、フロントエンドのTypeScriptコードとバックエンドの設定が別リポジトリ・別言語で分離されていると、
型安全性の恩恵を受けられず、APIスキーマの変更がフロントエンドへ及ぼす影響を見逃しやすくなります。

AWS Amplify Gen 2は、こうした課題に対してTypeScriptのcode-first定義でバックエンドを構成するアプローチを提供します。
2024年5月にGAとなった本バージョンでは、amplify/ディレクトリ配下にTypeScriptファイルを配置し、
defineDatadefineAuthdefineFunctiondefineStorageといったAPIでバックエンドリソースを宣言的に定義します。
定義した内容は内部のAWS CDK L3 constructを通じてCloudFormationスタックへ変換され、自AWSアカウントへ自動展開されます。
IDEの補完・型検証・リファクタリングが効き、GitリポジトリでIaCとアプリケーションコードを一元管理できます。
フロントエンドのコードはAmplifyが自動生成する型付きデータクライアントを通じてバックエンドと通信するため、
スキーマ変更の影響をTypeScriptのコンパイルエラーとして即座に検出できます。

Gen 2 が解決するフルスタック開発の本番課題

従来のフルスタック開発では「設定の整合性管理」と「環境の再現性」が最大の課題でした。
Gen 2はTypeScript code-firstによりこれらを一気に解決します。

課題従来のアプローチAmplify Gen 2の解決策
IaC整合性CFnテンプレート手動管理TypeScript定義から自動生成
型安全性フロントエンドのみバックエンドAPIまで貫通
開発環境分離共有環境/手動構築per-developer sandbox自動分離
設定変更追跡JSON差分が難読TypeScriptコードでPRレビュー
CDK拡張Overrideで回避策CDK constructを直接追加

Amplify Gen 2 の主要コンポーネント

Amplify Gen 2では、フルスタックアプリを構成するバックエンドコンポーネントをTypeScriptで宣言します。
各コンポーネントは独立したTypeScriptファイルで管理されるため、チームが並行して作業してもGitコンフリクトを最小化できます。
定義ファイルはamplify/ディレクトリ以下に配置し、amplify/backend.tsでまとめてエクスポートします。
Amplifyはこのbackend.tsを読み取り、スタック間の依存関係を自動的に解決してCloudFormationスタックを生成します。

コンポーネント定義API展開されるAWSリソース
データ(GraphQL/REST)defineDataAWS AppSync + Amazon DynamoDB
認証defineAuthAmazon Cognito User Pool + Identity Pool
サーバーレス関数defineFunctionAWS Lambda
ファイルストレージdefineStorageAmazon S3
フロントエンドホスティングAmplify HostingCloudFront + S3(静的)/ SSR対応

各コンポーネント間の参照(たとえばdataがauthのユーザーグループを参照する、functionがstorageバケットにアクセスする)も
TypeScriptの型安全な方法で記述できます。
Amplifyが自動生成するclient-configにより、フロントエンドはバックエンドのエンドポイントや認証情報を
ハードコードせずに利用できます。

AWS CDK 基盤が実現するカスタマイズ性

Amplify Gen 2の重要な特長は、バックエンドがAWS CDK L3 constructで実装されている点です。
Amplify標準のdefineDatadefineAuthなどのAPIで表現できない要件は、
backend.ts内でCDK constructを直接追加することで対応できます。
たとえばAmazon ElastiCache・Amazon SES・AWS WAFといったAmplify標準外のAWSサービスも、
CDK constructとしてbackend.tsに追記するだけで同一CloudFormationスタックで管理できます。
この設計により、Amplify Gen 2はシンプルなCRUDアプリから、
カスタムVPC・セキュリティグループ・専用Auroraクラスターを持つエンタープライズ規模のシステムまで、
同じcode-firstアプローチで扱えます。
CDK constructを活用することで、将来的なアーキテクチャ変更にも柔軟に対応できます。

per-developer sandbox による高速開発フィードバックサイクル

Amplify Gen 2の特徴的な機能の一つが、npx ampx sandboxコマンドによるper-developer専用クラウド環境です。
各開発者が独立したAWSクラウド環境を持てるため、他の開発者の変更に影響されず、本番データを汚染するリスクもありません。
sandboxはホットリロードに対応しており、TypeScriptファイルを変更するとバックエンドが自動的に更新されます。
Gen 1比で最大8倍高速なデプロイが可能で、フロントエンド開発のような素早いフィードバックサイクルを実現します。
sandbox環境はampx sandbox deleteコマンドで削除でき、作成されたAWSリソースもすべて片付けられます。
不要なコスト発生を防ぎ、開発完了後はクリーンな環境を保てる点も実運用で重要です。
sandboxは本番環境とは完全に分離されており、本番デプロイにはGitブランチと連携したAmplify Hostingを使用します(§6で詳述)。

東京リージョン(ap-northeast-1)対応

Amplify Gen 2はAWS CDK基盤のため、バックエンドリソースはAWSプロファイルで設定したリージョンへ展開できます。
東京リージョン(ap-northeast-1)では、AppSync・DynamoDB・Cognito・Lambda・S3がすべて利用可能であり、
日本国内へのデプロイをそのまま実施できます。
Amplify Hostingのフロントエンドは、独自ドメインとCloudFront CDNによって世界中にコンテンツを配信します。
東京リージョンを指定するには、AWSプロファイルでregion = ap-northeast-1を設定するか、
CI/CD環境変数でAWS_REGION=ap-northeast-1を指定します。
料金は構成するAWSサービスごとの従量課金であり、詳細は各サービスの公式料金ページを参照してください。

Gen 1 と Gen 2 の違い(移行検討ポイント)

Gen 1(〜2024年)はCLI対話式・JSON/YAMLファイル中心のアプローチでした。
amplify add authのようなCLIコマンドで対話的にリソースを追加し、
生成された設定はCloudFormationテンプレートとしてamplify/backend/以下に書き出される構造です。
この方式ではTypeScriptの型チェックが効かず、手動で保守するJSONの肥大化も課題でした。

項目Gen 1Gen 2
定義方式CLI対話 + JSON/CFnテンプレートTypeScript code-first
基盤Amplify独自ビルドシステムAWS CDK L3 construct
開発環境Amplify mock(一部のみ)per-developer sandbox(完全クラウド分離)
カスタマイズOverride機能(制限あり)CDK constructを直接利用可能
型安全なしTypeScript完全対応
EOL予定2027年5月1日現行バージョン

Gen 1は2027年5月1日をもってEOL(End of Life)となりメンテナンスモードへ移行する予定です。
AWSは公式の移行ツールとドキュメントを提供しており、Gen 1からGen 2への移行は段階的に進められます。
新規プロジェクトは必ずGen 2で開始し、既存のGen 1プロジェクトは計画的にGen 2へ移行することをお勧めします。

本シリーズVol1が扱うトピック(AWS Amplify Gen 2)

  • プロジェクト初期化とper-developer sandbox(ampx sandbox)・AWS CDK基盤(§2)
  • data — defineDataによるAppSync GraphQL/DynamoDBとスキーマ・認可ルール(§3)
  • auth — defineAuthによるAmazon Cognito統合・ソーシャルログイン・MFA(§4)
  • functions/storage — defineFunction(Lambda)とdefineStorage(Amazon S3)(§5)
  • デプロイ・CI/CD・環境戦略とAmplify Hosting(§6)
  • 本番運用 — 監視・コスト・東京リージョン・カスタムCDKリソース拡張(§7)
既存記事との棲み分け

  • 本記事はフルスタックTypeScript開発基盤としてのAmplify Gen 2を扱います。Amplify Hosting単体のCI/CD断片(CodePipeline連携・SAM統合など)はDevOps/CI/CD実践 Vol2で扱っています。
  • 認証/IDの深掘り(User Pools・passkeys・Identity Pools・外部IdP)はAmazon Cognito本番運用 Vol1を参照してください。本記事はAmplify文脈でのCognito統合に絞ります。
  • メッセージング/API統合(SQS・SNS・EventBridge・AppSync単体)はApplication Integration Vol1、Lambda/API Gateway詳細はサーバーレス本番運用を参照してください。

本記事を読み終えることで、Amplify Gen 2のTypeScript code-first定義のしくみを理解できます。
data・auth・functions・storageの各バックエンドコンポーネントの定義方法を把握し、
Amplify Hostingを使ったフルスタックデプロイのCI/CD戦略と本番運用の基礎を習得できます。
また、既存のAWSサービス専編記事との役割分担が明確になり、追加調査が必要な場面でどの記事を参照するかをすぐに判断できます。

2. プロジェクト初期化とper-developer sandbox

fig02: per-developer sandboxの開発フロー(各開発者が分離されたクラウド開発環境でampx sandboxを起動し、ホットリロードで反復、本番はブランチデプロイへ昇格する流れ)
fig02: per-developer sandbox — 分離されたクラウド開発環境と反復フロー

AWS Amplify Gen 2(以下 Gen 2)は、2024年5月にGA(一般提供)となった新しい開発者体験です。従来の Amplify Gen 1 が CLI 対話・JSON 設定を中心としていたのに対し、Gen 2 は TypeScript による code-first でバックエンドリソースを定義し、AWS CDK(L3 construct) 経由で CloudFormation スタックとして各 AWS サービスへ展開する設計になっています。Gen 1 は 2027 年 5 月 1 日にサポート終了(EOL)予定でメンテナンスモードに入っており、新規プロジェクトでは Gen 2 の採用を推奨します。

想定読者と前提条件

本記事 Vol1 の想定読者は次の方々です。

  • AWS の基本サービス(IAM・CloudFormation)を理解している中上級者
  • TypeScript / Node.js の開発経験がある方
  • フルスタック Web アプリをクラウドネイティブに構築・運用したい方

作業を始める前に、以下の前提条件を満たしていることを確認してください。

項目要件
Node.js18.x 以上(推奨: 20.x LTS)
npm10.x 以上
AWS アカウントアクティブなアカウント
IAM 権限CloudFormation・AppSync・DynamoDB・Cognito・Lambda・S3 への権限(AdministratorAccess 推奨)
AWS CLI設定済み(aws configure 実行済み または環境変数でクレデンシャル設定済み)

IAM 権限は Amplify が CloudFormation スタックを作成・更新・削除するために必要です。本番環境では最小権限原則に従い、必要なサービスへの権限のみを付与したカスタムポリシーを作成することを推奨します。

Amplify Gen 2 のアーキテクチャ概要

Gen 2 のバックエンドは @aws-amplify/backend パッケージが提供する L3 CDK construct を核としています。開発者は amplify/ ディレクトリに TypeScript ファイルを配置するだけで、各 AWS サービスのリソース定義を記述できます。実際のプロビジョニングは CDK → CloudFormation が担うため、個々のサービス API を直接呼び出す必要はありません。

Gen 2 が提供する主な define 系 API は次のとおりです。

関数対応 AWS サービス配置ファイル例
defineDataAWS AppSync + Amazon DynamoDBamplify/data/resource.ts
defineAuthAmazon Cognito User Poolamplify/auth/resource.ts
defineFunctionAWS Lambdaamplify/functions/*/resource.ts
defineStorageAmazon S3amplify/storage/resource.ts

これらを amplify/backend.ts でまとめて defineBackend に渡すことで、1 つの CloudFormation スタックとして一元管理されます。各 define 関数はリソースの設定を TypeScript の型安全な API で記述でき、エディタの IntelliSense によるオートコンプリートとコンパイル時の型検証が有効になります。

プロジェクトの初期化

新規プロジェクトは create-amplify パッケージで作成します。既存の React / Next.js / Vue プロジェクトへの追加も可能です。

npm create amplify@latest

実行すると対話プロンプトが表示され、プロジェクト名と使用するフロントエンドフレームワークを選択します。完了後、以下のディレクトリ構造が生成されます。

my-app/
├── amplify/
│├── auth/
││└── resource.ts  # defineAuth の定義
│├── data/
││└── resource.ts  # defineData の定義
│├── backend.ts # defineBackend でリソースを集約
│└── tsconfig.json # amplify/ ディレクトリ専用の TypeScript 設定
├── src/  # フロントエンドソース
├── package.json
└── ...

amplify/backend.ts の基本構成は次のようになります。

import { defineBackend } from '@aws-amplify/backend';
import { auth } from './auth/resource';
import { data } from './data/resource';

defineBackend({
  auth,
  data,
});

defineBackend に渡したリソースが 1 つの CloudFormation スタックとして管理されます。新しいリソース(functions・storage など)を追加する場合は、対応する resource.ts を作成して defineBackend に追加するだけです。

per-developer sandbox の仕組みと起動方法

Gen 2 最大の特徴が per-developer sandbox です。各開発者が専用の分離されたクラウド環境を持ち、ampx sandbox コマンドで起動します。

npx ampx sandbox

実行すると、AWS アカウントに名前付きの CloudFormation スタック(例: amplify-<app>-<username>-sandbox)が作成され、amplify/backend.ts に定義したすべてのリソースがプロビジョニングされます。

per-developer sandbox には次の 3 つの大きなメリットがあります。

① 開発者間の干渉がない

各開発者が独立したスタックを持つため、他メンバーの変更が自分の環境に影響しません。チームで並行開発するときに、ブランチ間でデータ状態が混在するリスクを排除できます。フロントエンドエンジニアが認証フローを試している間、バックエンドエンジニアが API スキーマを変更しても、それぞれの sandbox が独立しているため互いに干渉しません。

② ホットリロードによる高速反復開発

ampx sandbox はデフォルトでファイル変更を監視します。amplify/ 配下のファイルが更新されると自動的に差分デプロイが実行されます。Gen 1 の amplify push と比較して最大 8 倍高速なデプロイが可能で、スキーマ変更や Auth 設定の修正をリアルタイムで確認しながら開発を進められます。

③ 終了時の自動クリーンアップ

Ctrl+C で sandbox を停止・削除すると、作成されたすべての AWS リソースが削除されます。開発用リソースが本番環境に残留してコスト超過や情報漏洩リスクになる問題を防げます。削除を省略したい場合は --no-delete オプションを使用できます。

sandbox の管理コマンドと amplify_outputs.json

sandbox が起動すると、フロントエンドアプリが参照する amplify_outputs.json が自動生成されます。このファイルには AppSync エンドポイント・Cognito User Pool ID など、フロントエンドが接続するために必要な設定がすべて含まれます。

# 通常起動(ウォッチモード)
npx ampx sandbox

# 別名の sandbox を起動(複数機能を並行テストする場合)
npx ampx sandbox --identifier feature-branch

# sandbox を削除せずに停止(リソースを維持したい場合)
npx ampx sandbox --no-delete

# sandbox のリソース一覧を確認
npx ampx sandbox status

フロントエンドでは amplify_outputs.json を読み込んで Amplify.configure() を呼び出します。

import { Amplify } from 'aws-amplify';
import outputs from '../amplify_outputs.json';

Amplify.configure(outputs);

amplify_outputs.json には sandbox 固有の設定情報が含まれるため、.gitignore に追加して Git 管理から除外することを推奨します。開発者ごとに異なる設定が生成されるため、コミットすると他の開発者の環境設定を上書きするリスクがあります。

AWS CDK 基盤の活用とカスタムリソース

Gen 2 は内部で AWS CDK を使用しているため、Amplify が標準で提供しないサービスを CDK construct として追加できます。defineBackend の戻り値から Stack オブジェクトにアクセスできます。

import { defineBackend } from '@aws-amplify/backend';
import { auth } from './auth/resource';
import { data } from './data/resource';
import { Queue } from 'aws-cdk-lib/aws-sqs';

const backend = defineBackend({ auth, data });

// カスタム CDK リソースを追加
const customStack = backend.createStack('custom-resources');
new Queue(customStack, 'MyQueue', {
  queueName: 'amplify-gen2-queue',
});

この仕組みにより、Amplify の抽象化レイヤーを維持しながら、SQS・SNS・Step Functions など任意の AWS サービスをバックエンドスタックに組み込めます。カスタムリソースの詳細は §7 で解説します。

Gen 1 と Gen 2 の主な違い

Gen 1 の経験者が Gen 2 を学ぶ際、または Gen 1 からの移行を検討する際に把握しておくべき主な違いは次のとおりです。

項目Gen 1Gen 2
設定方法CLI 対話・JSON/YAMLTypeScript code-first
デプロイコマンドamplify pushnpx ampx sandbox / npx ampx pipeline-deploy
IaC 基盤CloudFormation テンプレート自動生成AWS CDK(L3 construct)経由
開発環境共有環境または手動ブランチ管理per-developer sandbox(自動分離)
設定の正本JSON 設定ファイルTypeScript ソースコード
EOL2027年5月1日(メンテナンスモード)現行バージョン(継続開発)

AWS は Gen 1 から Gen 2 への移行ツールを提供しています。既存の Gen 1 プロジェクトを Gen 2 に移行する場合は、まず sandbox 環境で動作確認を行い、その後本番環境に適用することを推奨します。

開発ワークフローの全体像

Gen 2 での典型的な開発サイクルは次のフローで進みます。

1. プロジェクト初期化
npm create amplify@latest
cd my-app && npm install

2. 開発者 sandbox 起動(ウォッチモード)
npx ampx sandbox
→ amplify_outputs.json が自動生成される

3. フロントエンド開発(別ターミナル)
npm run dev
→ amplify_outputs.json を読み込んだフロントエンドが起動

4. バックエンド変更
amplify/data/resource.ts を編集
→ ampx sandbox がホットリロードで差分デプロイ

5. 動作確認 → コードをコミット

6. プルリクエスト → Amplify Hosting が自動デプロイ
npx ampx pipeline-deploy --branch main --app-id <app-id>

package.json にスクリプトを登録しておくと便利です。

{
  "scripts": {
 "dev": "vite",
 "sandbox": "ampx sandbox",
 "sandbox:reset": "ampx sandbox delete && ampx sandbox",
 "deploy": "ampx pipeline-deploy"
  }
}

amplify/ ディレクトリの TypeScript 設定

amplify/tsconfig.jsonamplify/ ディレクトリ専用の TypeScript 設定です。ルートの tsconfig.json とは分離されており、Node.js 環境用の設定(module: "node16"moduleResolution: "node16")が適用されます。フロントエンドの TypeScript 設定と混同しないよう注意してください。

{
  "compilerOptions": {
 "target": "es2022",
 "module": "es2022",
 "moduleResolution": "bundler",
 "resolveJsonModule": true,
 "esModuleInterop": true,
 "isolatedModules": true,
 "strict": true
  },
  "include": ["./**/*.ts"]
}

amplify/ 内の TypeScript はフロントエンドコードとは別にトランスパイルされます。フロントエンド用の型定義(amplify_outputs.json の型など)は amplify/ から直接 import せず、amplify_outputs.json を通じて間接参照する設計になっています。

Vol1 では §3 以降で defineDatadefineAuthdefineFunctiondefineStorage の各リソース定義を詳しく解説します。まず本セクションの手順で sandbox を起動し、amplify_outputs.json が生成されることを確認してから次のセクションへ進んでください。

3. data — defineDataとAppSync GraphQL/DynamoDB

fig03: defineDataのデータフロー(TypeScriptスキーマ定義→AWS AppSync GraphQL API→Amazon DynamoDBテーブル、型付きデータクライアントとリアルタイムsubscriptionの関係)
fig03: defineData — スキーマ定義からAppSync/DynamoDBへの型付きデータフロー

3-1. defineDataの基本構造とスキーマ定義

AWS Amplify Gen 2におけるdefineDataは、TypeScriptコードでアプリケーションのデータモデルを定義し、AWS AppSync GraphQL APIとAmazon DynamoDBテーブルを自動的にプロビジョニングします。amplify/data/resource.tsにスキーマを記述するだけで、ボイラープレートコードなしに型安全なフルスタックデータレイヤーが完成します。

// amplify/data/resource.ts
import { type ClientSchema, a, defineData } from '@aws-amplify/backend';

const schema = a.schema({
  Todo: a
 .model({
content: a.string(),
isDone: a.boolean().default(false),
priority: a.enum(['LOW', 'MEDIUM', 'HIGH']),
createdAt: a.datetime(),
 })
 .authorization((allow) => [
allow.owner(),
allow.authenticated().to(['read']),
 ]),

  BlogPost: a
 .model({
title: a.string().required(),
body: a.string(),
authorId: a.string(),
tags: a.string().array(),
publishedAt: a.datetime(),
 })
 .authorization((allow) => [
allow.groups(['Admin', 'Editor']).to(['create', 'update', 'delete']),
allow.authenticated().to(['read']),
allow.publicApiKey().to(['read']),
 ]),

  Comment: a
 .model({
content: a.string().required(),
postId: a.id().required(),
 })
 .authorization((allow) => [
allow.owner(),
allow.authenticated().to(['read']),
 ]),
});

export type Schema = ClientSchema<typeof schema>;

export const data = defineData({
  schema,
  authorizationModes: {
 defaultAuthorizationMode: 'userPool',
 apiKeyAuthorizationMode: {
expiresInDays: 30,
 },
  },
});

このスキーマ定義により、Amplifyは以下のAWSリソースを自動生成します。

  • AWS AppSync GraphQL API: クエリ・ミューテーション・サブスクリプションエンドポイントを自動生成します
  • Amazon DynamoDB テーブル: モデルごとに個別テーブルを作成し、オンデマンドキャパシティで運用開始します
  • JavaScript リゾルバ: カスタムビジネスロジックのためのリゾルバをAppSyncに自動配置します

3-2. 型付きデータクライアントとIntelliSense

defineDataの最大の特長は、TypeScriptの型定義が自動的にフロントエンドのデータクライアントに反映される点です。generateClient<Schema>()で生成したクライアントは完全な型情報を持ち、IDE上でのIntelliSenseによるコード補完と型検証が利用できます。

// src/app/page.tsx(Next.jsの例)
import { generateClient } from 'aws-amplify/data';
import type { Schema } from '@/amplify/data/resource';

const client = generateClient<Schema>();

// 型安全なCRUD操作
async function createTodo(content: string) {
  const { data: todo, errors } = await client.models.Todo.create({
 content,
 isDone: false,
 priority: 'MEDIUM',
  });
  if (errors) {
 console.error('作成エラー:', errors);
 return null;
  }
  return todo; // Schema['Todo']['type'] として型推論されます
}

async function listTodos() {
  const { data: todos } = await client.models.Todo.list({
 filter: {
isDone: { eq: false },
 },
 limit: 20,
 sortDirection: 'DESC',
  });
  return todos;
}

async function updateTodo(id: string, isDone: boolean) {
  const { data: updated } = await client.models.Todo.update({ id, isDone });
  return updated;
}

コンパイル時に型チェックが走るため、存在しないフィールドへのアクセスや型の不一致はビルド段階で検出されます。Gen 1時代のAPI.graphql()呼び出しと比較して、型安全性が格段に向上しています。

3-3. リアルタイムサブスクリプション

AppSyncのサブスクリプション機能をデータクライアント経由で簡単に利用できます。WebSocket接続が自動管理されるため、リアルタイムコラボレーションやライブ更新が少ないコードで実現できます。

import { useState, useEffect } from 'react';
import { generateClient } from 'aws-amplify/data';
import type { Schema } from '@/amplify/data/resource';

const client = generateClient<Schema>();

function TodoList() {
  const [todos, setTodos] = useState<Schema['Todo']['type'][]>([]);

  useEffect(() => {
 // 初期データ取得
 client.models.Todo.list().then(({ data }) => setTodos(data));

 // リアルタイム更新をサブスクライブ
 const createSub = client.models.Todo.onCreate().subscribe({
next: (todo) => setTodos((prev) => [todo, ...prev]),
error: (err) => console.error('サブスクリプションエラー:', err),
 });

 const updateSub = client.models.Todo.onUpdate().subscribe({
next: (updated) =>
  setTodos((prev) =>
 prev.map((t) => (t.id === updated.id ? updated : t))
  ),
 });

 const deleteSub = client.models.Todo.onDelete().subscribe({
next: (deleted) =>
  setTodos((prev) => prev.filter((t) => t.id !== deleted.id)),
 });

 return () => {
createSub.unsubscribe();
updateSub.unsubscribe();
deleteSub.unsubscribe();
 };
  }, []);

  return (
 <ul>
{todos.map((todo) => (
  <li key={todo.id}>{todo.content}</li>
))}
 </ul>
  );
}

サブスクリプションはデフォルトで認証済みユーザーのデータのみ受信します。allow.owner()ルール下では自分が作成したレコードの変更だけを受信するため、他ユーザーのデータが意図せず流れ込む心配はありません。

3-4. 認可ルール詳解

Amplify Gen 2の認可システムは、モデルレベルとフィールドレベルの2段階で設定できます。主要な認可プロバイダーを以下に整理します。

allow.owner() — オーナーベース認可

レコードを作成したユーザーのみが全操作できる最も一般的なパターンです。CognitoのサブジェクトIDがownerフィールドに自動設定され、AppSyncがサーバーサイドで所有権を検証します。

UserProfile: a
  .model({
 displayName: a.string(),
 bio: a.string(),
  })
  .authorization((allow) => [
 allow.owner(),
  ]),

allow.authenticated() — 認証ユーザー認可

Cognitoで認証済みのすべてのユーザーにアクセスを許可します。ブログ記事一覧など、ログイン必須だが所有者限定でない場面に使います。

BlogPost: a
  .model({
 title: a.string().required(),
 body: a.string(),
  })
  .authorization((allow) => [
 allow.owner().to(['create', 'update', 'delete']),
 allow.authenticated().to(['read']),
  ]),

allow.publicApiKey() — 未認証アクセス(APIキー)

APIキーを使った未認証アクセスを許可します。公開コンテンツの表示に用います。APIキーの有効期限はapiKeyAuthorizationMode.expiresInDaysで設定し、定期的なローテーションが必要です。

PublicArticle: a
  .model({
 title: a.string().required(),
 content: a.string(),
  })
  .authorization((allow) => [
 allow.publicApiKey().to(['read']),
 allow.groups(['Admin']).to(['create', 'update', 'delete']),
  ]),

allow.groups() — グループベース認可

Cognito User Poolのグループ(例: AdminEditor)に基づいた認可です。ロール管理が明確な業務アプリに適しています。グループメンバーシップはJWTのcognito:groupsクレームに含まれ、AppSyncが自動的に認可判断に使います。

.authorization((allow) => [
  allow.groups(['Admin']).to(['create', 'read', 'update', 'delete']),
  allow.groups(['Editor']).to(['create', 'read', 'update']),
  allow.authenticated().to(['read']),
]),

3-5. Lambda関数からのデータアクセス — allow.resource

defineFunctionで定義したLambda関数にデータアクセスを付与する場合はallow.resource()を使います。Lambda関数はIAMロールを通じてAppSync APIを安全に呼び出せます。

// amplify/data/resource.ts
import { myFunction } from '../functions/my-function/resource';

const schema = a.schema({
  Order: a
 .model({
userId: a.string(),
amount: a.float(),
status: a.enum(['PENDING', 'PROCESSING', 'SHIPPED', 'DELIVERED']),
 })
 .authorization((allow) => [
allow.owner(),
allow.resource(myFunction).to(['read', 'update']),
 ]),
});

AppSyncのVTLテンプレートや組み込みリゾルバでは対応しきれない複雑なビジネスロジックを、Lambda関数として安全に実装できます。Lambda側では環境変数に設定されたAppSyncエンドポイントに対してIAM署名付きリクエストを送信します。

3-6. カスタムクエリ・ミューテーション

AppSyncの自動生成APIに加え、a.query()a.mutation()でカスタムオペレーションを定義できます。

const schema = a.schema({
  generateReport: a
 .query()
 .arguments({
startDate: a.datetime().required(),
endDate: a.datetime().required(),
 })
 .returns(a.customType({
totalRevenue: a.float(),
orderCount: a.integer(),
 }))
 .authorization((allow) => [allow.groups(['Admin'])])
 .handler(a.handler.function(generateReportFunction)),

  sendNotification: a
 .mutation()
 .arguments({
userId: a.string().required(),
message: a.string().required(),
 })
 .returns(a.boolean())
 .authorization((allow) => [allow.authenticated()])
 .handler(a.handler.function(notificationFunction)),
});

カスタムハンドラにはLambda関数(a.handler.function())またはAppSyncのJavaScriptリゾルバ(a.handler.custom())を指定できます。JavaScriptリゾルバはコールドスタートがなく、DynamoDBへの直接アクセスを高速に実行できます。

3-7. インデックス設計とベストプラクティス

本番環境でのデータモデル設計では、DynamoDBのアクセスパターンに合わせてGSI(グローバルセカンダリインデックス)を設定します。クエリのフィルタリングはスキャンではなく、GSIを活用した効率的なアクセスを基本とします。

BlogPost: a
  .model({
 authorId: a.string().required(),
 categoryId: a.string(),
 publishedAt: a.datetime(),
 title: a.string().required(),
  })
  .secondaryIndexes((index) => [
 index('authorId').sortKeys(['publishedAt']),
 index('categoryId').sortKeys(['publishedAt']),
  ])
  .authorization((allow) => [
 allow.publicApiKey().to(['read']),
 allow.owner().to(['create', 'update', 'delete']),
  ]),

AppSyncはリクエスト数とリアルタイム接続数、DynamoDBはリクエストユニット消費量とストレージ容量に基づいて課金されます。詳細はAWS AppSync公式料金ページを参照してください。開発sandboxでコストが積み上がらないよう、不要になったsandboxはampx sandbox deleteで削除することをお勧めします。

4. auth — defineAuthとAmazon Cognito統合

fig04: defineAuthとCognito連携(メール/ソーシャルプロバイダログイン、MFA、カスタム属性を宣言するとAmazon Cognito User Poolが自AWSアカウントに展開される構成)
fig04: defineAuth — Amazon Cognitoによる認証とソーシャルログイン/MFA

4.1 defineAuthの基本設計思想

AWS Amplify Gen 2における認証機能は、amplify/auth/resource.tsに記述したdefineAuthによって宣言的に定義します。コードを保存するとampx sandboxがAmazon Cognito User Poolをお客様のAWSアカウントに自動展開するため、Cognitoコンソールを直接操作する必要はありません。Gen 1のCLI対話形式と比較して、設定の全量をTypeScriptで管理でき、Pull Requestレビューで変更差分を明確化できます。

defineAuthの最小構成は以下のとおりです。

// amplify/auth/resource.ts
import { defineAuth } from '@aws-amplify/backend';

export const auth = defineAuth({
  loginWith: {
 email: true,
  },
});

この1ファイルを追加するだけで、AmplifyはメールアドレスによるパスワードログインができるCognito User Poolをデプロイします。amplify/backend.tsauthを追加することでdataやfunctionsと統合されます。

// amplify/backend.ts
import { defineBackend } from '@aws-amplify/backend';
import { auth } from './auth/resource';
import { data } from './data/resource';

export const backend = defineBackend({
  auth,
  data,
});

defineAuthが展開するCognito User Poolはお客様のAWSアカウントに所属するため、Cognitoコンソールから直接確認・管理できます。ただし、コンソールで直接変更した設定は次回のAmplifyデプロイで上書きされる可能性があるため、設定変更は必ずresource.tsファイルを通じて行ってください。

4.2 ログイン手段の設定

4.2.1 メール認証

最も基本的なログイン手段はメールアドレスによる認証です。loginWith.email: trueを設定すると、Amplifyはメール検証フローを自動構成します。ユーザー登録時に確認コードが送信され、コード入力によってメールアドレスの所有を確認します。

確認メールの文面をカスタマイズする場合は以下のように記述します。

export const auth = defineAuth({
  loginWith: {
 email: {
verificationEmailStyle: 'CODE',
verificationEmailSubject: 'アカウント確認コード',
verificationEmailBody: (createCode) =>
  `確認コード: ${createCode()}`,
 },
  },
});

verificationEmailStyleには'CODE'(数字コード)と'LINK'(確認リンク)を指定できます。本番環境でSES経由のカスタムドメインからメールを送信する場合は、Cognito User PoolのEmail configurationでSES ARNを指定してください。

4.2.2 外部プロバイダー(ソーシャルログイン)

Amplify Gen 2では、GoogleやApple、Facebookなどの外部プロバイダーを使ったソーシャルログインを宣言的に設定できます。外部プロバイダーのOAuth 2.0フローはAmplifyが管理するCognito User Poolのホストされた認証UIを通じて動作します。

import { defineAuth, secret } from '@aws-amplify/backend';

export const auth = defineAuth({
  loginWith: {
 email: true,
 externalProviders: {
google: {
  clientId: secret('GOOGLE_CLIENT_ID'),
  clientSecret: secret('GOOGLE_CLIENT_SECRET'),
  scopes: ['email', 'profile', 'openid'],
  attributeMapping: {
 email: 'email',
 fullname: 'name',
  },
},
apple: {
  clientId: secret('APPLE_CLIENT_ID'),
  keyId: secret('APPLE_KEY_ID'),
  privateKey: secret('APPLE_PRIVATE_KEY'),
  teamId: secret('APPLE_TEAM_ID'),
  scopes: ['email', 'name'],
},
callbackUrls: [
  'http://localhost:3000/auth/callback',
  'https://your-production-domain.com/auth/callback',
],
logoutUrls: [
  'http://localhost:3000/',
  'https://your-production-domain.com/',
],
 },
  },
});

secret()関数はAmplify Secretsを参照し、APIキーをコードに平文で埋め込まずに済みます。Secretsはampx sandbox secret set <KEY_NAME>コマンドで登録します。ソーシャルログインを使う場合、Cognitoドメイン(ホストされた認証UI)のコールバックURLを外部プロバイダーのダッシュボードに登録する作業が別途必要です。ampx sandboxでは起動時に出力されるURLを確認してください。

Facebook連携を追加する場合も同様の構造で設定できます。

facebook: {
  clientId: secret('FACEBOOK_CLIENT_ID'),
  clientSecret: secret('FACEBOOK_CLIENT_SECRET'),
  scopes: ['email', 'public_profile'],
},

4.2.3 電話番号認証

メール認証と同様に、電話番号をログイン識別子に使用できます。

export const auth = defineAuth({
  loginWith: {
 phone: true,
  },
});

メールと電話番号の両方を有効にする場合は両方をtrueにします。Cognitoの仕様上ユーザーは登録時に選択した識別子でログインするため、UIでの識別子選択フローを適切に設計してください。電話番号認証を使う場合はAmazon SNSのSMS送信設定が必要です。

4.3 多要素認証(MFA)の設定

MFAはmultifactorプロパティで設定します。'OPTIONAL'(任意)または'REQUIRED'(強制)を選択できます。'REQUIRED'を選択すると全ユーザーがMFAを設定しなければログインできなくなります。本番環境では'OPTIONAL'から開始し、セキュリティポリシーに応じて'REQUIRED'に移行するアプローチが一般的です。

export const auth = defineAuth({
  loginWith: {
 email: true,
  },
  multifactor: {
 mode: 'OPTIONAL',
 totp: true,
 sms: true,
  },
});

MFAの方式としてTOTP(時間ベースのワンタイムパスワード、Google Authenticator等)とSMS(テキストメッセージ)の2種類をサポートしています。TOTPはSMSと比較してSIMスワップ攻撃のリスクが低いため、セキュリティ要件が高い場合はTOTPを推奨します。SMSを使用する場合はAmazon SNSへのアクセス許可が必要で、場合によってはAmazon SNS Sandboxからの昇格が必要です。

AmplifyのフロントエンドライブラリおよびAmplify UI(@aws-amplify/ui-react)は、MFAセットアップ画面を自動的に表示します。Authenticatorコンポーネントを使うと、TOTPセットアップのQRコード表示・確認コード入力のステップがライブラリによって自動処理されます。独自UIを構築する場合は、Amplify Auth APIのsetUpTOTP()verifyTOTPSetup()を呼び出してカスタム実装できます。

4.4 カスタム属性の設定

標準のCognito属性(email、name等)に加えて、アプリケーション固有のユーザー属性を定義できます。

export const auth = defineAuth({
  loginWith: {
 email: true,
  },
  userAttributes: {
 preferredUsername: {
mutable: true,
required: false,
 },
 'custom:plan': {
dataType: 'String',
mutable: true,
 },
 'custom:organizationId': {
dataType: 'String',
mutable: false,
 },
  },
});

カスタム属性は名前の先頭にcustom:プレフィックスが必要です。mutable: falseにすると登録後に変更不可となります。Cognito User Poolのカスタム属性はデプロイ後に削除できないため、必要な属性のみを慎重に設計してからデプロイしてください。

カスタム属性をフロントエンドから取得・更新するには、Amplify Auth APIのfetchUserAttributes()updateUserAttributes()を使用します。

import { fetchUserAttributes, updateUserAttributes } from 'aws-amplify/auth';

// カスタム属性を含む全属性を取得
const attributes = await fetchUserAttributes();
console.log(attributes['custom:plan']);

// カスタム属性を更新
await updateUserAttributes({
  userAttributes: {
 'custom:plan': 'premium',
  },
});

4.5 アクセス認可とdataとの連動

Amplify Gen 2の大きな特徴として、defineAuthdefineDataを連動させた認可設計があります。データスキーマの認可ルールでallow.authenticated()を使うと、「ログインしているユーザーのみアクセス可能」というルールをTypeScriptで宣言できます。

// amplify/data/resource.ts
const schema = a.schema({
  Post: a.model({
 title: a.string().required(),
 content: a.string(),
 authorId: a.string(),
  })
  .authorization((allow) => [
 allow.owner(),
 allow.authenticated().to(['read']),
  ]),
});

allow.owner()はJWTのサブ(subject)をownerIdとして保存し、そのユーザーのみが作成・更新・削除できます。allow.authenticated()はCognitoでログイン済みの全ユーザーにread権限を与えます。認可ルールの組み合わせは複数設定でき、リストの順番に評価されます。

未ログインユーザーにも読み取りを許可する場合はallow.guest()を追加します。この場合、defineDataauthorizationModesにAPIキーを設定する必要があります。

.authorization((allow) => [
  allow.owner(),
  allow.authenticated().to(['read']),
  allow.guest().to(['read']),
])

4.5.1 グループベースの認可

Cognito User Poolのグループを使った認可も設定できます。

.authorization((allow) => [
  allow.groups(['admin']).to(['create', 'read', 'update', 'delete']),
  allow.authenticated().to(['read']),
])

グループはCognitoコンソールまたはAmplify Admin UIで管理できます。サインアップ時に自動的にグループに追加するにはPost Confirmationトリガー(Lambda)を使います。グループメンバーシップはJWTトークンにcognito:groupsクレームとして含まれ、AppSyncが自動的に認可判断に使用します。

4.6 Lambda Triggerの統合

defineAuthではCognito User Poolのトリガーとして動作するLambda関数を登録できます。

import { defineAuth, defineFunction } from '@aws-amplify/backend';

export const auth = defineAuth({
  loginWith: {
 email: true,
  },
  triggers: {
 postConfirmation: defineFunction({
entry: './post-confirmation-handler.ts',
 }),
 preAuthentication: defineFunction({
entry: './pre-auth-handler.ts',
 }),
  },
});

よく使われるトリガーとその用途は以下のとおりです。

トリガー用途
preSignUpサインアップ前のバリデーション・自動承認
postConfirmation確認後のDynamoDB初期データ作成・グループ追加
preAuthenticationログイン前の追加チェック(ブロックリスト等)
postAuthenticationログイン後の監査ログ記録
customMessage確認メール・SMS本文のカスタマイズ

パスワードレス認証(カスタム認証フロー)を実装する場合はdefineChallengecreateAuthChallengeverifyAuthChallengeResponseトリガーを組み合わせます。これらのトリガーもすべてdefineFunctionで定義し、TypeScriptで型安全に実装できます。

4.7 本番運用でのauth設計ガイドライン

4.7.1 Cognito専編との棲み分け

本セクション(§4)は、Amplify Gen 2フルスタック開発の文脈でのauth統合に絞っています。Amazon Cognitoの深掘りトピック(passkeyによるパスワードレス認証、Identity Poolsによるフェデレーションアクセス、アドバンスドセキュリティ機能の詳細設定)については関連記事を参照してください。

4.7.2 サンドボックス環境と本番環境の設定差異

ampx sandboxでは各開発者が独立したCognito User Poolを持つため、開発中の設定変更がチームメンバーに影響しません。ただし、サンドボックスのUser Pool設定と本番ブランチの設定が乖離しないよう、amplify/auth/resource.tsの変更はPull Requestでチーム全員がレビューする運用を推奨します。本番ブランチへのマージ時にAmplifyがCloudFormationスタックを更新します。

4.7.3 パスワードポリシーのカスタマイズ

export const auth = defineAuth({
  loginWith: {
 email: true,
  },
  passwordPolicy: {
 minLength: 12,
 requireDigits: true,
 requireLowercase: true,
 requireUppercase: true,
 requireSymbols: true,
  },
});

デフォルトのパスワードポリシー(8文字以上・大小英数記号混在)はほとんどの用途に適しています。金融・医療など高セキュリティ要件では最小文字数を12文字以上に設定してください。パスワードポリシーはUser Pool作成後に変更できないため、初期設計が重要です。

4.7.4 アカウント復旧手段の設定

accountRecoveryプロパティでアカウント復旧の手段を制御します。

export const auth = defineAuth({
  loginWith: {
 email: true,
  },
  accountRecovery: 'EMAIL_ONLY',
});

SMSによる復旧('PHONE_WITHOUT_MFA')はSIMスワップ攻撃のリスクが高まるため、業務系アプリでは'EMAIL_ONLY'または'NONE'(管理者対応のみ)を検討してください。

4.7.5 コスト考慮

Amazon Cognitoの料金はMonthly Active Users(MAU)ベースです。Amplify Gen 2で展開したUser Poolも通常のCognito料金モデルが適用されます。無料枠の範囲と有償化のタイミングは公式料金ページで確認してください。ソーシャルログインのMAUは追加料金が発生するため、外部プロバイダーを有効化する際は事前に見積もりをしてください。

5. functions と storage — defineFunction(Lambda)/defineStorage(S3)

fig05: functionsとstorageの連携(defineFunctionによるTypeScript Lambda関数がdataのリゾルバやイベント処理を担い、defineStorageがAmazon S3バケットを構成しアクセス制御する構成)
fig05: defineFunction/defineStorage — Lambda関数とAmazon S3ストレージ

5-1. defineFunction — TypeScript製Lambda関数の作成

AWS Amplify Gen 2では defineFunction を使うことで、TypeScript製のAWS Lambda関数をバックエンドリソースとして宣言的に定義できます。関数のコードはTypeScriptで記述し、Amplifyがビルド・デプロイ時に自動的にトランスパイルおよびパッケージングを行います。これにより、フロントエンドと同一のTypeScript型体系をサーバーサイドでも維持でき、型安全なフルスタック開発が実現します。

defineFunction を使った基本的な関数定義は以下のようになります。

// amplify/functions/myFunction/handler.ts
import type { Handler } from "aws-lambda";

export const handler: Handler = async (event) => {
  console.log("イベント受信:", JSON.stringify(event));
  return {
 statusCode: 200,
 body: JSON.stringify({ message: "Lambda関数が実行されました" }),
  };
};
// amplify/functions/myFunction/resource.ts
import { defineFunction } from "@aws-amplify/backend";

export const myFunction = defineFunction({
  name: "myFunction",
  entry: "./handler.ts",
  environment: {
 TABLE_NAME: "myTable",
  },
  timeoutSeconds: 30,
  memoryMB: 512,
});
// amplify/backend.ts
import { defineBackend } from "@aws-amplify/backend";
import { data } from "./data/resource";
import { auth } from "./auth/resource";
import { myFunction } from "./functions/myFunction/resource";

defineBackend({
  auth,
  data,
  myFunction,
});

defineFunction の引数では name(関数名)、entry(ハンドラファイルのパス)、environment(環境変数)、timeoutSeconds(タイムアウト秒数)、memoryMB(メモリサイズ)などを指定できます。これらのパラメータはTypeScriptで型チェックされるため、誤った値の入力をコンパイル時に検出できます。

5-2. dataスキーマとのハンドラ連携 — a.handler.function

Amplify Gen 2の defineData が提供するGraphQL APIでは、カスタムクエリ・カスタムミューテーション・カスタムサブスクリプションのリゾルバとしてLambda関数を指定できます。スキーマ定義で a.handler.function(myFunction) と記述するだけで、AppSyncがその関数をリゾルバとして呼び出す設定が自動的に構成されます。

// amplify/data/resource.ts
import { type ClientSchema, a, defineData } from "@aws-amplify/backend";
import { myFunction } from "../functions/myFunction/resource";

const schema = a.schema({
  // カスタムクエリ: Lambda関数をリゾルバとして指定
  generateSummary: a
 .query()
 .arguments({ content: a.string().required() })
 .returns(a.string())
 .authorization((allow) => [allow.authenticated()])
 .handler(a.handler.function(myFunction)),

  // 通常のモデル定義と組み合わせることも可能
  Post: a
 .model({
title: a.string().required(),
content: a.string(),
summary: a.string(),
 })
 .authorization((allow) => [allow.owner()]),
});

export type Schema = ClientSchema<typeof schema>;

export const data = defineData({
  schema,
  authorizationModes: {
 defaultAuthorizationMode: "userPool",
  },
});

Lambda関数側では、AppSyncからのリクエストを型安全に受け取るための型定義を活用できます。AppSyncのリゾルバイベントには arguments(スキーマで定義した引数)や identity(呼び出し元ユーザー情報)が含まれます。

// amplify/functions/myFunction/handler.ts
import type { AppSyncResolverHandler } from "aws-lambda";

type Arguments = {
  content: string;
};

export const handler: AppSyncResolverHandler<Arguments, string> = async (
  event
) => {
  const { content } = event.arguments;
  // コンテンツを処理して要約を返す
  const summary = content.slice(0, 100) + "...";
  return summary;
};

a.handler.function を使ったリゾルバ連携は、外部APIの呼び出し・複雑なビジネスロジック・AI/MLサービスとの統合など、AppSyncのVTLテンプレートや組み込みリゾルバでは対応しきれない処理に特に有効です。Lambda関数に対するIAMリソースアクセス権限は、defineBackend 内でCDKレイヤーを経由して付与できます。

5-3. イベント駆動統合 — EventBridge・SQS・DynamoDB Streams

defineFunction で定義したLambda関数はAppSyncのリゾルバとして使うだけでなく、イベント駆動のバックグラウンド処理にも活用できます。Amplify Gen 2ではCDK基盤を活用することで、EventBridge・SQS・DynamoDB Streamsなど多様なイベントソースとLambdaを接続できます。

// amplify/backend.ts(CDKレイヤーでイベントソース追加)
import { defineBackend } from "@aws-amplify/backend";
import { data } from "./data/resource";
import { auth } from "./auth/resource";
import { myFunction } from "./functions/myFunction/resource";
import * as events from "aws-cdk-lib/aws-events";
import * as targets from "aws-cdk-lib/aws-events-targets";

const backend = defineBackend({
  auth,
  data,
  myFunction,
});

// EventBridgeルールでLambdaをスケジュール実行(1時間ごと)
const eventStack = backend.createStack("EventStack");
const rule = new events.Rule(eventStack, "ScheduledRule", {
  schedule: events.Schedule.rate(cdk.Duration.hours(1)),
});
rule.addTarget(
  new targets.LambdaFunction(
 backend.myFunction.resources.lambda
  )
);

DynamoDB Streamsを使う場合は、data リソースが展開するDynamoDBテーブルのストリームARNを取得し、Lambda関数のイベントソースとして設定します。これにより、テーブルへの書き込みをトリガーとしてバックグラウンド処理を起動するパターンが実現します。

import * as lambda from "aws-cdk-lib/aws-lambda";
import * as lambdaEventSources from "aws-cdk-lib/aws-lambda-event-sources";
import * as dynamodb from "aws-cdk-lib/aws-dynamodb";

// DynamoDB Streamsイベントソースを追加
const postTable = backend.data.resources.tables["Post"];
backend.myFunction.resources.lambda.addEventSource(
  new lambdaEventSources.DynamoEventSource(postTable, {
 startingPosition: lambda.StartingPosition.LATEST,
 batchSize: 10,
  })
);

イベント駆動統合のユースケースとして、投稿作成後のAI要約生成・メール通知の非同期送信・検索インデックスの更新・監査ログの記録などが挙げられます。これらの処理をGraphQLリゾルバから切り離してバックグラウンド実行することで、フロントエンドへのレスポンスを高速化できます。

5-4. defineStorage — S3バケット構成とアクセスレベル制御

defineStorage はAmazon S3バケットをフルスタックアプリケーションのストレージ層として宣言的に定義するAPIです。バケットの作成・設定はAmplifyが管理し、開発者はアクセスレベル(認証ユーザー/ゲスト/オーナー)の定義に集中できます。

// amplify/storage/resource.ts
import { defineStorage } from "@aws-amplify/backend";

export const storage = defineStorage({
  name: "myProjectStorage",
  access: (allow) => ({
 // 認証済みユーザーは自分のフォルダに読み書き可能
 "profile-pictures/{entity_id}/*": [
allow.entity("identity").to(["read", "write", "delete"]),
 ],
 // 認証済みユーザーは共有フォルダを読み取り可能
 "shared/*": [
allow.authenticated.to(["read"]),
allow.groups(["admin"]).to(["read", "write", "delete"]),
 ],
 // ゲストユーザーは公開フォルダを読み取り可能
 "public/*": [
allow.guest.to(["read"]),
allow.authenticated.to(["read", "write"]),
 ],
  }),
});
// amplify/backend.ts
import { defineBackend } from "@aws-amplify/backend";
import { auth } from "./auth/resource";
import { data } from "./data/resource";
import { storage } from "./storage/resource";

defineBackend({
  auth,
  data,
  storage,
});

アクセスレベルは allow.entity("identity")(認証されたユーザー個人のパス)、allow.authenticated(全認証ユーザー)、allow.guest(未認証ユーザー)、allow.groups(["groupName"])(Cognitoグループ)の4種類から選択します。パスパターンには {entity_id} などのプレースホルダを使用でき、実行時にユーザーのCognito Identity IDに置換されます。これにより、テナント分離・ユーザーごとのプライベートストレージが簡潔に実現します。

フロントエンドからのアップロード・ダウンロードにはAmplify StorageクライアントAPIを使用します。

// フロントエンド: ファイルアップロード
import { uploadData, getUrl } from "aws-amplify/storage";

// プロファイル画像のアップロード(認証済みユーザー自身のフォルダへ)
const result = await uploadData({
  path: "profile-pictures/{entity_id}/avatar.jpg",
  data: file,
  options: {
 contentType: "image/jpeg",
 onProgress: ({ transferredBytes, totalBytes }) => {
console.log(`${transferredBytes}/${totalBytes} バイト転送済み`);
 },
  },
}).result;

// 署名付きURLを取得して画像を表示
const { url } = await getUrl({
  path: "profile-pictures/{entity_id}/avatar.jpg",
  options: { expiresIn: 3600 },
});

5-5. S3イベントとLambdaトリガー — ファイル操作の自動処理

defineStorage で定義したS3バケットはLambda関数のトリガーとして構成できます。ファイルのアップロード・削除・コピーをイベントとしてLambdaを起動することで、画像リサイズ・ウイルススキャン・メタデータ抽出・コンテンツ変換などの処理を自動化できます。

S3イベントトリガーの設定はCDKレイヤーで行います。

// amplify/backend.ts(S3イベントトリガー設定)
import { defineBackend } from "@aws-amplify/backend";
import { auth } from "./auth/resource";
import { data } from "./data/resource";
import { storage } from "./storage/resource";
import { imageProcessor } from "./functions/imageProcessor/resource";
import * as s3 from "aws-cdk-lib/aws-s3";
import * as s3n from "aws-cdk-lib/aws-s3-notifications";

const backend = defineBackend({
  auth,
  data,
  storage,
  imageProcessor,
});

// S3バケットへのオブジェクト作成イベントでLambdaを起動
const s3Bucket = backend.storage.resources.bucket;
const processorLambda = backend.imageProcessor.resources.lambda;

s3Bucket.addEventNotification(
  s3.EventType.OBJECT_CREATED,
  new s3n.LambdaDestination(processorLambda),
  { prefix: "uploads/", suffix: ".jpg" }
);

// Lambda関数にS3バケットへのアクセス権限を付与
s3Bucket.grantRead(processorLambda);
s3Bucket.grantPut(processorLambda);

S3イベントを受け取るLambda関数の実装例を以下に示します。

// amplify/functions/imageProcessor/handler.ts
import type { S3Handler } from "aws-lambda";
import { S3Client, GetObjectCommand, PutObjectCommand } from "@aws-sdk/client-s3";

const s3Client = new S3Client({ region: process.env.AWS_REGION });

export const handler: S3Handler = async (event) => {
  for (const record of event.Records) {
 const bucket = record.s3.bucket.name;
 const key = decodeURIComponent(record.s3.object.key.replace(/\+/g, " "));

 console.log(`処理開始: s3://${bucket}/${key}`);

 // S3からオブジェクトを取得
 const getCommand = new GetObjectCommand({ Bucket: bucket, Key: key });
 const { Body } = await s3Client.send(getCommand);

 // 画像処理(リサイズ・変換等)
 const processedData = await processImage(Body);

 // 処理済みファイルをS3に保存
 const processedKey = key.replace("uploads/", "processed/");
 const putCommand = new PutObjectCommand({
Bucket: bucket,
Key: processedKey,
Body: processedData,
ContentType: "image/webp",
 });
 await s3Client.send(putCommand);

 console.log(`処理完了: s3://${bucket}/${processedKey}`);
  }
};

async function processImage(body: any): Promise<Buffer> {
  // 実際の画像処理ロジック(sharpライブラリ等を使用)
  return Buffer.from([]);
}

S3トリガーを使ったイベント駆動パターンの主なユースケースとして以下が挙げられます。

  • 画像リサイズ・フォーマット変換: アップロード直後にWebP変換・サムネイル生成を自動実行します。
  • ドキュメント解析: PDFやオフィスファイルをアップロードすると、テキスト抽出・AI解析を非同期処理します。
  • ウイルス・マルウェアスキャン: ファイル受信後に安全検査を実施し、不正ファイルを隔離します。
  • メタデータ登録: アップロード完了後にDynamoDBへファイル情報を自動登録し、GraphQL APIから検索可能にします。
  • 通知送信: 特定フォルダへのアップロードをトリガーとして、管理者へメール・プッシュ通知を送信します。

5-6. 本番運用での注意点

defineFunctiondefineStorage を本番環境で運用する際には、以下の点に注意が必要です。

Lambda関数のコールドスタート対策: TypeScriptのビルド成果物サイズが大きいと、コールドスタート時間が増加します。不要なnpmパッケージを排除し、esbuild(Amplifyが内部で使用)の treeshaking が有効に機能するよう、named importsを活用してください。

環境変数の管理: defineFunctionenvironment フィールドに機密情報を直接記述しないことが重要です。AWS Secrets Managerに秘密情報を格納し、Lambda実行時に動的取得するパターンを推奨します。

S3バケットのCORS設定: Amplify Storageクライアントからのブラウザ直接アップロード(マルチパート含む)を行う場合、S3バケットに適切なCORSポリシーの設定が必要です。AmplifyはデフォルトでフロントエンドオリジンへのCORSを設定しますが、カスタムドメインを使用する場合はCDKレイヤーで明示的に指定してください。

Lambda同時実行数とスロットリング: イベント駆動処理でS3イベントが大量に発生するケースでは、スロットリング対策を事前に検討してください。Lambda同時実行数の上限超過でスロットリングが発生します。SQSをバッファとしてLambdaの前段に挟み、処理レートを制御するアーキテクチャを推奨します。

料金の見積もり: functions/storageの料金はLambda実行時間・メモリ・リクエスト数、S3ストレージ容量・リクエスト数・データ転送量の従量課金です。詳細は公式料金ページ(AWS Amplify・AWS Lambda・Amazon S3)を参照してください。開発期間中はper-developer sandboxのリソースが残存すると料金が発生するため、ampx sandbox delete で不要なsandboxを削除してください。

6. デプロイ・CI/CD・環境戦略

Amplify Gen 2のデプロイ・CI/CD・環境戦略の中心は、フルスタックブランチデプロイメントの概念です。
GitリポジトリのブランチとAmplify Hostingのブランチを紐付けると、
プッシュのたびにフロントエンド(Amplify Hosting)とバックエンド(CloudFormationスタック)の両方が自動的にデプロイされます。
開発・ステージング・本番の各環境をGitブランチで表現でき、IaCとアプリコードを一体として管理できます。
バックエンドの設定(DynamoDBテーブル・Cognitoプール等)も含めた完全な環境が、ブランチごとに分離されます。

6.1 フルスタックブランチデプロイメントの仕組み

Amplify Hostingでブランチを接続すると、以下のリソースが1つのデプロイパイプラインで管理されます。

レイヤーAmplifyが管理するリソースデプロイ先
フロントエンドビルド成果物(HTML/CSS/JS)CloudFront + S3
バックエンドCloudFormationスタック各AWSサービス
環境変数Amplify Hosting環境変数ビルド時に注入

GitリポジトリをAmplify Hostingに接続すると、Amplify AppがWebhookを設定します。
mainブランチへのプッシュが本番環境へ、stagingブランチへのプッシュがステージング環境へ
それぞれ独立したCloudFormationスタックとしてデプロイされます。
ブランチを追加するだけで新しい環境が自動的にプロビジョニングされるため、
環境ごとの手動セットアップ作業が不要になります。

Amplify Gen 2では各環境のバックエンドリソースは別々のCloudFormationスタックとして管理されます。
そのため、本番環境(mainブランチ)のDynamoDBテーブルとステージング環境(stagingブランチ)の
DynamoDBテーブルは完全に独立しており、データの混在が起こりません。

6.2 本番・ステージング・開発環境の分離戦略

推奨される環境構成は以下の3層です。

環境GitブランチAmplify環境用途
開発feature/*per-developer sandbox個人開発・テスト
ステージングstagingstaging(Amplify branch)結合テスト・QA
本番mainproduction(Amplify branch)エンドユーザー向け

sandbox環境はnpx ampx sandboxで開発者個人のAWSアカウントに立ち上げます。
本番・ステージングはAmplify Hostingのブランチデプロイで管理します。
この分離により、開発中のコードが本番データに触れるリスクをゼロにできます。

各環境でDynamoDBテーブルやCognitoプールが独立して作成されるため、
本番データを開発者が誤って変更する事故を防げます。
Amplify Gen 2では環境ごとにAWSアカウントを分けることもでき、
より強固な分離を実現できます(マルチアカウント構成)。
環境ごとの設定差異(エンドポイントURL・API ID等)はamplify_outputs.jsonに自動反映されるため、
フロントエンドのコードを変更せずに環境を切り替えられます。

6.3 ampx pipeline-deploy によるCI/CD連携

Amplify HostingはGitHub・GitLab・Bitbucketとのネイティブ統合を提供しますが、
既存のCI/CDパイプライン(GitHub Actions・CodePipeline等)に組み込む場合は
ampx pipeline-deployコマンドを使用します。

# GitHub Actions での使用例
npx ampx pipeline-deploy --branch $GITHUB_REF_NAME --app-id $AMPLIFY_APP_ID

ampx pipeline-deployはバックエンドのみをデプロイするコマンドです。
フロントエンドのビルドとデプロイは引き続きCI/CDパイプラインや
Amplify Hostingのビルド設定(amplify.yml)で制御します。
デプロイ前にnpx ampx generate outputsを実行すると、バックエンドの設定情報を
amplify_outputs.jsonとしてフロントエンドビルドに注入できます。

# CI/CDパイプラインの典型的なステップ
# 1. バックエンドをデプロイ
npx ampx pipeline-deploy --branch main --app-id $AMPLIFY_APP_ID
# 2. フロントエンドビルド用に出力ファイルを生成
npx ampx generate outputs --branch main --app-id $AMPLIFY_APP_ID
# 3. フロントエンドをビルド・デプロイ
npm run build

Amplify Hosting単体のCI/CD詳細(CodePipeline連携・SAM統合など)については
DevOps/CI/CD実践 Vol2を参照してください。

6.4 Amplify Hosting の設定とamplify.yml

Amplify Hostingのビルド設定は、リポジトリルートのamplify.ymlで管理します。
フロントエンドのビルドコマンド・ビルド成果物ディレクトリ・環境変数を一元管理できます。

version: 1
backend:
  phases:
 build:
commands:
  - npm ci --cache .npm --prefer-offline
  - npx ampx pipeline-deploy --branch $AWS_BRANCH --app-id $AWS_APP_ID
frontend:
  phases:
 preBuild:
commands:
  - npm ci --cache .npm --prefer-offline
  - npx ampx generate outputs --branch $AWS_BRANCH --app-id $AWS_APP_ID
 build:
commands:
  - npm run build
  artifacts:
 baseDirectory: .next
 files:
- '**/*'
  cache:
 paths:
- .next/cache/**/*
- .npm/**/*

AWS_BRANCHAWS_APP_IDはAmplify Hostingが自動的に設定する環境変数です。
npx ampx generate outputsはバックエンドの設定(エンドポイント・認証設定等)を
フロントエンドが読み込める形式(amplify_outputs.json)として生成します。
このファイルが存在することで、フロントエンドビルドがバックエンドの設定を自動的に取り込みます。

6.5 プレビュー環境とPull Requestデプロイ

Amplify Hostingには、Pull RequestごとにプレビューURLを自動生成する機能があります。
GitHubのPRを作成すると、Amplifyがそのブランチ用の独立したフルスタック環境をデプロイし、
専用のプレビューURLを発行します。
PRのレビュアーがプレビューURLでUIと動作を確認できるため、レビューの質が向上します。

プレビュー環境は以下の設定でAmplify Hostingコンソールから有効化できます。

  1. Amplify Hostingコンソールで対象アプリを開きます
  2. 「ブランチ設定」→「プレビュー」を有効化します
  3. GitHubアプリの権限でPRアクセスを許可します

プレビュー環境もバックエンド(CloudFormationスタック)を含む完全な環境として起動しますが、
使用後は自動的に削除されます(PRクローズ時)。
プレビュー環境の数が増えるとAWSリソース料金が発生するため、アクティブなPR数を適切に管理してください。

6.6 環境変数とシークレット管理

本番環境で必要なAPIキーや外部サービス認証情報は、
Amplify Hostingの「環境変数」設定ページで管理します。
設定した環境変数はビルド時とランタイム(SSRの場合)に自動的に注入されます。

機密性の高いシークレットはAWS Secrets Managerやシステムマネージャのパラメータストアに格納し、
Lambda関数やAmplify Hostingのビルドプロセスからアクセスする構成が推奨されます。
defineFunctionでLambda関数を定義する際に、パラメータストアへのアクセス権限を付与できます。
バックエンドの開発者シークレット(外部プロバイダーのAPIキー等)は
ampx sandbox secret set <KEY_NAME>コマンドで登録し、secret()関数で参照します。

// amplify/backend.ts でシークレットを参照する例
import { defineBackend } from '@aws-amplify/backend';
import { defineFunction, secret } from '@aws-amplify/backend';

const myFunction = defineFunction({
  name: 'my-function',
  environment: {
 EXTERNAL_API_KEY: secret('EXTERNAL_API_KEY'),
  },
});

シークレットはAmplify Hostingのコンソールまたはコマンドラインから設定します。
本番・ステージング・開発環境それぞれで独立したシークレット値を設定できます。

6.7 コスト管理と見積もり

Amplify Gen 2のコストはバックエンドを構成するAWSサービスごとの従量課金になります。
Amplify Hosting自体の料金(ビルド時間・ホスティング転送量)に加えて、
以下のサービス料金が発生します。

サービス課金軸
AWS AppSyncリクエスト数・サブスクリプション接続時間
Amazon DynamoDB読み書きリクエスト数・ストレージ容量
Amazon CognitoMAU(月間アクティブユーザー)数
AWS Lambda呼び出し回数・実行時間
Amazon S3ストレージ容量・データ転送量
Amplify Hostingビルド分・ホスティング転送量

各サービスの料金は変動することがあるため、最新の料金は各サービスの公式料金ページを参照してください。
開発初期はper-developer sandboxとAmplify HostingのFree Tierを活用すると、
コストを最小限に抑えながら開発できます。
本番展開前にAWS Pricing Calculatorを使って月次コストを試算することをお勧めします。
DynamoDBのオンデマンドモードはトラフィックが少ない初期に適していますが、
予測可能なトラフィックになったらプロビジョニングモードへの移行でコストを最適化できます。
per-developer sandboxのリソースが開発完了後も残存すると継続的な課金が発生するため、
npx ampx sandbox deleteコマンドで不要なsandboxを削除する習慣をつけてください。

7. 本番運用 — 監視・コスト・東京リージョン・CDK拡張

fig06: 本番運用の全体像(東京リージョンap-northeast-1へのデプロイ、CloudWatchによる監視、料金構成、カスタムCDKリソースによるAmplify標準外サービスの追加)
fig06: 本番運用 — 東京リージョン・監視・コスト・カスタムCDK拡張

7-1. 東京リージョン(ap-northeast-1)へのデプロイ

AWS Amplify Gen 2はAWS CDK基盤のため、バックエンドリソースはAWSプロファイルで設定したリージョンに展開されます。東京リージョン(ap-northeast-1)でフルスタックアプリを運用する場合、AWSプロファイルのデフォルトリージョンを東京に設定するか、ampx sandboxampx pipeline-deployコマンド実行時に明示的にリージョンを指定します。

# AWS CLIプロファイルでリージョンを東京に設定
aws configure set region ap-northeast-1

# またはコマンド実行時にリージョンを指定
npx ampx sandbox --region ap-northeast-1

# CI/CDパイプラインからデプロイする場合
npx ampx pipeline-deploy \
  --branch main \
  --app-id <Amplify_App_ID> \
  --region ap-northeast-1

Amplify Gen 2で使用するバックエンドサービス(AppSync・DynamoDB・Cognito・Lambda・S3)はすべて東京リージョンで利用可能です。Amplify Hostingのフロントエンドホスティングも東京リージョンに展開できます。CloudFrontのCDNエッジロケーションは日本国内に複数存在するため、東京リージョンとの組み合わせで国内ユーザーへの低レイテンシ配信が実現します。

マルチリージョン構成(DR・グローバル展開)を検討する場合は、Amplify Gen 2のバックエンドスタックとCloudFormationをリージョンごとに個別展開する必要があります。DynamoDBグローバルテーブルとCognito Identity Poolsのフェデレーション設定はCDKレイヤーで追加実装します。

東京リージョン対応の確認ポイント

サービス東京(ap-northeast-1)対応
AWS AppSync対応
Amazon DynamoDB対応
Amazon Cognito User Pools対応
AWS Lambda対応
Amazon S3対応
Amplify Hosting対応

7-2. Next.js / React との統合

Amplify Gen 2はNext.jsとReactの両方を公式サポートしています。プロジェクト初期化時にフレームワークを選択でき、Amplify Hostingが各フレームワークの特性に合わせた最適なビルド・デプロイ設定を自動適用します。

Next.js統合の設定

Next.js App Routerを使う場合、サーバーサイドコンポーネントとクライアントサイドコンポーネントでAmplifyの初期化方法が異なります。クライアントサイドではAmplify.configure()を呼び出し、サーバーサイドではcreateServerRunnerを使います。

// src/app/ConfigureAmplifyClientSide.tsx(クライアントコンポーネント)
'use client';

import { Amplify } from 'aws-amplify';
import outputs from '@/amplify_outputs.json';

Amplify.configure(outputs, { ssr: true });

export default function ConfigureAmplifyClientSide() {
  return null;
}
// src/lib/amplify-server-utils.ts(サーバーサイド用)
import { createServerRunner } from '@aws-amplify/adapter-nextjs';
import outputs from '@/amplify_outputs.json';

export const { runWithAmplifyServerContext } = createServerRunner({
  config: outputs,
});

サーバーコンポーネントからデータを取得する場合は、runWithAmplifyServerContextでコンテキストを設定してからgenerateServerClientUsingCookiesを使います。

// src/app/page.tsx(Server Component)
import { cookies } from 'next/headers';
import { generateServerClientUsingCookies } from '@aws-amplify/adapter-nextjs/data';
import { runWithAmplifyServerContext } from '@/lib/amplify-server-utils';
import outputs from '@/amplify_outputs.json';
import type { Schema } from '@/amplify/data/resource';

export default async function HomePage() {
  const posts = await runWithAmplifyServerContext({
 nextServerContext: { cookies },
 operation: async (contextSpec) => {
const client = generateServerClientUsingCookies<Schema>({
  config: outputs,
  cookies,
});
const { data } = await client.models.BlogPost.list({
  filter: { publishedAt: { attributeExists: true } },
});
return data;
 },
  });

  return (
 <main>
{posts.map((post) => (
  <article key={post.id}>
 <h2>{post.title}</h2>
  </article>
))}
 </main>
  );
}

React(SPA)統合の設定

React SPAの場合はシンプルです。エントリポイントでAmplify.configure()を1回呼び出すだけで、アプリ全体でAmplify APIが使えるようになります。

// src/main.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { Amplify } from 'aws-amplify';
import outputs from '../amplify_outputs.json';
import App from './App';

Amplify.configure(outputs);

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
 <App />
  </React.StrictMode>
);

Amplify UI(@aws-amplify/ui-react)の<Authenticator>コンポーネントを使うと、ログイン・サインアップ・パスワードリセットUIを数行で組み込めます。

// src/App.tsx
import { Authenticator } from '@aws-amplify/ui-react';
import '@aws-amplify/ui-react/styles.css';

function App() {
  return (
 <Authenticator>
{({ signOut, user }) => (
  <main>
 <h1>ようこそ、{user?.username}さん</h1>
 <button onClick={signOut}>サインアウト</button>
 <TodoList />
  </main>
)}
 </Authenticator>
  );
}

export default App;

7-3. Gen 1 → Gen 2 移行設計(EOL: 2027-05-01)

AWS Amplify Gen 1(CLI v1/v2ベースの従来版)は2027年5月1日にEOL(End of Life)を迎える予定です。AWSはGen 1をメンテナンスモードに移行しており、新機能の追加は行われません。現在Gen 1でAmplifyを運用しているチームは、EOL前にGen 2への移行計画を立てることを強く推奨します。

Gen 1とGen 2の主な違い

観点Gen 1Gen 2
設定方式CLI対話・amplify/フォルダのJSONTypeScriptのcode-first
デプロイ基盤Amplify独自のCloudFormationAWS CDK(L3 construct)
IaC管理amplify/backend/の自動生成ファイルamplify/内のTypeScriptファイル
型安全性限定的(手動型定義)完全(自動型生成)
カスタマイズoverrideフォルダ経由CDK constructを直接記述
サポート状況メンテナンスモード(EOL: 2027-05-01)アクティブ開発

移行の基本戦略

AWSはGen 1からGen 2への移行を支援するツールを提供しています。ただし、完全自動移行ツールは提供されておらず、基本的にはGen 2プロジェクトを新規作成してリソースを再定義するアプローチが推奨されています。

移行の推奨ステップは以下のとおりです。

ステップ1: Gen 2プロジェクトの新規作成

# 新規Gen 2プロジェクトを作成
npm create amplify@latest
# または既存プロジェクトへ追加
npm create amplify@latest -- --no-install

ステップ2: バックエンドリソースの再定義

Gen 1のCLI設定(amplify/backend/以下のJSON)をGen 2のTypeScriptに書き直します。

// Gen 1相当のauth設定をGen 2で再定義
// Gen 1: amplify/backend/auth/myauth/cli-inputs.json のJSON設定
// Gen 2: amplify/auth/resource.ts

import { defineAuth } from '@aws-amplify/backend';

export const auth = defineAuth({
  loginWith: {
 email: true,
 // Gen 1でGoogle連携していた場合
 externalProviders: {
google: {
  clientId: secret('GOOGLE_CLIENT_ID'),
  clientSecret: secret('GOOGLE_CLIENT_SECRET'),
  scopes: ['email', 'profile', 'openid'],
},
callbackUrls: ['https://your-domain.com/auth/callback'],
logoutUrls: ['https://your-domain.com/'],
 },
  },
  multifactor: { mode: 'OPTIONAL', totp: true },
});

ステップ3: データスキーマの移行

Gen 1のschema.graphqlをGen 2のdefineDataスキーマに変換します。

// Gen 1: amplify/backend/api/myapi/schema.graphql
// type Post @model @auth(rules: [{allow: owner}, {allow: public, operations: [read]}]) {
//title: String!
//content: String
// }

// Gen 2: amplify/data/resource.ts
const schema = a.schema({
  Post: a
 .model({
title: a.string().required(),
content: a.string(),
 })
 .authorization((allow) => [
allow.owner(),
allow.publicApiKey().to(['read']),
 ]),
});

ステップ4: 段階的移行とデータ共存

本番データをGen 1のDynamoDBテーブルに保持したまま、Gen 2のappからGen 1のAppSync APIを参照するハイブリッド移行も可能です。Gen 2のdefineDataでカスタムクエリを定義し、既存テーブルへのクロスアカウント/クロスリージョンアクセスをCDKレイヤーで構成します。フルカットオーバー時にDynamoDBのデータをGen 2テーブルへ移行します。

7-4. 本番監視 — CloudWatch / AppSync / Lambda メトリクス

Amplify Gen 2で構成したフルスタックアプリの本番監視は、各AWSサービスのCloudWatchメトリクスを中心に行います。

AppSyncの主要監視メトリクス

AppSync GraphQL APIの健全性を監視するために、以下のCloudWatchメトリクスを監視設定します。

メトリクス用途推奨アラーム閾値
4XXErrorクライアントエラー(認可エラー含む)エラー率 > 1%
5XXErrorサーバーエラー(Lambda障害等)エラー率 > 0.1%
LatencyAPIレスポンスレイテンシp99 > 3,000ms
ConnectCountWebSocket接続数上限の80%
// CDKレイヤーでCloudWatchアラームを追加する例
import * as cw from 'aws-cdk-lib/aws-cloudwatch';
import * as cwa from 'aws-cdk-lib/aws-cloudwatch-actions';
import * as sns from 'aws-cdk-lib/aws-sns';

const monitoringStack = backend.createStack('MonitoringStack');
const alarmTopic = new sns.Topic(monitoringStack, 'AlarmTopic');

// AppSync 5XXエラーアラーム
const appSyncApi = backend.data.resources.graphqlApi;
new cw.Alarm(monitoringStack, 'AppSync5XXAlarm', {
  metric: appSyncApi.metric('5XXError', {
 period: cdk.Duration.minutes(1),
 statistic: 'Sum',
  }),
  threshold: 5,
  evaluationPeriods: 3,
  treatMissingData: cw.TreatMissingData.NOT_BREACHING,
}).addAlarmAction(new cwa.SnsAction(alarmTopic));

Lambda関数の監視

defineFunctionで定義したLambda関数は自動的にCloudWatch Logsにログを出力します。本番環境では以下を標準監視として設定します。

// Lambda エラー率アラーム
const myLambda = backend.myFunction.resources.lambda;
new cw.Alarm(monitoringStack, 'LambdaErrorAlarm', {
  metric: myLambda.metricErrors({
 period: cdk.Duration.minutes(5),
 statistic: 'Sum',
  }),
  threshold: 1,
  evaluationPeriods: 1,
}).addAlarmAction(new cwa.SnsAction(alarmTopic));

// Lambda 実行時間アラーム(タイムアウト手前での早期警告)
new cw.Alarm(monitoringStack, 'LambdaDurationAlarm', {
  metric: myLambda.metricDuration({
 period: cdk.Duration.minutes(5),
 statistic: 'p99',
  }),
  threshold: 25000, // タイムアウト30秒の場合、25秒で警告
  evaluationPeriods: 2,
}).addAlarmAction(new cwa.SnsAction(alarmTopic));

7-5. カスタムCDKリソースによるAmplify拡張

Amplify Gen 2はAWS CDK基盤のため、Amplifyが標準でサポートしていないAWSサービスをCDK constructで追加できます。これはGen 1のoverridesフォルダよりも柔軟で型安全な拡張方法です。

// amplify/backend.ts(CDKでの拡張例)
import { defineBackend } from '@aws-amplify/backend';
import { auth } from './auth/resource';
import { data } from './data/resource';
import * as ses from 'aws-cdk-lib/aws-ses';
import * as sqs from 'aws-cdk-lib/aws-sqs';
import * as iam from 'aws-cdk-lib/aws-iam';

const backend = defineBackend({ auth, data });

// SESのメール送信設定を追加
const emailStack = backend.createStack('EmailStack');
const emailIdentity = new ses.EmailIdentity(emailStack, 'AppEmailIdentity', {
  identity: ses.Identity.domain('example.com'),
});

// SQSキューをバッファとして追加
const queueStack = backend.createStack('QueueStack');
const notificationQueue = new sqs.Queue(queueStack, 'NotificationQueue', {
  visibilityTimeout: cdk.Duration.seconds(300),
  retentionPeriod: cdk.Duration.days(14),
});

// Lambda関数にSESメール送信権限を付与
backend.myFunction.resources.lambda.addToRolePolicy(
  new iam.PolicyStatement({
 actions: ['ses:SendEmail', 'ses:SendRawEmail'],
 resources: ['*'],
 conditions: {
StringEquals: { 'ses:FromAddress': 'noreply@example.com' },
 },
  })
);

// Lambda関数からSQSキューへのアクセスを許可
notificationQueue.grantSendMessages(backend.myFunction.resources.lambda);

// SQSキューのURLを環境変数でLambdaに渡す
backend.myFunction.resources.lambda.addEnvironment(
  'NOTIFICATION_QUEUE_URL',
  notificationQueue.queueUrl
);

7-6. コスト最適化の考え方

Amplify Gen 2を使ったフルスタックアプリのコストは、構成するAWSサービスの従量課金の合計になります。各サービスの料金は公式ページを参照してください。

主なコスト要因

サービス主なコスト要因
AWS AppSyncAPIリクエスト数・リアルタイム接続分数
Amazon DynamoDBリクエストユニット消費量・ストレージ容量
Amazon CognitoMonthly Active Users(MAU)
AWS Lambda実行時間×メモリ・リクエスト数
Amazon S3ストレージ容量・リクエスト数・データ転送量
Amplify Hostingビルド時間・ホスティング帯域

コスト削減のポイント

開発コストとして特に注意が必要なのは、ampx sandboxのリソース放置です。各開発者がsandboxを起動したまま停止を忘れると、AppSync APIやDynamoDBテーブルが稼働し続けてコストが発生します。開発終了時はampx sandbox deleteで確実にリソースを削除してください。

本番環境ではDynamoDBの「オンデマンド」から「プロビジョニング」モードへの移行を検討してください。トラフィックが予測可能なアプリでは、プロビジョニングモードとAuto Scalingの組み合わせでコストを削減できます。

8. つまずきポイント・アンチパターン・まとめ

本セクションでは、AWS Amplify Gen 2 を使ったフルスタック開発で実際によく発生するつまずきポイントを 8 件、アンチパターンとその正しい対処法を 5 件取り上げます。Gen 2 は従来の Gen 1 から大幅にアーキテクチャを変えているため、Gen 1 経験者ほど思い込みによるミスを生じやすい傾向があります。

つまずきポイント

① Gen 1 CLI(amplify)と Gen 2 CLI(ampx)の混用

Gen 1 では @aws-amplify/cli パッケージの amplify コマンドを使用しますが、Gen 2 では @aws-amplify/backend-cli パッケージの ampx コマンドを使います。amplify push を Gen 2 プロジェクトで実行すると、Gen 1 の設定ファイルを検出できずエラーとなります。逆に、Gen 2 プロジェクトで npx ampx sandbox を実行するつもりが、グローバルインストールされた amplify CLI を誤って呼んでしまうケースもあります。npx 経由で @aws-amplify/backend-cli を指定するか、package.json のスクリプトに ampx コマンドを登録することで混用を防げます。

② sandbox を本番環境として使用

npx ampx sandbox で起動した環境は 開発専用 です。Ctrl+C 停止時にリソースがすべて削除されるため、本番データの消失リスクが生じます。また、CloudFormation スタック名にユーザー名が含まれる(例: amplify-myapp-taro-sandbox)ため、チーム共有には適していません。本番環境には必ず npx ampx pipeline-deploy を使った Amplify Hosting の fullstack branch deployments を使用してください。

amplify_outputs.json をそのままコミット

amplify_outputs.json には Cognito User Pool ID・AppSync エンドポイント URL など環境固有の設定が含まれます。これを Git コミットすると、他の開発者が自分の sandbox 環境の設定で上書きされ、接続先が変わってしまう問題が起きます。.gitignoreamplify_outputs.json を追加し、sandbox 起動時に自動生成させる運用を徹底してください。

④ IAM 権限不足による sandbox 起動失敗

npx ampx sandbox は CloudFormation・AppSync・DynamoDB・Cognito・Lambda・S3 など複数サービスへの権限を必要とします。開発者の IAM ユーザーに最小権限しか付与されていない場合、スタック作成途中で AccessDeniedException が発生してロールバックします。エラーメッセージに含まれるサービス名と操作名(例: iam:CreateRole)を確認し、必要な権限を追加するか、AdministratorAccess の一時付与を検討してください。

⑤ TypeScript スキーマの型エラーを無視

Gen 2 の defineData は TypeScript の型システムでスキーマを定義します。エディタの型エラーを無視して npx ampx sandbox を実行すると、CloudFormation デプロイ中にエラーが発生し、エラーの原因追跡が困難になります。スキーマ変更後は必ず TypeScript のコンパイルチェック(npx tsc --noEmit)を通過させてから sandbox を起動してください。

⑥ 認可ルールの設計漏れ(全員アクセス可能になる)

defineData のモデルに .authorization() を記述しないと、デフォルトで allow.owner() が適用されますが、カスタムクエリ・ミューテーションの場合は明示的な認可ルールが必要です。allow.publicApiKey() を誤って全モデルに設定すると、未認証ユーザーがすべてのデータにアクセスできるセキュリティリスクが生じます。認可ルールは必ず最小権限原則に従い、モデルごとに必要なアクセスレベルを明示的に定義してください。

⑦ sandbox 削除でデータが消える

npx ampx sandbox 停止時のリソース削除は DynamoDB テーブルも対象 です。開発中に登録したテストデータはすべて消えます。重要なテストデータを保持したい場合は --no-delete オプションで停止するか、DynamoDB のポイントインタイムリカバリ(PITR)を有効化してスナップショットを取得してください。本番環境データを誤って sandbox に混入させないよう、環境分離の設計を徹底することが重要です。

⑧ バックエンドとフロントエンドのリージョン不一致

Gen 2 のバックエンドは AWS CLI プロファイルが示すリージョンへ展開されます。amplify_outputs.json には展開先リージョンが記録されますが、フロントエンドのコードで Amplify.configure() を呼び出さずリージョンをハードコードすると、本番環境でのリージョン変更時に接続エラーが発生します。必ず amplify_outputs.json から設定を読み込む形を維持し、リージョン情報をコードへ直接埋め込まないようにしてください。

アンチパターン → 正しい対処法

アンチパターン ①: amplify CLI で Gen 2 プロジェクトを操作する

Gen 1 の経験を持つ開発者が、Gen 2 プロジェクトで amplify pushamplify add auth などの Gen 1 コマンドを実行しようとするケースです。Gen 2 プロジェクトには Gen 1 の設定ファイル(amplify/ 配下の JSON)が存在しないためエラーになります。

正しい対処法: Gen 2 では npx ampx sandbox(開発環境)・npx ampx pipeline-deploy(CI/CD)を使用します。amplify コマンドは Gen 1 専用として割り切り、Gen 2 プロジェクトでは使用しないルールをチームで共有してください。

アンチパターン ②: 複数の開発者が同一の sandbox を共有

コスト削減を目的に、チーム全員が同じ AWS アカウント上の 1 つの sandbox を共有するケースです。スキーマ変更の競合・テストデータの干渉・予期しないリソース削除などのリスクがあります。

正しい対処法: per-developer sandbox の設計思想に従い、各開発者が自分専用の sandbox を持ちます。--identifier オプションで sandbox に名前を付けることで複数の sandbox を同一アカウントで管理できます。コストが懸念される場合は、使用しない sandbox を npx ampx sandbox delete --identifier <名前> で削除する習慣をつけてください。

アンチパターン ③: 認可ルールを allow.publicApiKey() のみにする

開発初期の簡便さから、すべてのモデルとカスタムクエリに allow.publicApiKey() を設定してしまうケースです。API キーを知っている者は誰でもデータにアクセスできるため、本番環境でのデータ漏洩リスクが高まります。

正しい対処法: allow.authenticated() または allow.owner() を基本の認可ルールとし、公開が必要なデータのみ allow.publicApiKey() を組み合わせる形で設計します。認可ルールは defineData のスキーマレベルとフィールドレベルの両方で設定でき、きめ細かいアクセス制御が可能です。

アンチパターン ④: CDK リソースを CloudFormation コンソールから直接変更する

Amplify Gen 2 が展開した CloudFormation スタックのリソースを AWS コンソールから手動変更するケースです。次回 npx ampx sandboxpipeline-deploy を実行したときに、コードの設定で上書きされてしまい、手動変更が消えます。

正しい対処法: リソースの変更はすべて amplify/ 配下の TypeScript ファイルを通じて行います。Amplify 標準の define 系 API でカバーできない設定は backend.createStack() によるカスタム CDK construct として記述してください。「コードが唯一の正本(Single Source of Truth)」の原則を守ることが重要です。

アンチパターン ⑤: Gen 1 の amplify/ 配下の JSON 設定をそのまま Gen 2 に流用

Gen 1 では amplify/backend/api/myapi/schema.graphqlamplify/backend/auth/myauth/parameters.json などの設定ファイルが正本でしたが、Gen 2 ではこれらのファイル形式は使用しません。Gen 1 の設定ファイルをコピーして Gen 2 プロジェクトに配置しても、ampx はこれらを認識しません。

正しい対処法: Gen 1 から Gen 2 への移行では、各リソースの設定を defineDatadefineAuthdefineFunctiondefineStorage で一から再定義します。AWS が提供する公式移行ガイドと移行ツールを活用し、sandbox 環境で動作確認してから本番移行を実施してください。

まとめ

本記事 Vol1 では、AWS Amplify Gen 2 のフルスタック TypeScript 開発基盤について以下のトピックを解説しました。

  • §2: プロジェクト初期化・per-developer sandbox・AWS CDK 基盤・Gen 1 との違い
  • §3: defineData による AppSync GraphQL/DynamoDB スキーマ定義と認可ルール
  • §4: defineAuth による Amazon Cognito 統合・ソーシャルログイン・MFA
  • §5: defineFunction(Lambda)と defineStorage(S3)の宣言的定義
  • §6: fullstack branch deployments と CI/CD パイプライン
  • §7: 東京リージョン対応・CloudWatch 監視・コスト管理・カスタム CDK リソース

Gen 2 の最大の特徴は「TypeScript が設定の正本(Single Source of Truth)」という設計思想です。amplify/ 配下の TypeScript ファイルがすべての AWS リソースを定義し、CDK → CloudFormation が確実にプロビジョニングします。この設計により、インフラとアプリケーションコードの整合性が型システムによって保証されます。

Gen 2 はフロントエンド主導のフルスタック開発基盤として、認証・API・データベース・ストレージ・サーバーレス処理を統合管理できる強力なプラットフォームです。Vol2 以降では、より高度な本番運用パターン(マルチ環境戦略・カスタム認証フロー・パフォーマンスチューニング)を解説予定です。

Gen 1 → Gen 2 移行チェックリスト

Gen 1 から Gen 2 へ移行する際に確認すべき主な項目をまとめます。

チェック項目対応内容完了
Node.js バージョン確認18.x 以上であることを確認
@aws-amplify/backend-cli インストールnpm install --save-dev @aws-amplify/backend-cli
amplify/backend.ts 作成defineBackend エントリーポイントを作成
defineAuth への移行Gen 1 の amplify/backend/auth/ → TypeScript 再定義
defineData への移行Gen 1 の schema.graphql → TypeScript スキーマに書き換え
defineFunction への移行Gen 1 の Lambda 設定 → defineFunction で再定義
defineStorage への移行Gen 1 の S3 設定 → defineStorage で再定義
sandbox での動作確認npx ampx sandbox で全リソースが正常展開されることを確認
amplify_outputs.json への切り替えフロントエンドの Amplify.configure() を更新
.gitignore への追加amplify_outputs.json を Git 管理外に設定
Amplify Hosting の再接続Gen 2 用のビルド設定(amplify.yml)に更新
Gen 1 リソースの削除移行確認後に Gen 1 スタックを amplify delete で削除

移行作業は必ず sandbox 環境で検証 してから本番環境へ適用してください。Gen 1 と Gen 2 を同一 AWS アカウントで並行稼働させることは可能ですが、長期間の並行稼働はコスト増加の原因になります。

料金の目安と注意点

Amplify Gen 2 を構成するサービスの料金は従量課金制です。構成するサービスごとに発生する主なコストは次のとおりです。

サービス主な課金軸備考
AWS AppSyncAPI リクエスト数・データ転送量GraphQL クエリ・ミューテーション・サブスクリプション単位
Amazon DynamoDBストレージ容量・読み書きユニットオンデマンドキャパシティモード推奨
Amazon Cognito月間アクティブユーザー(MAU)初月 50,000 MAU まで無料枠あり
AWS Lambda実行時間・リクエスト数月間 100 万リクエスト・40 万 GB-秒まで無料枠あり
Amazon S3ストレージ容量・リクエスト数・転送量最初の 5GB は無料枠
Amplify Hostingビルド時間・転送量月間 1,000 ビルド分・15GB まで無料枠あり

Per-developer sandbox は開発期間中のみ AWS リソースが起動します。Ctrl+C 停止でリソースが削除されますが、--no-delete で停止した場合やsandbox が残存している場合は料金が継続発生します。npx ampx sandbox status で残存する sandbox を定期確認することを推奨します。詳細な料金は各サービスの公式料金ページを参照してください。

本記事のポイント(AWS Amplify Gen 2)

  • TypeScript code-first でバックエンドを定義 → AWS CDK → CloudFormation でプロビジョニング
  • per-developer sandbox でチーム開発のリソース干渉を排除
  • defineData/defineAuth/defineFunction/defineStorage の 4 本柱でフルスタック構成
  • Gen 1 との決定的な違い: 設定の正本が JSON から TypeScript ソースコードへ
  • Gen 1 は 2027年5月1日 EOL 予定 → 新規プロジェクトは Gen 2 採用を推奨

Amplify Hosting の CI/CD 実践 — DevOps/CI/CD Vol2 でコンテナ・SAM との統合パターンを深掘り

Amazon Cognito 認証/アイデンティティ 本番運用 Vol1 — passkeys・Identity Pools・ソーシャルログインを徹底解説

AWS Application Integration 本番運用 Vol1 — AppSync・EventBridge・SQS の実践統合パターン

AWS サーバーレス本番運用 — Lambda/API Gateway/Step Functions のフルスタック構成を解説

AWS CloudWatch Synthetics・RUM フロントエンド監視 Vol1 — Amplify Hosting と連携した可視化戦略

デバッグ・トラブルシューティング手順

Gen 2 の問題調査では、以下の確認手順を参考にしてください。

sandbox 起動失敗時の確認手順

  1. npx ampx sandbox のエラーメッセージを確認し、AccessDeniedException があれば IAM 権限を確認します。
  2. AWS マネジメントコンソールの CloudFormation 画面で、amplify-<app>-<username>-sandbox スタックのイベントタブを確認します。ROLLBACK_IN_PROGRESS または DELETE_IN_PROGRESS の直前のイベントがエラー原因です。
  3. npx ampx sandbox --debug オプションを追加して詳細ログを出力します。
  4. 前回の sandbox が正常に削除されていない場合は、CloudFormation コンソールから手動でスタックを削除してから再試行します。

TypeScript エラーの確認

# amplify/ ディレクトリの型チェック
cd amplify && npx tsc --noEmit

amplify/tsconfig.json のスコープで型チェックが実行されます。エラーが解消されてから npx ampx sandbox を実行してください。

amplify_outputs.json が更新されない場合

sandbox が起動中の状態で amplify/backend.ts を変更し保存すると、通常は自動的に再デプロイされて amplify_outputs.json が更新されます。更新されない場合は、以下を確認してください。

  • ターミナルで ampx sandbox が実行中かどうか
  • ファイルの変更が保存されているか(エディタの自動保存を確認)
  • TypeScript のコンパイルエラーがないか

強制的に再デプロイするには、Ctrl+C で sandbox を停止してから再起動します。

AppSync エラー(403・401)のデバッグ

AppSync GraphQL クエリが認証エラーを返す場合、以下を確認します。

  1. フロントエンドで Amplify.configure(outputs) が正しく呼び出されているか確認します。
  2. defineData のスキーマで .authorization() が適切に設定されているか確認します。
  3. 認証が必要なクエリの場合、Auth.currentSession() が有効なセッションを返すか確認します。
  4. AppSync コンソールのログを確認します(CloudWatch Logs グループ: /aws/appsync/apis/<api-id>)。

S3 CORS エラー

ブラウザから S3 への直接アップロードで CORS エラーが発生する場合は、S3 バケットの CORS 設定を確認します。Amplify Storage は自動的に CORS を設定しますが、カスタムドメインを使用する場合は AllowedOrigins に追加が必要です。

# S3 バケットの CORS 設定を確認
aws s3api get-bucket-cors --bucket <bucket-name>

次のステップ

本記事 Vol1 を読み終えた次のステップとして、以下の取り組みを推奨します。

  1. sandbox でプロトタイプ作成: npm create amplify@latest でプロジェクトを作成し、defineData で簡単なスキーマを定義して sandbox での動作を確認します。
  2. §3 の defineData を試す: シンプルな Todo モデルを定義し、フロントエンドから CRUD 操作を実装します。
  3. §4 の defineAuth と組み合わせる: 認証ユーザーのみ Todo を作成できる認可ルール(allow.owner())を設定します。
  4. Amplify Hosting の接続: GitHub リポジトリと Amplify Hosting を接続し、プルリクエストごとの自動デプロイを設定します。
  5. 本番環境移行: 開発が完了したら npx ampx pipeline-deploy または Amplify Hosting の自動デプロイで本番環境を構築します。