0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Amplify Gen2 の FunctionDirectiveStack が 500 リソース超過したので stackMappings で Resolver を分散する

0
Last updated at Posted at 2026-09-29

はじめに

AWS Amplify Gen 2 でカスタム Lambda Resolver を増やしていくと、CloudFormation の 500 リソース上限 に当たることがあります。カスタム Query / Mutation はデフォルトで FunctionDirectiveStack という NestedStack に集約されるため、API が増えるほどこのスタックが膨らみ、新規 API の追加やデプロイが失敗します。

2026 年 5 月、@aws-amplify/backend-data 1.7.0 に defineData() の stackMappings が追加され、@aws-amplify/backend 1.23.0 から使えます。AppSync Resolver を別 NestedStack へ分散配置できます。

本記事では、なぜこの機能が必要になるのか、どう使うのか、移行先をどう選ぶのかをまとめます。


なぜ stackMappings が必要になるか

Amplify Gen 2 の Resolver 配置

Resolver の置き場所は、種類によって違います。

種類 例 デフォルトの配置
モデル自動生成の CRUD CreateOrderResolver 各モデル用 NestedStack
カスタム Lambda(a.handler.function(...)) QuerysearchItemsResolver FunctionDirectiveStack
カスタム JS(a.handler.custom(...)) 共有 DataSource 上の Resolver data 親スタック(stackMappings の対象外)

上限対策の主戦場は a.handler.function(...) です。ユーザー管理・検索・通知・ファイル処理など、Lambda 連携の GraphQL フィールドが多いプロジェクトでは、FunctionDirectiveStack のリソース数が急速に増えます。1 Resolver あたり IAM Role / Policy、DataSource、FunctionConfiguration、Resolver が載るため、数十個でも上限が見えてきます。

a.handler.custom(...) は FunctionDirectiveStack を膨らませません。公開型どおり mapping 対象外なので、本記事の手順では扱いません。

直面する CloudFormation 制約

制約 内容
リソース数 1 CloudFormation スタックあたり 500 リソース
テンプレートサイズ S3 上のテンプレート 1MB 上限
公式の位置づけ リソース数上限に遭遇していない場合の利用は 非推奨(graphql-api-construct の型コメント)

親 data スタックのテンプレートが 1MB を超える問題も起こり得ます。NestedStack を増やすと、親に AWS::CloudFormation::Stack・Parameters・Outputs・DependsOn が増えます。data スタックの 1MB 超過は amplify-backend#3066 でも報告されています。

上限にまだ余裕がある場合は、公式どおり stackMappings は使いません。すでに上限が見えている場合が、この機能の出番です。

よく見るエラー

デプロイログを検索するときの手がかりです。FunctionDirectiveStack が 500 を超えると、次のようなメッセージになります。

[TooManyResourcesInStack] Number of resources in stack
'amplify-.../data/.../FunctionDirectiveStack': 517 is greater than allowed maximum of 500:
AWS::IAM::Role (...), AWS::IAM::Policy (...), AWS::AppSync::DataSource (...),
AWS::AppSync::FunctionConfiguration (...), AWS::AppSync::Resolver (...), AWS::CDK::Metadata (1)

sandbox / synth の警告は次の形です。

Number of resources in stack 'FunctionDirectiveStack': 505 is greater than allowed maximum of 500

親 data テンプレートが 1MB を超えると、次が出ます。

Template may not exceed 1000000 bytes in size.
Template size exceeds limit: 1022366/1000000. Split resources into multiple stacks
or set suppressTemplateIndentation to reduce template size.

ネストスタックの一括更新が大きすぎると、次も出ます。

Limit on the number of resources in a single stack operation exceeded

モデルスタックへ誤って載せた場合など、循環依存は次の形です。

The CloudFormation deployment failed due to circular dependency found between nested stacks

stackMappings とは

defineData() に追加するオプションで、AppSync Resolver の CloudFormation 論理 ID → 移行先 NestedStack 名 を指定します。@aws-amplify/backend 1.23.0 以上が前提です(機能追加は @aws-amplify/backend-data 1.7.0。リリースコミット)。

