はじめに
非常に複雑だった@connectionがv2になってシンプルになって帰ってきました。
今回の変更で、よりAmplifyを始めやすくなったのではないでしょうか。
環境
@aws-amplify/cli 7.6.3
定義
今回、公式ドキュメントに定義としては記載がなかったので、ドキュメントを見て分かる範囲で記載しています。
directive @hasOne(fields: [String!])
directive @hasMany(indexName: String, fields: [String!])
directive @belongsTo(fields: [String!])
directive @manyToMany(relationName: String)
fields
fieldsを指定することで、指定したキーを使って紐付先のテーブルから情報を取得することができます。
indexName
@index
で設定したセカンダリインデックスを使って、データを取得することができます。indexNameがない場合は、紐付先のプライマリーキーでのデータ取得になります。。
relationName
manyToManyで自動生成されるテーブル名を指定します。
スキーマへの記載方法
以下のようにまとめていきます。
- 1対1
- 1対多
- 多対多
1対1
fields指定なし / 双方向データ取得なし
type Project @model {
id: ID!
name: String
team: Team @hasOne
# projectTeamId: ID! が自動生成
}
type Team @model {
id: ID!
name: String!
}
mutation CreateProject {
createProject(input: {projectTeamId: "team-id", name: "Some Name"}) {
id
projectTeamId
name
team {
name
id
}
}
}
紐付け元に、${紐付元のテーブル名}${紐付先のテーブル名}Idが自動生成されます。
今回は、projectTeamId
という変数が自動生成され、create時にprojectTeamId
を保存することで、紐付けが完了します。
fields指定あり / 双方向データ取得なし
projectTeamId
の変数名を自分で指定したい場合は、以下のように記載します。
個人的には、fields指定ありの方が一手間はかかりますが、スキーマファイル内で明示的に記載できるので、後々わかりやすい気がしています。
type Project @model {
id: ID!
teamId: ID!
name: String
team: Team @hasOne(fields: ["teamId"])
}
type Team @model {
id: ID!
name: String!
}
mutation CreateProject {
createProject(input: { name: "New Project", teamId: "a-team-id"}) {
id
teamId
name
team {
id
name
}
}
}
fields指定なし / 双方向データ取得あり
fields指定なしで双方向からデータの取得がしたい場合は、以下のように記載します。
type Project @model {
id: ID!
name: String
team: Team @hasOne
# projectTeamId : ID! が@hasOneによって自動生成
}
type Team @model {
id: ID!
name: String!
project: Project @belongsTo
# teamProjectId : ID! が@belongsToによって自動生成
}
mutation CreateProject {
createProject(input: { name: "New Project", projectTeamId: "a-team-id"}) {
id
projectTeamId
name
team {
id
teamProjectId
name
project {
id
projectTeamId
name
}
}
}
}
fields指定あり / 双方向データ取得あり
fields指定なしで双方向からデータの取得がしたい場合は、以下のように記載します。
type Project @model {
id: ID!
teamId: ID!
name: String
team: Team @hasOne(fields: ["teamId"])
}
type Team @model {
id: ID!
projectId: ID!
name: String!
project: Project @belongsTo(fields: ["projectId"])
}
mutation CreateProject {
createProject(input: { name: "New Project", teamId: "a-team-id"}) {
id
teamId
name
team {
id
projectId
name
project {
id
teamId
name
}
}
}
}
1対多
fields指定なし / 双方向データ取得なし
type Post @model {
id: ID!
title: String!
comments: [Comment] @hasMany
}
type Comment @model {
id: ID!
content: String!
# postCommentsId: ID! が自動生成
}
mutation CreatePost {
createPost(input: {title: "Hello World!!"}) {
id
title
comments {
items {
id
postCommentsId
content
}
}
}
}
hasManyの紐付け先テーブル内に、${紐付け元のテーブル名}${@hasManyをつけた項目名}Id
が自動生成されます。
上記の例では、commentを新規作成する場合は、必ずinputにpostCommentsIdを保存する必要があります。
fields指定あり / 双方向データ取得なし
紐付け用のIDを自動生成したくないときは、hasOneと同様にfiledを指定+indexNameを指定、@indexを利用することで変更できます。
type Post @model {
id: ID!
title: String!
comments: [Comment] @hasMany(indexName: "byPost", fields: ["id"])
}
type Comment @model {
id: ID!
postId: ID! @index(name: "byPost", sortKeyFields: ["content"])
content: String!
}
mutation CreatePost {
createPost(input: {title: "Hello world!"}) {
id
title
comments {
items {
postId
content
id
}
}
}
}
fields指定なし / 双方向データ取得あり
これまでの流れだと、hasManyとbelongsToによって、postCommentsId
とcommentPostId
の2つの変数が作成されそうですが、commentPostId
だけ生成されます。
type Post @model {
id: ID!
title: String!
comments: [Comment] @hasMany
}
type Comment @model {
id: ID!
content: String!
post: Post @belongsTo
# postCommentsId: ID がhasManyによって自動生成
}
mutation CreatePost {
createPost(input: {title: "Hello World!!"}) {
id
title
comments {
items {
id
commentPostId
content
post {
id
title
}
}
}
}
}
fields指定あり / 双方向データ取得あり
こちらもお好みで、変数を指定したい場合は、fieldsを指定します。
type Post @model {
id: ID!
title: String!
comments: [Comment] @hasMany(indexName: "byPost", fields: ["id"])
}
type Comment @model {
id: ID!
postId: ID! @index(name: "byPost")
content: String!
post: Post @belongsTo(fields: ["postId"])
}
mutation CreatePost {
createPost(input: {title: "Hello World!!"}) {
id
title
comments {
items {
id
postId
content
post {
id
title
}
}
}
}
}
多対多
@manyToManyを利用した場合
中間テーブルの作成を勝手にやってくれます。
type Post @model {
id: ID!
title: String!
content: String
tags: [Tag] @manyToMany(relationName: "PostTags")
}
type Tag @model {
id: ID!
label: String!
posts: [Post] @manyToMany(relationName: "PostTags")
}
# 実質的に以下をスキーマに記載した時と同じ動作になります。
# type PostTags @model {
# id: ID!
# postID: ID!
# tagID: ID!
# post: Post @belongsTo(fields: ["postID"])
# tag: Tag @belongsTo(fields: ["tagID"])
# }
mutation CreatePost {
createPost(input: {title: "Hello World!!"}) {
id
title
content
tags {
items {
id
postID
tagID
tag {
id
label
posts {
items {
post {
id
title
content
}
}
}
}
post {
id
title
content
}
}
}
}
}
@manyToManyを利用しない場合/双方データ取得あり
現状、manyToManyで生成される中間テーブルに独自の項目を追加したり、アクセス権限を縛ったりはできません。(厳密にはやり方わからない。)
なので、とりあえずmanyToManyを手動でスキーマに書くと以下のようになります。
type Post @model {
id: ID!
title: String!
content: String
tags: [PostTags] @hasMany(indexName: "byPost", fields: ["id"])
}
type Tag @model {
id: ID!
label: String!
posts: [PostTags] @hasMany(indexName: "byTag", fields: ["id"])
}
type PostTags @model {
id: ID!
postId: ID! @index(name: "byPost")
tagId: ID! @index(name: "byTag")
post: Post @belongsTo(fields: ["postId"])
tag: Tag @belongsTo(fields: ["tagId"])
}
mutation CreatePost {
createPost(input: {title: "Hello World!!"}) {
id
title
content
tags {
items {
id
postId
tagId
tag {
id
label
posts {
items {
post {
id
title
content
}
}
}
}
post {
id
title
content
}
}
}
}
}
@manyToManyを利用しない場合/双方データ取得なし
地味に多対多で双方向データ取得不要なケースもあるので、それも記載しておきます。
# 例:Postからのみ取得できればOKパターン
type Post @model {
id: ID!
title: String!
content: String
tags: [PostTags] @hasMany(indexName: "byPost", fields: ["id"])
}
type Tag @model {
id: ID!
label: String!
}
type PostTags @model {
id: ID!
postId: ID! @index(name: "byPost")
tagId: ID!
tag: Tag @hasOne(fields: ["tagId"])
}
mutation CreatePost {
createPost(input: {title: "Hello World!!"}) {
id
title
content
tags {
items {
id
postId
tagId
tag {
id
label
}
}
}
}
}
注意点/メモ
fieldsに指定できるのはstringのみ?
未検証ですが、@connection
の時は、fieldsに指定できるのはstringのみでしたので、おそらく今回もその可能性が高いと思っています。
その場合は、自分でカスタムリゾルバーを作成しましょう。
IdとID問題
v7.6.3では、自動生成されるidは、hasOne/hasMany/belongToはxxxId
、manyToManyで生成されるテーブルのみxxxID
で生成されます。なぜこうなっているのかは、わかりませんが、注意が必要です。
indexName
未検証なので定義のところには記載しませんでしたが、hasOneやbelongToでも多分indexNameでindex指定できると思われます。また検証したら更新します。
おわりに
その昔issueで議論されていた内容が、気づいたら新機能として実装されていました。
これもAmplifyの開発は活発である証拠とかと思いますので、引き続き、今後のAmplifyの進化に期待です!
参考