はじめに
最近、Amplify Gen2を使ってWebアプリケーションの構築・デプロイを行うことが多くなりました。Claude Code様様です。Amplify Gen2ではバックエンドリソースも構築できるのですが、テンプレートを見ると、authとdataというディレクトリが存在します。authはAmazon Cognitoを定義するもので、dataはAppSync GraphQL + Amazon DynamoDBを定義するものです。「そういえばAppSyncってなんだ?」となったので試しながら調べてみました。
GraphQLとは
AppSyncの話の前に、まずはGraphQLについて整理します。
GraphQLは、GraphQLという仕様に基づいたAPIのクエリ言語で、必要な情報のみ取得できる特徴があります。
オーバーフェッチング
比較として、REST APIの例を出します。REST APIでは、エンドポイントごとにレスポンスの形が固定されています。例えば GET /users/{id} というエンドポイントがあるとして、呼び出すと次のようなレスポンスが返ってくるとします。
{
"id": "001",
"name": "あるけみー",
"email": "alchemy@example.com",
"address": "○○県...",
"phone": "0A0-XXXX-XXXX",
"createdAt": "2026-04-01T00:00:00Z"
}
もし画面でユーザー名だけ表示したい場合でも、name 以外の不要なフィールドも全部返ってきます。これをオーバーフェッチングと呼びます。
不要なデータを毎回転送し続けるため、ネットワーク帯域幅が無駄に消費されます。トラフィックが少ない場合にはさほど影響はないですが、大量のトラフィックが発生するWebシステムにおいて、使用しない情報を取得することで通信量が多くなりパフォーマンスに影響する可能性があります。
アンダーフェッチング
逆に、1つのエンドポイントで必要なデータが全部揃わず、複数回リクエストが必要になることもあります。例えば「ユーザー情報とそのユーザーの注文履歴を一緒に取得したい」という場合、GET /users/{id} と GET /orders?userId={id} の2回リクエストしなければなりません。これをアンダーフェッチングと呼びます。リクエスト回数が増えることでパフォーマンスに影響が出る可能性があります。
GraphQLによる解決
GraphQLはこれらの問題を解決するために設計されており、クライアント側が必要なフィールドだけ指定できることが最大の特徴です。1つのエンドポイントに対して必要なデータ構造を記述してリクエストします。
query {
getUser(id: "001") {
name
}
}
このクエリを送れば name だけが返ってきます。関連データをまとめて取得することも可能です。
query {
getUser(id: "001") {
name
orders {
id
amount
}
}
}
1回のリクエストでユーザー情報と注文履歴を同時に取得できます。
AWS AppSyncとは
AWS AppSyncは、GraphQLおよびPub/Sub APIを提供するAWSのフルマネージドサービスです。GraphQLサーバーを自前で構築・運用する手間なく、DynamoDBやAWS Lambda、その他のデータソースと接続したGraphQL APIをすぐに作れます。
本記事ではGraphQL APIの作成を扱います。
API Gateway REST APIとの違い
どちらが良いというわけではなく、ユースケース次第です。シンプルなCRUD APIや既存のLambdaをそのままAPIとして公開したい場合はAmazon API Gatewayの方が向いていると考えられます。一方でフロントエンドからのデータ取得を柔軟にしたい、リアルタイム機能が必要、複数のバックエンドを統合したいといった要件があればAppSyncが向いています。
実際にAPIを作ってみる
ここからはAWSコンソールからAppSyncのAPIを作成していきます。AWSのクイックスタートドキュメントに沿って進めていきます。
AppSyncのGraphQL APIには、3つの主要コンポーネントがあります。
- スキーマ: APIの設計図。どんな操作(クエリ・ミューテーション)ができるか、どんなデータ型があるかを定義
-
データソース: データの格納先。DynamoDB、Lambdaなどを接続できます
- Lambdaはデータを保持しているわけではないですが、Lambdaを介してさまざまなデータソースにアクセス可能なため、Lambdaも設定可能となっているようです
- リゾルバー: スキーマのフィールドとデータソースを繋ぐロジック。JavaScriptランタイム(APPSYNC_JS)で記述します。リゾルバーのランタイムとしてVTL(Velocity Template Language)も選択可能ですが、現在はAPPSYNC_JSが主にサポートされています
We now primarily support the APPSYNC_JS runtime and its documentation.
https://docs.aws.amazon.com/appsync/latest/devguide/tutorials.html
AppSync APIの作成
AWSコンソールからAppSyncを開き、「APIを作成」を選択し、GraphQL APIを選びます。
作成方法は複数選択肢がありますが、今回は「Design from scratch」を選択します。
API名などはデフォルトにし、「次へ」を押します。
ちなみに、API Gateway同様にPrivate APIも作成できるようですね。
次はリソースを定義します。「DynamoDB テーブルを使用するタイプを今すぐ作成」を選択し、モデル名とフィールドを入力します。
タイプにはプログラミングで使われるStringやBool以外にもIDやEmailなどが指定できます。
この時点では以下のようなスキーマ定義になります。必須のパラメータは型に「!」がつきます。
type Todo {
id: ID!
name: String
when: String
where: String
description: String
}
モデルテーブル名を入力します。これが作成されるDynamoDBのテーブル名となります。Primary keyにはidを指定します。
最後に確認画面が出るので作成をします。
作成されたスキーマなどを確認
スキーマ
作成されたスキーマを確認すると、CreateTodoInputやDeleteTodoInputといったいわゆるCRUD操作に利用するスキーマが自動で定義されます。
input CreateTodoInput {
name: String
when: String
where: String
description: String
}
input DeleteTodoInput {
id: ID!
}
input ModelSizeInput {
ne: Int
eq: Int
le: Int
lt: Int
ge: Int
gt: Int
between: [Int]
}
input TableBooleanFilterInput {
ne: Boolean
eq: Boolean
attributeExists: Boolean
}
input TableFloatFilterInput {
ne: Float
eq: Float
le: Float
lt: Float
ge: Float
gt: Float
between: [Float]
attributeExists: Boolean
}
input TableIDFilterInput {
ne: ID
eq: ID
le: ID
lt: ID
ge: ID
gt: ID
contains: ID
notContains: ID
between: [ID]
beginsWith: ID
attributeExists: Boolean
size: ModelSizeInput
}
input TableIntFilterInput {
ne: Int
eq: Int
le: Int
lt: Int
ge: Int
gt: Int
between: [Int]
attributeExists: Boolean
}
input TableStringFilterInput {
ne: String
eq: String
le: String
lt: String
ge: String
gt: String
contains: String
notContains: String
between: [String]
beginsWith: String
attributeExists: Boolean
size: ModelSizeInput
}
input TableTodoFilterInput {
id: TableIDFilterInput
name: TableStringFilterInput
when: TableStringFilterInput
where: TableStringFilterInput
description: TableStringFilterInput
}
type Todo {
id: ID!
name: String
when: String
where: String
description: String
}
type TodoConnection {
items: [Todo]
nextToken: String
}
input UpdateTodoInput {
id: ID!
name: String
when: String
where: String
description: String
}
type Mutation {
createTodo(input: CreateTodoInput!): Todo
updateTodo(input: UpdateTodoInput!): Todo
deleteTodo(input: DeleteTodoInput!): Todo
}
type Query {
getTodo(id: ID!): Todo
listTodos(filter: TableTodoFilterInput, limit: Int, nextToken: String): TodoConnection
}
type Subscription {
onCreateTodo(
id: ID,
name: String,
when: String,
where: String,
description: String
): Todo
@aws_subscribe(mutations: ["createTodo"])
onUpdateTodo(
id: ID,
name: String,
when: String,
where: String,
description: String
): Todo
@aws_subscribe(mutations: ["updateTodo"])
onDeleteTodo(
id: ID,
name: String,
when: String,
where: String,
description: String
): Todo
@aws_subscribe(mutations: ["deleteTodo"])
}
データソース
データソースではDynamoDBが作成されたことを確認できます。
クエリ
クエリでは作成したデータソースに対してクエリを行い、テストを行うことができます。
試しにVariablesの値を書き換えて、createTodoをしてみます。すると、idが自動で振られ、DynamoDBにもデータが登録されます。
Deleteのクエリを実行すると、先ほど登録された内容がDynamoDBから削除されていることが確認できました。