カスタム Lambda Resolver を退避する例です。論理 ID は Query<フィールド名>Resolver / Mutation<フィールド名>Resolver になります。

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

値は 任意の NestedStack 名 です。存在しない名前を指定すると、その名前の NestedStack が新規作成されます。公開型定義では Resolver のみ が対象で、DataSource や FunctionConfiguration は mapping しません。

Lambda 本体の置き場所は stackMappings では変わりません。通常は Function スタック側に残ります。resourceGroupName: 'data' で data 側へ寄せていると、後述のとおり親テンプレート容量と循環依存の両方で不利になりやすいです。

公式ドキュメントの例は、モデル自動生成の CRUD を業務単位へ振る書き方です。FunctionDirectiveStack の分散とは別用途ですが、指定の形は同じです。

stackMappings: {
  CreateOrderResolver: 'OrderMutations',
  UpdateOrderResolver: 'OrderMutations',
}

移行手順

全体フロー

Step 1: 対象 Resolver の特定

論理 ID は推測せず、公式どおり npx ampx sandbox または cdk synth の成果物 から取得します。CloudFormation コンソールのテンプレートでも確認できます。

カスタム Query / Mutation の論理 ID は、だいたい次の形です。

  • Query: Query<フィールド名>Resolver(例: searchItems → QuerysearchItemsResolver)
  • Mutation: Mutation<フィールド名>Resolver(例: exportReport → MutationexportReportResolver)

大文字小文字を含め、成果物と完全一致させてください。同じ Lambda を複数フィールドが使う場合は、同じ移行先にまとめると運用しやすいです。

Step 2: 移行先の選定

stackMappings の値は 任意の NestedStack 名 です。公式例はドメイン / 操作種別への振り分けです。それ以外は実務で使う切り方で、どれか 1 つに決め打ちする必要はありません。

分け方 内容 向くケース
ドメイン / 操作種別 OrderMutations のように業務や CRUD で切る 公式例。チームやデプロイ境界を分けたいとき
退避スタック 1 本 溢れた Resolver を同じ先(例: SharedResolvers)へ まず 500 上限を回避したいとき
既存モデルスタックへ相乗り Todo など、すでに存在するモデル用 NestedStack へ載せる そのモデル専用で本数が少なく、循環依存がないとき
番号付き分割 CustomStack1, CustomStack2 … 1 本の退避先も上限に近づいたとき

運用上、移行先にしないスタック:

  • FunctionDirectiveStack(移行元そのもの)
  • ConnectionStack / AmplifyTableManager など、責務が不明な Amplify 内部スタック

新規 NestedStack を Lambda ごと・API ごとに増やすと、親 data テンプレートに AWS::CloudFormation::Stack が追加され、別の容量問題になります。切り方を決めたら、同じ方針でまとめる方が安全です。

既存モデルスタックへ載せる場合は、移行先に十分な余裕があること(500 リソース / 1MB に近づいていないこと)と、後述の循環依存に当たらないことを確認してください。

Step 3: デプロイ(未デプロイ vs デプロイ済み)

未デプロイ(初回追加前)なら、stackMappings を足してからデプロイするだけです。

デプロイ済みの Resolver は、CloudFormation 上スタック間をインプレース移動できません。公開型定義どおり、一度アプリから外して別スタックで再追加します。2 段階デプロイが必要です。

デプロイ 1: Resolver 削除

  1. 対象 Query / Mutation をスキーマから一時削除する
  2. 呼び出し元が対象 API を使用しない状態を確認する
  3. 開発環境 / ステージング / 本番へデプロイし、Resolver 削除を完了する

デプロイ 2: mapping 付き再追加

  1. Query / Mutation を元の契約(フィールド名・引数・戻り値・認可)で戻す
  2. 同時に Resolver 論理 ID の stackMappings を追加する
  3. 再デプロイする

ステージングや本番では API 一時停止 が発生します。削除デプロイ前に影響範囲を確認してください。フィールド名・引数・戻り値・認可規則は変更しません。移行はインフラ配置だけの変更です。

