DynamoDB, AppSync,GraphQLについて学んでみた
DynamoDB = MySQL = データストア
AppSync = APIサーバ = エンドポイント
GraphQL = SQL = 問い合わせ言語
って感じ?
DynamoDBは、スキーマレス(テーブル定義なし)ってのが違和感ある。
idなど、Partition Key(Primary Key)だけ決めて、あとはjsonでデータを入れる感じ?
AWSを使わなくても、dockerでローカルでも試せる。
1 2 3 4 5 6 7 8 |
# dockerでローカルdynamoDB起動 docker run -d -p 8000:8000 amazon/dynamodb-local # テーブル一覧 aws dynamodb list-tables --endpoint-url http://localhost:8000 { "TableNames": [] } |
phpMyAdminみたいなwindowsアプリ、NoSQL Workbench(無料、AWS公式GUIツール)もある
https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/workbench.settingup.html
テーブル作成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
$ aws dynamodb create-table \ > --table-name Users \ > --attribute-definitions AttributeName=id,AttributeType=S \ > --key-schema AttributeName=id,KeyType=HASH \ > --provisioned-throughput ReadCapacityUnits=1,WriteCapacityUnits=1 \ > --endpoint-url http://localhost:8000 { "TableDescription": { "AttributeDefinitions": [ { "AttributeName": "id", "AttributeType": "S" } ], "TableName": "Users", "KeySchema": [ { "AttributeName": "id", "KeyType": "HASH" } ], "TableStatus": "ACTIVE", "CreationDateTime": "2025-03-13T16:55:05.306000+09:00", "ProvisionedThroughput": { "LastIncreaseDateTime": "1970-01-01T09:00:00+09:00", "LastDecreaseDateTime": "1970-01-01T09:00:00+09:00", "NumberOfDecreasesToday": 0, "ReadCapacityUnits": 1, "WriteCapacityUnits": 1 }, "TableSizeBytes": 0, "ItemCount": 0, "TableArn": "arn:aws:dynamodb:ddblocal:000000000000:table/Users", "DeletionProtectionEnabled": false } } |
データ追加
1 2 3 4 |
aws dynamodb put-item \ --table-name Users \ --item '{"id": {"S": "1"}, "name": {"S": "Alice"}, "age": {"N": "30"}}' \ --endpoint-url http://localhost:8000 |
データ取得
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# IDで検索 $ aws dynamodb get-item \ > --table-name Users \ > --key '{"id": {"S": "1"}}' \ > --endpoint-url http://localhost:8000 { "Item": { "name": { "S": "Alice" }, "age": { "N": "30" }, "id": { "S": "1" } } } # 全件取得(scan) aws dynamodb scan --table-name Users --endpoint-url http://localhost:8000 |
id(partition key)でしか検索できないので、nameでも検索できるようにする。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# GSI(Global Secondary Index)追加 aws dynamodb update-table \ --table-name Users \ --attribute-definitions AttributeName=name,AttributeType=S \ --global-secondary-index-updates \ "[{\"Create\":{\"IndexName\": \"NameIndex\", \"KeySchema\": [{\"AttributeName\":\"name\",\"KeyType\":\"HASH\"}], \"ProvisionedThroughput\": {\"ReadCapacityUnits\": 1, \"WriteCapacityUnits\": 1}, \"Projection\": {\"ProjectionType\": \"ALL\"}}}]" \ --endpoint-url http://localhost:8000 # nameで検索(nameは予約語なので、ちょっと特殊な書き方) # --expression-attribute-names '{"#n": "name"}'(予約語をエスケープ) aws dynamodb query \ --table-name Users \ --index-name NameIndex \ --key-condition-expression "#n = :n" \ --expression-attribute-names '{"#n": "name"}' \ --expression-attribute-values '{":n": {"S": "Alice"}}' \ --endpoint-url http://localhost:8000 |
これらはAWS CLI(直接DynamoDBを叩く)コマンドなので、あんまり実務では使われない。
AppSync経由でGraphQLを使う方が基本。
AppSyncもローカルで試せる。Amplifyプロジェクトが必要。
CloudFormationも必要なので、AWSアカウントも必要だし、使い終わったらamplify deleteでスタック削除しないと、ずっと残ったまま!
1 2 3 4 5 6 7 8 9 10 11 12 |
mkdir amplify-appsync-mock cd amplify-appsync-mock amplify init # GraphQL API(AppSync)の追加。サンプルのToDoテーブルを作成 amplify add api ? Select from one of the below mentioned services: GraphQL ? Here is the GraphQL API that we will create. Select a setting to edit or continue Continue ? Choose a schema template: Single object with fields (e.g., “Todo” with ID, name, description) # ローカルにAppSync(GraphQLのAPIサーバ)起動 amplify mock api |
http://localhost:20002 でブラウザから操作できる!
GraphQL操作は、3種類だけ!
query = select
mutation = insert,update, delete
subscription = データのリアルタイム通知(TRIGGER, LISTEN)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# ToDoテーブルがあるので、そこへデータ挿入する mutation { createTodo(input: { name: "Learn GraphQL", description: "Study how AppSync works" }) { id name description } } # 挿入データが返ってくる。idはハッシュ値 { "data": { "createTodo": { "id": "b1ffd4ce-6e09-47d3-90eb-c370dffb1fe5", "name": "Learn GraphQL", "description": "Study how AppSync works" } } } |
graphQL操作は、たった8種類だけなんだ。sqlに比べるとだいぶシンプルだな
query getテーブル名 1件だけ取得、partition(sort) keyで絞る
query getテーブル名s 全件取得、filterでwhere条件で絞る
mutation createテーブル名 新規作成
mutation Updateテーブル名 更新
mutation deleteテーブル名 削除
subscription onCreateテーブル名 新規作成のリアルタイム通知
subscription onUpdateテーブル名 更新のリアルタイム通知
subscription onDeleteテーブル名 削除のリアルタイム通知
group by, order byは出来ないので、フロント側で処理する。
joinも出来ない。
スキーマ定義の段階で、リレーションを親子ともに定義する必要があるのね
これって、あとからリレーションしたくなったら出来ない?
SQLのcreate tableみたいに、graphQLからテーブル定義は出来ない!
厳密には、APIのリクエストとレスポンスの構造を定義する
以下に定義ファイル(schema.graphql)がある
amplify\backend\api\amplifyappsyncmock\schema.graphql
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# 最初からあるToDoテーブル type Todo @model { id: ID! name: String! description: String } # Userテーブルを追加してみる。@model付与でCRUD APIが生成 type User @model { id: ID! name: String! email: String! @index(name: "byEmail", queryField: "userByEmail") createdAt: AWSDateTime! } |
@modelで、CRUD APIが以下のファイルに定義される。
amplify\backend\api\amplifyappsyncmock\build\schema.graphql
type 先頭に記述して、型の定義
フィールド名: 型!←エクスクラメーションマークは必須の意味
// 標準型
ID, String, Int, Float, Boolean
// AWS独自型
AWSDate, AWSDateTime, AWSTime, AWSTimestamp
AWSEmail, AWSURL, AWSPhone, AWSJSON, AWSIPAddress
// リスト(配列)
[String], [Float!]!(リスト自体のnull許可/不許可)
// オブジェクト型
type Address などを定義してネスト可能
// 関連(リレーション)
@hasOne, @hasMany, @belongsTo でモデル同士を関連付け
// カスタム型
type Coordinates などを定義し、特定のモデルの一部として利用可能
Amplify GraphiQL Explorer(phpMyAdminみたいなやつ)
http://localhost:20002/
基本のCRUD操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
# ユーザ作成 mutation CreateUser { createUser(input: { name: "Taro", email: "taro@example.com" }) { id name email createdAt } } 戻り値 { "data": { "createUser": { "id": "bd63c567-aae2-47fd-8a76-abe441c230c9", "name": "Taro", "email": "taro@example.com", "createdAt": "2025-03-14T07:07:04.648Z" } } } # 一覧取得 query Users { listUsers { items { id name email createdAt } } } # filterで条件 query Users { listUsers(filter: { email: { eq: "taro@example.com" } }) { items { id name email createdAt } } } #更新 mutation UpdateUser { updateUser( input: { id: "bd63c567-aae2-47fd-8a76-abe441c230c9", name: "Updated Taro", email: "updated@example.com" } ) { id name email createdAt } } #削除 mutation DeleteUser { deleteUser(input: { id: "bd63c567-aae2-47fd-8a76-abe441c230c9" }) { id name email } } # idとemailのAND条件でdelete mutation DeleteUser { deleteUser( input: { id: "bd63c567-aae2-47fd-8a76-abe441c230c9" } condition: { email: { eq: "taro@example.com" } } ) { id name email } } |