Aurora MySQL が RDS Data API を遂にサポート!AppSync で爆速 GraphQL API を作ってみた - Qiita (2025)

@inada_riku(稲田 大陸)
  • AWS
  • Aurora
  • AppSync

Last updated at Posted at 2024-10-02

はじめに

本記事は 2024 年 10 月 2 日時点の情報に基づいて執筆しています。
内容に不備ありましたら気軽にコメントください!

こんにちは。いなりくです✋

2023 年 11 月 27 日に、AWS AppSync は RDS Data API を使用して Amazon Aurora クラスター内のデータベースに対してイントロスペクションを行うことで、検出したテーブルに適合した GraphQL API のインターフェイスを簡単に作成することが可能になりました。

以下の記事では、このアップデートを Amazon Aurora Serverless v2 (PostgreSQL) に試したものです。しかし、前回記事の執筆時点では Aurora MySQL では、Data API は Aurora Serverless v1 データベースでのみサポートだったため、あと一歩の検証となりました。

そんな記事を書いたことすらさっぱりと忘れてたのですが、先日 X を眺めていると、こんなアップデートがありました。「遂に来た!」と思い、Aurora MySQL でも早速試してみることとします。

RDS Data API の対応状況

検証を始める前に、自分の理解のためにも RDS Data API の今までの対応状況について調べてみました。

エンジン クラスタータイプ 2023 2024
- 12/20 12/21 - 9/26 -
PostgreSQL Serverless v2 / Provisioned ×
Serverless v1
MySQL Serverless v2 / Provisioned × ×
Serverless v1

2023 年 12 月 21 日に以下のアップデートが来ました。

2024 年 9 月 26 日に以下のアップデートが来ました。

余談ですが、公式ドキュメントにもあるように、MySQL 8.0 または PostgreSQL 13 上でアプリケーションを実行できる場合は、Aurora Serverless v2 を使用することをお勧めします。

Important (公式ドキュメントから引用)
Aurora has two generations of serverless technology, Aurora Serverless v2 and Aurora Serverless v1. If your application can run on MySQL 8.0 or PostgreSQL 13, we recommend that you use Aurora Serverless v2. Aurora Serverless v2 scales more quickly and in a more granular way. Aurora Serverless v2 also has more compatibility with other Aurora features such as reader DB instances. Thus, if you're already familiar with Aurora, you don't have to learn as many new procedures or limitations to use Aurora Serverless v2 as with Aurora Serverless v1.

やってみた

では、やっていきます。基本的な手順に関しては公式ドキュメントのリンクを載せているのでそちらを参照ください。

Step1. Amazon Aurora Serverless v2 (MySQL) の作成

まず、Aurora Serverless v2 (MySQL) の DB クラスタを作成します。手順については「Aurora MySQL DB クラスターの作成と接続」を参考にしてください。

注意
エンジンバージョンがデフォルトではAurora MySQL 3.05.2 (compatible with MySQL 8.0.32) - メジャーバージョンのデフォルト 8.0 になっているので、Aurora MySQL 3.07.0 (compatible with MySQL 8.0.36) に変える必要がある点だけ注意してください。
Aurora MySQL が RDS Data API を遂にサポート!AppSync で爆速 GraphQL API を作ってみた - Qiita (2)

また、DB クラスター作成時に、以下のようなチェックボックスができるのでチェックを入れます。忘れてしまった場合もあとから有効化は可能です。

Aurora MySQL が RDS Data API を遂にサポート!AppSync で爆速 GraphQL API を作ってみた - Qiita (3)

認証情報は AWS Secret Manager で管理します。後ほど、認証情報は AWS AppSync からイントロスペクションを実施する際にも利用します。

Aurora MySQL が RDS Data API を遂にサポート!AppSync で爆速 GraphQL API を作ってみた - Qiita (4)

DB クラスターの作成が完了したら、データベースの作成を行います。今回は「sample_db」とします。

CREATE DATABASE sample_db;

次に、テーブルの作成を行います。以下のようなテーブル定義にします。

