2022/1/4更新
GraphQL Transform V2から、@connection
が@hasOne/@hasMany/@belongsTo/@manyToMany
に進化して使いやすくなっていますので、新しくプロジェクトを始める方は、以下の記事をご参考ください。
はじめに
今回は、Amplifyで一対多や多対多を実現するのに必須である@connection
について解説していきます。
2019年11月頃?に定義が変わって相当わかりやすくなりました。基本的には新定義を理解するだけで問題有りませんが、参考としてこの記事には旧定義も記載しています。
定義
まずは、定義から見ていきましょう。@connection
について初めての方は、この定義解説は飛ばしてあとから読むことをおすすめします。
新定義
directive @connection(keyName: String, fields: [String!]) on FIELD_DEFINITION
keyName
@key
で設定したセカンダリインデックスを接続先に指定することができます。keyNameを指定しない場合は、接続するテーブルのプライマリーインデックスが指定されます。
fields
fieldsに引数を指定することで、接続先のテーブルから情報を取得することができます。
旧定義
directive @connection(name: String, keyField: String, sortField: String, limit: Int) on FIELD_DEFINITION
name
nameは宣言しなくても自動生成してくれますが、わかりにくい形になることも多いので、命名したほうがベターです。
keyField
keyFieldを指定しない場合は、自動的にそのテーブルのプライマリインデックスが指定されます。keyFieldにの名前を指定することで、セカンダリインデックスを利用したテーブル間の紐付けが可能になります。
sortField
sortFieldを設定することで、レコードのソート方法を設定できます。こちらもDynamoDB上では、ソートキーに設定されることで、ソート検索ができるようになります。createdAtでソートを掛けたい場合は必須です。
limit
1回のget(list)で取得するデータの個数。設定しない場合は自動で10になります。
何ができるのか?
そもそも@connection
を使って何ができるのか?っていう話を簡潔に。新定義のみ解説していきます。
1対1
getUser
で所属するTeam情報も同時に取得できます。
// 実際にDBに書き込まれるのは、id/teamId/nameの3つteamは取得用。
type User @model {
id: ID!
teamId: ID!
name: String
team: Team @connection(fields: ["teamId"])
}
type Team @model {
id: ID!
name: String!
}
export const getUser = `query GetUser ($id: ID!) {
getUser (id: $id) {
id
teamId
name
team {
id
name
}
}
}
以下の記載でも同様のことができます。
その場合、User情報をcreate時に、userTeamId
という項目を作り、そこに所属するチームのIDを与えてあげる必要があります。
type User @model {
id: ID!
name: String
team: Team @connection
}
type Team @model {
id: ID!
name: String!
}
export const getUser = `query GetUser ($id: ID!) {
getUser (id: $id) {
id
name
team {
id
name
}
}
}
1対多
getTeam
でチームに所属するUserの情報一覧が取得できます。
type User
@model
@key(name: "byTeam", fields: ["teamId"]) {
id: ID!
teamId: ID!
name: String
team: Team @connection(fields: ["teamId"])
}
type Team @model {
id: ID!
name: String!
users: [User] @connection(keyName: "byTeam", fields: ["id"])
}
export const getTeam = `query GetTeam($id: ID!) {
getTeam (id: $id) {
id
name
users {
items {
id
name
}
}
}
}
export const getUser = `query GetUser ($id: ID!) {
getUser (id: $id) {
id
name
team {
id
name
}
}
}
多対多
ユーザーが複数のチームに所属する場合は、以下のようになります。
type User @model {
id: ID!
name: String
teams: [TeamGroup] @connection(keyName: "byUser", fields: ["id"])
}
type TeamGroup
@model
@key(name: "byTeam", fields: ["teamId"])
@key(name: "byUser", fields: ["userId"])
{
id: ID!
userId: ID!
teamId: ID!
user: User @connection(fields: ["userId"])
team: Team @connection(fields: ["teamId"])
}
type Team @model {
id: ID!
name: String!
users: [TeamGroup] @connection(keyName: "byTeam", fields: ["id"])
}
export const getUser = `query GetUser($id: ID!) {
getUser (id: $id) {
id
name
teams {
items {
id
userId
teamId
team {
id
name
}
}
}
}
}
export const getTeam = `query GetTeam($id: ID!) {
getTeam (id: $id) {
id
name
users {
items {
id
userId
teamId
user {
id
name
}
}
}
}
}
はまりポイント解説
理解するのに時間がかかったけれど大事なところをピックアップ。
@connetion
をつけたモデルの削除
@connetion
をつけたモデルの削除をする場合は注意が必要です。削除はできますが、戻す時に下記エラーが出ることがあります。
REATE_FAILED AnswerquestionResolver
AWS::AppSync::Resolver Fri Dec 13 2019 23:00:09 GMT 0900 (GMT 09:00)
Only one resolver is allowed per field. (Service: AWSAppSync; Status Code: 400;
Error Code: BadRequestException; Request ID: eefe6b89-2664-46a2-bf5a-435567035e15)
原因としては、削除したモデルに@connection
で繋がっていたモデルが存在していた場合、その繋がりの記録がどこかしらに残っており、上書きできないので、エラーです、みたいな内容です。
こうなってしまうと、その両方のモデルを一度削除した上で、もう一度新しくpushし直す必要があり、@connection
がいろんなモデルにクロスしているともはや再生不能に陥るので、モデルの削除には気をつけましょう。
以下でissueで話し合われています。
keyNameのfieldsはnon-null
keyNameのfieldsはnon-nullなので、「!」のつけ忘れに注意が必要です。
@connection
関係なく、@key
でGSIを設定する場合は、fieldsに指定するカラムがnullで問題ありません。
fieldsに指定できるのはstringのみ
@connection
の仕様上、fieldsに指定できるのはstringのみため、プライマリーキーがIntで宣言されているテーブルは取得することができないため、注意が必要です。
その場合は、以下の記事を参考に@connection
ではなく、フィールドに対してカスタムリゾルバーを作成することで取得することができる、とAWSサポートから回答をもらいましたが、まだ手を付けてはおりません。
おわりに
新定義になり、とにかくわかりやすくなりました。初めはわかりづらいかもしれませんが、実践すればすぐになれると思います。
Amplifyは絶賛開発中に現在進行形でどんどん便利になっていっています。公式ドキュメントの定期なチェックは必須です。
参考
関連記事
AWS amplify フレームワークの使い方Part1〜Auth設定編〜
AWS Amplify フレームワークの使い方Part2〜Auth実践編〜
AWS Amplify フレームワークの使い方Part3〜API設定編〜
AWS Amplify フレームワークの使い方Part4〜API実践編〜
AWS Amplify フレームワークの使い方Part5〜GraphQL Transform @model編〜
[AWS Amplify フレームワークの使い方Part6〜GraphQL Transform @auth編〜]
(https://qiita.com/too/items/fae2879ea36f00c3ae10)
AWS Amplify フレームワークの使い方Part7〜GraphQL Transform @key編〜
AWS Amplify フレームワークの使い方Part9〜Function 基礎編〜
AWS Amplify フレームワークの使い方Part10〜Storage編〜
AWS Amplify フレームワークの使い方Part11〜Function 権限管理編〜
AWS Amplify フレームワークの使い方Part12〜ENV編〜
[AWS Amplify フレームワークの使い方Part13〜Auth 設定更新編〜]
(https://qiita.com/too/items/52f35860bcb5bdf5e667)
[AWS Amplify フレームワークの使い方Part14〜Lambda レイヤー編〜]
(https://qiita.com/too/items/54de781085bd9a3a66d0)