Step 4: 検証

npx ampx sandbox などでデプロイしたあと、.amplify/artifacts/cdk.out の NestedStack テンプレート、または CloudFormation コンソールで次を確認します。

  • synth / デプロイ成功
  • 対象 Resolver が 移行先テンプレートに存在し、FunctionDirectiveStack に存在しない
  • DataSource / FunctionConfiguration は 元スタックに残る
  • 各スタックが 500 リソース未満、各テンプレートが 1MB 未満
  • API の正常系・認可拒否が従来どおり

運用上の注意点

ここからは公開型定義にない、実運用で確認した点です。

循環依存を避ける

公式の循環依存トラブルシュートが扱うのは、主に data スタック ↔ function スタック です。回避策として resourceGroupName: 'data' で Lambda を data 側へ寄せる案内がありますが、stackMappings が必要になる規模ではあまり向きません。Lambda / IAM が data 親に増え、500 リソースや 1MB の別枠を圧迫します。

加えて、data 配置の Lambda がモデルテーブルを CDK の Construct 参照(backend.data.resources.tables など)で繋いでいると、そのモデルスタックへ Resolver を mapping したときに循環依存になります。

resourceGroupName: 'data' の Lambda
  --(テーブルの CFN 参照)--> モデル NestedStack
       ^                              |
       |                              v
FunctionDirectiveStack <--(stackMappings)-- 同モデル NestedStack

この組み合わせでは、該当モデルへは載せないか、先にテーブル参照を環境変数など CDK 非参照の方法へ切り替えてください。Resolver 用 Lambda は Function スタックのまま、テーブル名を環境変数で渡す方が、容量と循環の両方を避けやすいです。

段階的に移行する

一度にすべての Resolver を移す必要はありません。

  1. まず上限に近い / 追加予定の Resolver から着手する
  2. ドメイン単位・退避 1 本・モデル相乗りなど、プロジェクトで使う切り方を決める
  3. 新規 Resolver は 未デプロイのうち mapping を追加 する

ハマりどころ

問題 対処
デプロイ済み Resolver の移動 2 段階デプロイ必須(API 一時停止)
論理 ID の推測 sandbox / synth 成果物から取得(大文字小文字含め完全一致)
a.handler.custom に mapping しても効かない 対象は a.handler.function の Resolver のみ。custom は data 親へ直接生成される
循環依存 テーブルを CDK 参照しているモデルスタックへは載せない。resourceGroupName: 'data' で Lambda を寄せると親容量も増える
TooManyResourcesInStack / 500 超過 FunctionDirectiveStack の Resolver を stackMappings で分散
Template may not exceed 1000000 bytes 新規 NestedStack の乱立を避ける。親 data の 1MB も確認

ロールバック

sandbox で試している場合は、直前に足した mapping だけを外して再デプロイすれば戻ります。本番へ 2 段階デプロイ済みの場合は、同じ手順を逆向き(mapping 付き削除 → 元の配置で再追加)になり、こちらも API 一時停止が発生します。


まとめ

カスタム Resolver が増えると、FunctionDirectiveStack が 500 リソース / 1MB 上限 に当たることがあります。当たったら(または直前になったら)stackMappings で分散します。

  1. 理由: カスタム Lambda Resolver(a.handler.function)は FunctionDirectiveStack に集約され、上限に達するとデプロイが止まる
  2. 使い方: Amplify Backend 1.23.0(backend-data 1.7.0)の stackMappings で、Resolver 論理 ID を別 NestedStack へ振り分ける。対象は Resolver のみ
  3. 移行先: 任意の NestedStack 名を指定できる。公式例はドメイン / 操作種別。実務では退避 1 本やモデル相乗りも使う。スタックの乱立は避ける
  4. デプロイ: 未デプロイなら mapping 追加のみ。デプロイ済みは削除 → mapping 付き再追加の 2 段階

公式は、上限に遭遇していない場合の利用を非推奨としています。Resolver を継続的に追加するプロジェクトでは、上限の手前で移行計画を持っておくと、デプロイ停止を防げます。


参考リンク

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?