CREATE TABLE conversations ( id INT NOT NULL PRIMARY KEY, name VARCHAR(255) NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE messages ( id VARCHAR(36) PRIMARY KEY, conversation_id INT NOT NULL, sub VARCHAR(36) NOT NULL, body TEXT NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (conversation_id) REFERENCES conversations(id) ); CREATE TABLE conversation_participants ( conversation_id INT NOT NULL, sub varchar(36) NOT NULL, last_read_at DATETIME, PRIMARY KEY (conversation_id, sub), FOREIGN KEY (conversation_id) REFERENCES conversations(id) );

テーバルが正常に作成されたか確認します。

MySQL [sample_db]> show tables;+---------------------------+| Tables_in_sample_db |+---------------------------+| conversation_participants || conversations || messages |+---------------------------3 rows in set (0.002 sec)

Step2. GraphQL API の作成

次に、GraphQL API を作成しましょう。GraphQL API データソースで「Amazon Aurora クラスターから始める - New」を選択し、次に進みます。

Aurora MySQL が RDS Data API を遂にサポート!AppSync で爆速 GraphQL API を作ってみた - Qiita (5)

次に、API 名にわかり易い名前を入力します。今回はデフォルトの「My AppSync API」としました。

Aurora MySQL が RDS Data API を遂にサポート!AppSync で爆速 GraphQL API を作ってみた - Qiita (6)

次にデータベースを選択します。

Aurora MySQL が RDS Data API を遂にサポート!AppSync で爆速 GraphQL API を作ってみた - Qiita (7)

「データベースを選択」をクリックすると接続したい Amazon Aurora クラスターが選択出来ます。データベースには「sample_db」と入力します。AWS Secret Manager シークレットは Step1 で作成済みのものを選択します。入力が終えたら「インポート」をクリックします。

Aurora MySQL が RDS Data API を遂にサポート!AppSync で爆速 GraphQL API を作ってみた - Qiita (8)

「インポート」をクリックするとイントロスペクションが開始され、下の画像のようにデータベースのテーブルがインポートされました。タイプ名はカスタマイズすることができるので、以下のように変更します。(画像は変更前です。)

  • Conversation_participants : Participant
  • Conversations : Conversation
  • Messages : Message

問題なければ、「次へ」をクリックします。

Aurora MySQL が RDS Data API を遂にサポート!AppSync で爆速 GraphQL API を作ってみた - Qiita (9)

スキーマの設定では、クエリのみを作成するか、クエリ、ミューテーション、サブスクリプションを作成するかを選択することが出来ます。今回は、「すべてのモデルに対してクエリ、ミューテーション、サブスクリプションを作成」を選択します。これで GraphQL API の作成は完了です!

Aurora MySQL が RDS Data API を遂にサポート!AppSync で爆速 GraphQL API を作ってみた - Qiita (10)

Step4. 動作確認

では、実際にどんなスキーマやリゾルバーが作成されたか見てみましょう。まずはスキーマを見てみましょう。

確認 1 : GraphQL スキーマ

以下が自動で生成されたスキーマです。Aurora PostgreSQL で試したときと同様にスキーマが正しく作成できていることが確認できました。

schema.graphql

schema.graphql

type Conversation {id: Int!name: String!created_at: String}type ConversationConnection {items: [Conversation]nextToken: String}input CreateConversationInput {id: Int!name: String!created_at: String}input CreateMessageInput {id: String!conversation_id: Int!sub: String!body: String!created_at: String}input CreateParticipantInput {conversation_id: Int!sub: String!last_read_at: String}input DeleteConversationInput {id: Int!}input DeleteMessageInput {id: String!}input DeleteParticipantInput {conversation_id: Int!sub: String!}type Message {id: String!conversation_id: Int!sub: String!body: String!created_at: String}type MessageConnection {items: [Message]nextToken: String}input ModelSizeInput {ne: Inteq: Intle: Intlt: Intge: Intgt: Intbetween: [Int]}enum ModelSortDirection {ASCDESC}input OrderByConversationInput {id: ModelSortDirectionname: ModelSortDirectioncreated_at: ModelSortDirection}input OrderByMessageInput {id: ModelSortDirectionconversation_id: ModelSortDirectionsub: ModelSortDirectionbody: ModelSortDirectioncreated_at: ModelSortDirection}input OrderByParticipantInput {conversation_id: ModelSortDirectionsub: ModelSortDirectionlast_read_at: ModelSortDirection}type Participant {conversation_id: Int!sub: String!last_read_at: String}type ParticipantConnection {items: [Participant]nextToken: String}input TableConversationConditionInput {name: TableStringFilterInputcreated_at: TableStringFilterInputand: [TableConversationConditionInput]or: [TableConversationConditionInput]not: [TableConversationConditionInput]}input TableConversationFilterInput {id: TableIntFilterInputname: TableStringFilterInputcreated_at: TableStringFilterInputand: [TableConversationFilterInput]or: [TableConversationFilterInput]not: [TableConversationFilterInput]}input TableIntFilterInput {ne: Inteq: Intle: Intlt: Intge: Intgt: Intbetween: [Int]attributeExists: Boolean}input TableMessageConditionInput {conversation_id: TableIntFilterInputsub: TableStringFilterInputbody: TableStringFilterInputcreated_at: TableStringFilterInputand: [TableMessageConditionInput]or: [TableMessageConditionInput]not: [TableMessageConditionInput]}input TableMessageFilterInput {id: TableStringFilterInputconversation_id: TableIntFilterInputsub: TableStringFilterInputbody: TableStringFilterInputcreated_at: TableStringFilterInputand: [TableMessageFilterInput]or: [TableMessageFilterInput]not: [TableMessageFilterInput]}input TableParticipantConditionInput {last_read_at: TableStringFilterInputand: [TableParticipantConditionInput]or: [TableParticipantConditionInput]not: [TableParticipantConditionInput]}input TableParticipantFilterInput {conversation_id: TableIntFilterInputsub: TableStringFilterInputlast_read_at: TableStringFilterInputand: [TableParticipantFilterInput]or: [TableParticipantFilterInput]not: [TableParticipantFilterInput]}input TableStringFilterInput {ne: Stringeq: Stringle: Stringlt: Stringge: Stringgt: Stringcontains: StringnotContains: Stringbetween: [String]beginsWith: StringattributeExists: Booleansize: ModelSizeInput}input UpdateConversationInput {id: Int!name: Stringcreated_at: String}input UpdateMessageInput {id: String!conversation_id: Intsub: Stringbody: Stringcreated_at: String}input UpdateParticipantInput {conversation_id: Int!sub: String!last_read_at: String}type Mutation {createParticipant(input: CreateParticipantInput!): ParticipantupdateParticipant(input: UpdateParticipantInput!, condition: TableParticipantConditionInput): ParticipantdeleteParticipant(input: DeleteParticipantInput!, condition: TableParticipantConditionInput): ParticipantcreateConversation(input: CreateConversationInput!): ConversationupdateConversation(input: UpdateConversationInput!, condition: TableConversationConditionInput): ConversationdeleteConversation(input: DeleteConversationInput!, condition: TableConversationConditionInput): ConversationcreateMessage(input: CreateMessageInput!): MessageupdateMessage(input: UpdateMessageInput!, condition: TableMessageConditionInput): MessagedeleteMessage(input: DeleteMessageInput!, condition: TableMessageConditionInput): Message}type Query {getParticipant(conversation_id: Int!, sub: String!): ParticipantlistParticipants(filter: TableParticipantFilterInput,limit: Int,orderBy: [OrderByParticipantInput],nextToken: String): ParticipantConnectiongetConversation(id: Int!): ConversationlistConversations(filter: TableConversationFilterInput,limit: Int,orderBy: [OrderByConversationInput],nextToken: String): ConversationConnectiongetMessage(id: String!): MessagelistMessages(filter: TableMessageFilterInput,limit: Int,orderBy: [OrderByMessageInput],nextToken: String): MessageConnection}type Subscription {onCreateParticipant(conversation_id: Int, sub: String, last_read_at: String): Participant@aws_subscribe(mutations: ["createParticipant"])onUpdateParticipant(conversation_id: Int, sub: String, last_read_at: String): Participant@aws_subscribe(mutations: ["updateParticipant"])onDeleteParticipant(conversation_id: Int, sub: String, last_read_at: String): Participant@aws_subscribe(mutations: ["deleteParticipant"])onCreateConversation(id: Int, name: String, created_at: String): Conversation@aws_subscribe(mutations: ["createConversation"])onUpdateConversation(id: Int, name: String, created_at: String): Conversation@aws_subscribe(mutations: ["updateConversation"])onDeleteConversation(id: Int, name: String, created_at: String): Conversation@aws_subscribe(mutations: ["deleteConversation"])onCreateMessage(id: String,conversation_id: Int,sub: String,body: String,created_at: String): Message@aws_subscribe(mutations: ["createMessage"])onUpdateMessage(id: String,conversation_id: Int,sub: String,body: String,created_at: String): Message@aws_subscribe(mutations: ["updateMessage"])onDeleteMessage(id: String,conversation_id: Int,sub: String,body: String,created_at: String): Message@aws_subscribe(mutations: ["deleteMessage"])}

確認 2 : リゾルバー

次に、リゾルバーを見てみます。以下は createMessage のリゾルバーコードです。
JavaScript で書かれているので読みやすいですね。

import { util } from '@aws-appsync/utils';import { insert, createPgStatement, toJsonObject } from '@aws-appsync/utils/rds';/** * Puts an item into the messages table using the supplied input. * @param {import('@aws-appsync/utils').Context} ctx the context * @returns {*} the request */export function request(ctx) { const { input } = ctx.args; const insertStatement = insert({ table: 'messages', values: input, returning: '*', }); return createPgStatement(insertStatement)}/** * Returns the result or throws an error if the operation failed. * @param {import('@aws-appsync/utils').Context} ctx the context * @returns {*} the result */export function response(ctx) { const { error, result } = ctx; if (error) { return util.appendError( error.message, error.type, result ) } return toJsonObject(result)[0][0]}

確認 3 : GraphQL スキーマの作成タイミング

最後に AWS AppSync の GraphQL スキーマ作成のタイミングを確認してみました。
conversations テーブルに status というカラムを追加してみました。

ALTER TABLE conversations ADD COLUMN status VARCHAR(100);

これで、 AWS AppSync コンソール側でスキーマの反映ができれば...と思いましたが、現時点 (2024 年 10 月 2 日) 時点ではスキーマの反映は GraphQL API を新規作成するタイミングだけでした。作成された schema.graphql に手を加えている場合 (例えば、別のデータソースを接続した、など)、上書きされてしまうリスクもあるので、あまり必要ではないのかなと思いました。ここはぜひ色んな方の意見も伺いたいです。

まとめ

今回は、AWS AppSync の RDS Data API を使用して Amazon Aurora Serverless v2 (MySQL) クラスター内のデータベースに対してイントロスペクションを行うことで、検出したテーブルに適合した GraphQL API のインターフェイスを作成する機能を試してみました。遂に、Aurora の任意のエンジン、クラスタータイプで RDS Data API を使って GraphQL API を爆速構築できるようになったことは嬉しいですね。

1

Go to list of users who liked

1

comment0

Go to list of comments

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

Sign upLogin

Aurora MySQL が RDS Data API を遂にサポート!AppSync で爆速 GraphQL API を作ってみた - Qiita (2025)

References

Top Articles
Latest Posts
Recommended Articles
Article information

Author: Rev. Porsche Oberbrunner

Last Updated:

Views: 6011

Rating: 4.2 / 5 (73 voted)

Reviews: 88% of readers found this page helpful

Author information

Name: Rev. Porsche Oberbrunner

Birthday: 1994-06-25

Address: Suite 153 582 Lubowitz Walks, Port Alfredoborough, IN 72879-2838

Phone: +128413562823324

Job: IT Strategist

Hobby: Video gaming, Basketball, Web surfing, Book restoration, Jogging, Shooting, Fishing

Introduction: My name is Rev. Porsche Oberbrunner, I am a zany, graceful, talented, witty, determined, shiny, enchanting person who loves writing and wants to share my knowledge and understanding with you.