CDK TypeScriptを用いてAWS AppSync試してみた
リテールアプリ共創部のるおんです。先日、AppSyncとGraphQLを用いたアプリケーションの開発に携わる機会がありました。その際、初めてAppSyncを使用したので入門してみました。今回は、その際に実装したAppSyncとDynamoDBを組み合わせた簡単なAPIの実装方法を共有したいと思います。
AppSyncとは
AWS AppSyncは、AWSが提供するマネージドGraphQLサービスです。リアルタイムデータ同期、オフラインプログラミング機能を備え、大規模なアプリケーションの開発を容易にします。また、DynamoDB、Aurora、OpenSearchなど、様々なAWSサービスと簡単に統合できるのが特徴です。
全体像
今回は、AWS CDKを用いてAppSyncとDynamoDBを連携させたシンプルなGraphQL APIを構築します。使用した言語はTypeScriptです。
また、基本的なGraphQLクエリの構築に慣れていることを前提としています。
今回の構成では、AppSyncがGraphQL APIのエンドポイントとして機能し、DynamoDBがデータストアとして使用されます。
DynamoDBテーブルでは、「title(タイトル)」と「author(著者)」の情報を保存するシンプルな構造のテーブルを作成します。
CDKでなく、マネジメントコンソールから実装する場合、以下の記事が参考になりました。
CDKで実装してみる
それでは、この構成をCDKで実装していきましょう。TypeScriptが使用可能で、AWS CDKを用いて手元のマシンからデプロイができる環境が整っていることを前提に進めていきます。
1. セットアップ
まず、プロジェクトを作成してCDKを開始します。
mkdir appsync-demo
cd appsync-demo
cdk init app --language typescript
2. スタックの実装
早速ですが以下が完全なスタックコードです。
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as appsync from 'aws-cdk-lib/aws-appsync';
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
export class AppsyncDemoStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// AppSync API の作成
const api = new appsync.GraphqlApi(this, 'Api', {
name: 'demo-api',
definition: appsync.Definition.fromFile('schema.graphql'),
});
// DynamoDB テーブルの作成
const table = new dynamodb.Table(this, 'BooksTable', {
tableName: 'books',
partitionKey: { name: 'title', type: dynamodb.AttributeType.STRING },
});
// DynamoDB をデータソースとして追加
const dataSource = api.addDynamoDbDataSource('ItemsDataSource', table);
// リゾルバーの追加
// getBooks リゾルバー
dataSource.createResolver("getBooksResolver", {
typeName: 'Query',
fieldName: 'getBooks',
requestMappingTemplate: appsync.MappingTemplate.dynamoDbScanTable(),
responseMappingTemplate: appsync.MappingTemplate.dynamoDbResultList(),
});
// createBook リゾルバー
dataSource.createResolver("createBookResolver", {
typeName: 'Mutation',
fieldName: 'createBook',
requestMappingTemplate: appsync.MappingTemplate.dynamoDbPutItem(
appsync.PrimaryKey.partition('title').is('input.title'),
appsync.Values.projecting('input')
),
responseMappingTemplate: appsync.MappingTemplate.dynamoDbResultItem(),
});
}
}
'demo-api'という名前のAppSync APIを作成し、スキーマはschema.graphql
ファイルから読み込むようにしています。
また、books
という名前のDynamoDBテーブルを作成し、パーティションキーとして文字列型のtitle
を指定しています。
作成したDynamoDBテーブルをAppSync APIのデータソースとして追加し、getBooks
クエリととcreateBook
ミューテーションに対応するリゾルバーを作成しています。
このコードにより、基本的なCRUD操作(Create, Read)が可能なGraphQL APIが構築されます。ただし、更新(Update)と削除(Delete)の操作は含まれていないため、必要に応じて追加することができます。
3. GraphQLスキーマの定義
schema.graphql
ファイルを作成し、以下のスキーマを定義します。
type Book {
title: String!
author: String!
}
input CreateBookInput {
title: String!
author: String!
}
type Query {
getBooks: [Book]
}
type Mutation {
createBook(input: CreateBookInput!): Book
}
このGraphQLスキーマは以下の要素で構成されています:
- Book 型:
title と author の2つのフィールドを持つ本の基本構造を定義します。
両フィールドとも String! 型で、非nullであることを示しています。 - reateBookInput 入力型:
新しい本を作成する際に使用する入力データの構造を定義します。
Book 型と同じフィールドを持ちますが、入力データとして使用されます。 - Query 型:
getBooks クエリを定義し、これは Book 型の配列を返します。
このクエリにより、すべての本のリストを取得できます。 - Mutation 型:
createBook ミューテーションを定義します。
CreateBookInput! 型の input 引数を受け取り、新しく作成された Book を返します。
このスキーマにより、クライアントは本のリストを取得したり(getBooksクエリ)、新しい本を追加したり(createBookミューテーション)することができます。スキーマはAPIの契約として機能し、クライアントとサーバー間でデータ構造と操作を明確に定義します。
動作確認
デプロイ
以下のコマンドでスタックをデプロイします。
cdk deploy
デプロイが完了すると、AppSyncのAPIが作成されていると思います。また、DynamoDBにbookテーブルが作成されており、ここにはまだ何も項目がないことを確認してください。
マネジメントコンソールからの実行
AWS Management ConsoleにログインしてAppSyncのダッシュボードに移動します。
作成したAPIを選択し、「クエリ」タブを開きます。
このタブを開くと、以下の画像のようにGraphQL操作を直接テストできるインターフェースが提供されます。
まだ、何もクエリとクエリ変数が設定されていないと思うので、以下のコードをそれぞれ記述してみます。
クエリ:
query getBooks {
getBooks {
title
author
}
}
mutation createBook($createbookinput: CreateBookInput!) {
createBook(input: $createbookinput) {
title
author
}
}
クエリ変数:
{
"createbookinput": {
"title": "AppSync入門!",
"author": "るおん"
}
}
設定ができたら、実行してみます。
「実行する」ボタンを押して、createBook
を選択します。すると、クエリ変数として渡した値でDynamoDBテーブル内に項目が作成されます。
以下の赤線内ように実行結果のレスポンスが返却されています。そしてDynamoDBを確認すると、実際に項目が追加されていることがわかります。
次に、「実行する」ボタンからgetBooks
を選択してみます。すると、DynamoDB内のbooksテーブル内の項目が返却されて先ほど作成した項目が返却されることが確認できると思います。
無事APIのクエリが実行できることが確認できました!
Node.jsからの実行
次は、Node.jsからAxiosを用いてAPIを実行してみます。API URLとAPIキーを自身のものに置き換えてください。
const axios = require("axios");
axios.post("https://xxxxxxxxxxxxx.appsync-api.ap-northeast-1.amazonaws.com/graphql", {
query: /* GraphQL */`
query getBooks {
getBooks {
title
author
}
}`
}, {
headers: {
'x-api-key': "xxx-xxxxxxxxxxxxxxxxxx",
},
}).then((response) => {
console.log(JSON.stringify(response.data));
}).catch((error) => {
console.error(error);
})
このスクリプトを実行すると、マネジメントコンソールでテストしたように、booksテーブル内の項目が返却されます。
node index.js
# 出力:{"data":{"getBooks":[{"title":"AppSync入門!","author":"るおん"}]}}
AppSyncを用いてGraphQL APIを無事実行できることが確認できました!
おわりに
今回はCDK TypeScriptを使ってAppSyncとDynamoDBを組み合わせたシンプルなGraphQL APIを構築しました。マネジメントコンソールとNode.jsクライアントの両方から動作確認を行い、AppSyncの基本的な使い方を説明しました。
今後は、より複雑なスキーマやリゾルバー、認証機能の追加など、AppSyncの高度な機能にも挑戦してみたいと思います。
参考になれば幸いです。