なお、クエリ作成はExplorerからクエリを作成することもできます。
キャッシュ
この時点では作成していませんが、Amazon ElastiCache(Redis OSS)によるキャッシュ層を設定することもできます。これによりAPIのレイテンシー低減に寄与しますが、インスタンスタイプにより時間課金が発生します。
設定
作成されたAPIの詳細情報などが確認できます。ログの設定もこの項目から可能です。
中でもプライマリ認証モードで、APIキーの作成が可能です。(API作成時、APIキーも自動で作成されていました。)
API GatewayにもAPIキー(x-api-keyヘッダー)の仕組みはありますが、これは使用量プランの識別が主目的で、認可手段としての利用は推奨されていません。
対してAppSyncでは下記の記載があります。
API キーは、パブリック API の公開が安全であるユースケース、または開発目的での使用が推奨されます。クライアントでは、API キーをヘッダー x-api-key で指定します。
すなわち、用途次第ではAPIキーによる認可も可能と読み取れます。ただ、AppSyncも複数の認可方法が提供されているので、必要に応じて適切な方法を選んだほうが良いでしょう。
クリーンアップ
今回作成したリソースを削除する場合は、AppSyncのAPIを削除すれば紐づくAPIキー・スキーマ・リゾルバーもまとめて削除されます。ただし、DynamoDBのテーブルは別途削除が必要です。
まとめ
Amplifyを使う中で、さりげなく使っているAppSyncについて入門してみましたが、想像よりは簡単に扱えると思いました。一方で、設計思想等を理解する必要がある点や、REST APIと比べるとまだまだ浸透していないと感じる部分もあります。
今後はREST API一択(だからAPI Gateway)というわけではなく、要件に応じて使うことも検討したいです。
参考












