データモデルをカスタマイズする
Amplifyは、GraphQLスキーマで@modelディレクティブで注釈が付けられたGraphQLタイプに対して、Amazon DynamoDBデータベーステーブルを自動的に作成します。@hasOne、@hasMany、@belongsTo、および@manyToManyディレクティブを介して、データモデル間の関係を作成できます。
データベーステーブルのセットアップ
次のGraphQLスキーマは、「Todo」のデータベーステーブルを自動的に作成します。@modelは、データベーステーブルに主キーとしてidフィールドも自動的に追加します。主キーをカスタマイズする方法を学ぶために、主キーを構成するを参照してください。
type Todo @model { content: String}amplify pushまたはcdk deploy時に、Amplifyはtodoデータベーステーブルと、作成、読み取り、更新、削除、およびリスト操作を実行するための対応するGraphQL APIをデプロイします。
さらに、@modelは、タイプにcreatedAtとupdatedAtヘルパーフィールドも追加します。これらのフィールドの値は、明示的に上書きされない限り、クライアント側では読み取り専用です。詳細については、作成および更新タイムスタンプをカスタマイズするを参照してください。
次のクエリを実行して、すべてのtodoをリストしてみてください:
query QueryAllTodos { listTodos() { todos { items { id content createdAt updatedAt } } }}import { Amplify } from 'aws-amplify';import { generateClient } from 'aws-amplify/api';import config from './amplifyconfiguration.json';import { listTodos } from './graphql/queries';
const client = generateClient();
Amplify.configure(config);
try { const result = await client.graphql({ query: listTodos }); const todos = result.data.listTodos;} catch (res) { const { errors } = res; console.error(errors);}主キーを構成する
@modelディレクティブを持つすべてのGraphQLタイプは、自動的に主キーとして設定されたidフィールドを持ちます。@primaryKeyディレクティブで別の必須フィールドをマークすることで、この動作をオーバーライドできます。
下の例では、todoIdがデータベースの主キーであり、@modelディレクティブによってidフィールドは自動的に作成されなくなります。
type Todo @model { todoId: ID! @primaryKey content: String}さらに設定しない場合、その主キーフィールドの完全な等値マッチでのみTodoをクエリできます。上の例では、これはtodoIdフィールドです。
注意:主キーが設定されてデプロイされた後、データベーステーブルを削除して再作成することなく、それを変更することはできません。
「ソートキー」を指定して、異なるフィールドの組み合わせを主キーとして使用することもできます。これにより、指定された「ソートキーフィールド」に対してより高度なソートおよびフィルタリング条件を適用できます。
type Inventory @model { productID: ID! @primaryKey(sortKeyFields: ["warehouseID"]) warehouseID: ID! InventoryAmount: Int!}上のスキーマにより、異なる条件を渡して正しい在庫項目をクエリできます:
query QueryInventoryByProductAndWarehouse($productID: ID!, $warehouseID: ID!) { getInventory(productID: $productID, warehouseID: $warehouseID) { productID warehouseID inventoryAmount }}import { getInventory } from './graphql/queries';
const result = await client.graphql({ query: getInventory, variables: { productID: 'product-id', warehouseID: 'warehouse-id' }});const inventory = result.data.getInventory;セカンダリインデックスを構成する
AmplifyはAmazon DynamoDBテーブルを@modelタイプの基盤となるデータソースとして使用します。キー値データベースの場合、「セカンダリインデックス」でアクセスパターンをモデル化することが重要です。@indexディレクティブを使用して、セカンダリインデックスを構成します。
Amazon DynamoDBは、任意のスケールで1桁のミリ秒パフォーマンスを提供するキー値およびドキュメントデータベースですが、アクセスパターンに合わせて機能させるには少し前もった検討が必要です。DynamoDBクエリ操作は、データを効率的にクエリするために最大2つの属性を使用できます。クエリに渡された最初のクエリ引数(ハッシュキー)は厳密な等値を使用する必要があり、2番目の属性(ソートキー)はgt、ge、lt、le、eq、beginsWith、およびbetweenを使用できます。DynamoDBはほとんどのアプリケーションに十分な強力なさまざまなアクセスパターンを効果的に実装できます。
セカンダリインデックスは、「ハッシュキー」と、オプションで「ソートキー」で構成されます。「ハッシュキー」を使用して厳密な等値を実行し、「ソートキー」を使用してより大きい(gt)、以上(ge)、より小さい(lt)、以下(le)、等しい(eq)、で始まる、およびbetweenの操作を実行します。
type Customer @model { id: ID! name: String! phoneNumber: String accountRepresentativeID: ID! @index}下のクライアントクエリ例により、accountRepresentativeIDに基づいて「Customer」レコードをクエリできます:
query QueryCustomersForAccountRepresentative($accountRepresentativeID: ID!) { customersByAccountRepresentativeID( accountRepresentativeID: $accountRepresentativeID ) { customers { items { id name phoneNumber } } }}import { customersByAccountRepresentativeID } from './graphql/queries';
const result = await client.graphql({ query: customersByAccountRepresentativeID, variables: { accountRepresentativeID: 'account-rep-id' }});const customers = result.data.customersByAccountRepresentativeID;queryFieldまたはnameをオーバーライドして、GraphQLクエリ名またはセカンダリインデックス名をカスタマイズできます:
type Customer @model { id: ID! name: String! phoneNumber: String accountRepresentativeID: ID! @index(name: "byRepresentative", queryField: "customerByRepresentative")}query QueryCustomersForAccountRepresentative($representativeId: ID!) { customerByRepresentative(accountRepresentativeID: $representativeId) { customers { items { id name phoneNumber } } }}import { customerByRepresentative } from './graphql/queries';
const result = await client.graphql({ query: customerByRepresentative, variables: { accountRepresentativeID: 'account-rep-id' }});const customer = result.data.customerByRepresentative;オプションでソートキーを構成するには、sortKeyFieldsパラメータに追加フィールドを指定します:
type Customer @model @auth(rules: [{ allow: public }]) { id: ID! name: String! @index(name: "byNameAndPhoneNumber", sortKeyFields: ["phoneNumber"], queryField: "customerByNameAndPhone") phoneNumber: String accountRepresentativeID: ID! @index下のクライアントクエリ例により、「Customer」をnameに基づいてクエリし、phoneNumberに基づいてフィルタできます:
query MyQuery { customerByNameAndPhone(phoneNumber: { beginsWith: "+1" }, name: "Rene") { items { id name phoneNumber } }}import { customerByNameAndPhone } from './graphql/queries';
const result = await client.graphql({ query: customerByNameAndPhone, variables: { phoneNumber: { beginsWith: '+1' }, name: 'Rene' }});
const customer = result.data.customerByNameAndPhone;モデル間の関係をセットアップする
@modelタイプ間で「1対1」、「1対多」、「多対1」、および「多対多」の関係を作成します。
| 関係 | 説明 |
|---|---|
@hasOne | 2つのモデル間に単方向の1対1関係を作成します。例えば、プロジェクトは1つのチームを「持つ」場合があります。これにより、プロジェクトレコードからチームをクエリできます。 |
@hasMany | 2つのモデル間に単方向の1対多関係を作成します。例えば、投稿は多くのコメントを持ちます。これにより、投稿レコードからすべてのコメントをクエリできます。 |
@belongsTo | 「1対1」または「1対多」の関係を双方向にするために「属する」関係を使用します。例えば、プロジェクトは1つのチームを持ち、チームはプロジェクトに属します。これにより、プロジェクトレコードからチームをクエリでき、その逆も可能です。 |
@manyToMany | 2つのモデル間の多対多関係を容易にするための「結合テーブル」を構成します。例えば、ブログは多くのタグを持ち、タグは多くのブログを持ちます。 |
Has One関係
@hasOneディレクティブを使用して、2つのモデル間に単方向の1対1関係を作成します。
下の例では、プロジェクトがチームを持ちます。
type Project @model { id: ID! name: String team: Team @hasOne}
type Team @model { id: ID! name: String!}これにより、ソースレコードから関連レコードを取得できるクエリとミューテーションが生成されます:
mutation CreateProject { createProject(input: { projectTeamId: "team-id", name: "Some Name" }) { team { name id } name id }}import { createProject } from './graphql/mutations';
const result = await client.graphql({ query: createProject, variables: { input: { projectTeamId: 'team-id', name: 'Some Name' } }});
const project = result.data.createProject;関係情報を保存するために使用するフィールドをカスタマイズするには、fields配列引数を設定してタイプ上のフィールドと一致させます:
type Project @model { id: ID! name: String teamID: ID team: Team @hasOne(fields: ["teamID"])}
type Team @model { id: ID! name: String!}この場合、プロジェクトタイプは、チームの識別子として追加されたteamIDフィールドを持ちます。@hasOneは、このteamIDでチームテーブルをクエリして、接続されたチームオブジェクトを取得できます:
mutation CreateProject { createProject(input: { name: "New Project", teamID: "a-team-id" }) { id name team { id name } }}import { createProject } from './graphql/mutations';
const result = await client.graphql({ query: createProject, variables: { input: { teamID: 'team-id', name: 'New Project' } }});const project = result.data.createProject;@hasOne関係は常に関連モデルの主キーへの参照を使用します。デフォルトはidですが、@primaryKeyディレクティブでオーバーライドできます。
Has Many関係
@hasManyディレクティブを使用して、2つのモデル間に単方向の1対多関係を作成します。
type Post @model { id: ID! title: String! comments: [Comment] @hasMany}
type Comment @model { id: ID! content: String!}これにより、ソースポストレコードから関連するコメントレコードを取得できるクエリとミューテーションが生成されます:
mutation CreatePost { createPost(input: { title: "Hello World!!" }) { title id comments { items { id content } } }}import { createPost } from './graphql/mutations';
const result = await client.graphql({ query: createPost, variables { input: { title: 'Hello World!!' }, }});const post = result.data.createPost;const comments = post.comments.items;内部的には、@hasManyは関連テーブルにデフォルトセカンダリインデックスを構成して、ソースモデルから関連モデルをクエリできるようにします。
「has many」関係に使用するセカンダリインデックスをカスタマイズするには、関連テーブルのフィールドで@indexディレクティブを作成します。ここで、セカンダリインデックスに割り当てたいものです。
次に、セカンダリインデックスにname属性と値を指定します。オプションで、sortKeyFields属性を指定してセカンダリインデックスに「ソートキー」を構成し、指定したフィールドを値として指定できます。
@hasMany構成では、セカンダリインデックスからname値をindexNameパラメータの値として渡します。次に、接続されたインデックスと一致する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", sortKeyFields: ["content"]) content: String!}この場合、commentタイプは投稿レコードの参照を保存するために追加されたpostIDフィールドを持ちます。@hasManyによって参照されるidフィールドは、Postタイプのidです。@hasMany は、このpostIDでcommentテーブルのセカンダリインデックス「byPost」をクエリして、接続されたcommentオブジェクトを取得できます:
mutation CreatePost { createPost(input: { title: "Hello world!" }) { comments { items { postID content id } } title id }}import { createPost, createComment } from './graphql/mutations';import { getPost } from './graphql/mutations';
// create postconst result = await client.graphql({ query: createPost, variables: { input: { title: 'Hello World!!' } }});const post = result.data.createPost;
// create commentawait client.graphql({ query: createComment, variables: { input: { content: 'Hi!', postID: post.id } }});
// get postconst result = await client.graphql({ query: getPost, variables: { id: post.id }});
const postWithComments = result.data.createPost;const postComments = postWithComments.comments.items; // access comments from postBelongs To関係
@belongsToディレクティブで「1対1」または「1対多」の関係を双方向にします。
1対1の関係の場合、@belongsToディレクティブは、関係の両側からクエリできるようにする機能を容易にするだけです。双方向hasOne関係を更新するときは、対応するIDで関係の両側を更新する必要があります。
type Project @model { id: ID! name: String team: Team @hasOne}
type Team @model { id: ID! name: String! project: Project @belongsTo}これにより、ソースポストレコードから関連するコメントレコードを取得し、その逆も可能なクエリとミューテーションが生成されます:
mutation CreateProject { createProject(input: { name: "New Project", teamID: "a-team-id" }) { id name team { # query team from project id name project { # bi-directional query: team to project id name } } }}import { createProject, createTeam, updateTeam } from './graphql/mutations';
// create teamconst result = await client.graphql({ query: createTeam, variables: { input: { name: 'New Team' } }});const team = result.data.createTeam;
// create projectconst result = await client.graphql({ query: createProject, variables: { input: { name: 'New Project', projectTeamId: team.id } }});const project = result.data.createProject;const projectTeam = project.team; // access team from project
// update teamconst updateTeamResult = await client.graphql({ query: updateTeam, variables: { input: { id: team.id, teamProjectId: project.id } }});
const updatedTeam = updateTeamResult.data.updateTeam;const teamProject = updatedTeam.project; // access project from teamtype Post @model { id: ID! title: String! comments: [Comment] @hasMany}
type Comment @model { id: ID! content: String! post: Post @belongsTo}これにより、ソースポストレコードから関連するコメントレコードを取得し、その逆も可能なクエリとミューテーションが生成されます:
mutation CreatePost { createPost(input: { title: "Hello World!!" }) { title id comments { # query comments from the post items { id content post { # bi-directional query: comment to post id title } } } }}import { createPost, createComment } from './graphql/mutations';import { getPost } from './graphql/mutations';
// create postconst result = await client.graphql({ query: createPost, variables: { input: { title: 'Hello World!!' } }});const post = result.data.createPost;
// create commentawait client.graphql({ query: createComment, variables: { input: { content: 'Hi!', postID: post.id } }});
// get postconst result = await client.graphql({ query: getPost, variables: { id: post.id }});
const postWithComments = result.data.createPost;const postComments = postWithComments.comments.items; // access comments from post
const commentPost = postComments[0].post; // access post from comment;@belongsToはfields引数なしで使用できます。その場合、親の主キーを参照するフィールドが自動的に生成されます。
または、親オブジェクトの参照を保存するカスタムフィールドをセットアップします。双方向「1対多」関係の例を以下に示します。
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! post: Post @belongsTo(fields: ["postID"])}注意:
@belongsToディレクティブは、親から関連モデルへの@hasOneまたは@hasMany関係が既に存在することを必要とします。
多対多関係
@manyToManyディレクティブで2つのモデル間に多対多関係を作成します。両方のモデルで共通のrelationNameを指定して、それらを多対多関係に結合します。
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")}内部的には、@manyToManyディレクティブは、relationNameに基づいて名前が付けられた「結合テーブル」を作成して、多対多関係を容易にします。これにより、ソースポストレコードから関連するコメントレコードを取得し、その逆も可能なクエリとミューテーションが生成されます:
mutation CreatePost { createPost(input: { title: "Hello World!!" }) { id title content tags { # queries the "join table" PostTags items { tag { # related Tag records from Post id label posts { # queries the "join table" PostTags items { post { # related Post records from Tag id title content } } } } } } }}import { createPost, createTag, createPostTags } from './graphql/mutations';import { listPosts } from './graphql/queries';
// create postconst result = await client.graphql({ query: createPost, variables: { input: { title: 'Hello World' } }});const post = result.data.createPost;
// create tagconst tagResult = await client.graphql({ query: createTag, variables: { input: { label: 'My Tag' } }});const tag = tagResult.data.createTag;
// connect post and tagawait client.graphql({ query: createPostTags, variables: { input: { postId: post.id, tagId: tag.id } }});
// get postsconst listPostsResult = await client.graphql({ query: listPosts });const posts = listPostsResult.data.listPosts;
const postTags = posts[0].tags; // access tags from postフィールドのデフォルト値を割り当てる
@defaultディレクティブを使用して、Int、Stringなど、オプションのスカラータイプフィールドのデフォルト値を指定できます。
type Todo @model { content: String @default(value: "My new Todo") # Note: all "value" parameters must be passed as a string value. # Under the hood, Amplify will parse the string values into respective types. # For example, to set a default value for an integer field, # you must pass in `"0"` instead of `0` without the double-quotes. likes: Int @default(value: "0") #}新しいTodoを作成してcontent入力を指定しない場合、AmplifyはMy new Todoが値として自動入力されることを確保します。@defaultが適用されている場合、!を使用した非ヌルアサーションは無視されます。例えば、String!はStringと同じように扱われます。
サブスクリプションのサーバー側フィルタリング
サーバー側のサブスクリプションフィルタ式は、任意の@modelタイプに対して自動的に生成されます。
type Task @model { title: String! description: String type: String priority: Int}フィルタ式を渡すことで、サーバー側でサブスクリプションをフィルタできます。例えば:Securityタイプのタスクとpriority が5より大きいタスクをサブスクライブしたい場合は、filter引数を適切に設定できます。
subscription OnCreateTask { onCreateTask( filter: { and: [{ type: { eq: "Security" } }, { priority: { gt: 5 } }] } ) { title description type priority }}import { onCreateTask } from './graphql/subscriptions';
const subscription = client.graphql({ query: onCreateTask, variables: { filter: { and: [ { type: { eq: "Security" } } { priority: { gt: 5 } } ] } }}).subscribe({ next: ({ data }) => console.log(data), error: (error) => console.warn(error)});すべてのサブスクリプションイベントを取得したい場合は、filterパラメータを渡さないでください。
高度な設定
生成されたクエリ、ミューテーション、サブスクリプションの名前を変更する
目的の名前を指定することで、@model生成のGraphQLクエリ、ミューテーション、サブスクリプションの名前をオーバーライドできます。
type Todo @model(queries: { get: "queryFor" }) { name: String! description: String}上の例では、単一のTodo要素を取得するためにqueryForTodoクエリを実行できます。
生成されたクエリ、ミューテーション、サブスクリプションを無効にする
値をnullに割り当てることで、特定の操作を無効にできます。
type Todo @model(queries: { get: null }, mutations: null, subscriptions: null) { name: String! description: String}上の例はgetTodoクエリ、すべてのミューテーション、およびすべてのサブスクリプションを無効にしながら、listTodoなどの他のクエリの生成を許可します。
カスタムクエリを作成する
getクエリを無効にして、単一のTodoモデルを取得できるカスタムクエリを作成できます。
type Query { getMyTodo(id: ID!): Todo @function(name: "getmytodofunction-${env}")}上の例は、このクエリ用のlambda関数を呼び出すために@functionディレクティブを使用するカスタムクエリを作成します。
クエリ、ミューテーション、およびサブスクリプションのタイプ定義については、@modelディレクティブのタイプ定義を参照してください。
作成および更新タイムスタンプをカスタマイズする
@modelディレクティブは、各エンティティにcreatedAtとupdatedAtタイムスタンプを自動的に追加します。タイムスタンプフィールド名は、ディレクティブにtimestamps属性を渡すことで変更できます。
type Todo @model(timestamps: { createdAt: "createdOn", updatedAt: "updatedOn" }) { name: String! description: String}例えば、上のスキーマにより、以下の内容をクエリできます:
type Todo { id: ID! name: String! description: String createdOn: AWSDateTime! updatedOn: AWSDateTime!}サブスクリプション(リアルタイム更新)のアクセスレベルを変更する
デフォルトでは、リアルタイム更新はすべての@modelタイプに対してオンになっています。つまり、顧客はリアルタイム更新を受け取り、認可ルールは初期接続時に適用されます。そのモデルのサブスクリプションをオフにするか、リアルタイム更新をパブリックにして、すべてのサブスクライバーが受信できるようにすることもできます。
type Todo @model(subscriptions: { level: off }) { # or level: public name: String! description: String}2つのモデル間に複数の関係を作成する
同じタイプの2つの接続を作成するために関係ディレクティブを使用する場合は、接続フィールド名を明示的に指定する必要があります。
type Individual @model { id: ID! homeAddress: Address @hasOne shippingAddress: Address @hasOne}
type Address @model { id: ID! homeIndividualID: ID shippingIndividualID: ID homeIndividual: Individual @belongsTo(fields: ["homeIndividualID"]) shipIndividual: Individual @belongsTo(fields: ["shippingIndividualID"])}複合主キーを持つモデルへの関係
主キーが_ソートキー_に加えて_ハッシュキー_で定義されている場合、それは複合主キーと呼ばれます。
@hasOne、@hasMany、または@belongsToディレクティブでfields引数を明示的に定義し、複合主キーを持つモデルを参照する場合は、fields引数の値を特定の順序で設定する必要があります:
- 最初の値は常に関連モデルの主キーである必要があります。
- 残りの値は、関連モデルの
@primaryKeyディレクティブで指定されたsortKeyFieldsと一致する必要があります。
type Project @model { projectId: ID! @primaryKey(sortKeyFields: ["name"]) name: String! team: Team @hasOne(fields: ["teamId", "teamName"]) teamId: ID # customized foreign key for child primary key teamName: String # customized foreign key for child sort key}
type Team @model { teamId: ID! @primaryKey(sortKeyFields: ["name"]) name: String!}type Project @model { projectId: ID! @primaryKey(sortKeyFields: ["name"]) name: String! team: Team @hasOne(fields: ["teamId", "teamName"]) teamId: ID # customized foreign key for child primary key teamName: String # customized foreign key for child sort key}
type Team @model { teamId: ID! @primaryKey(sortKeyFields: ["name"]) name: String! project: Project @belongsTo(fields: ["projectId", "projectName"]) projectId: ID # customized foreign key for parent primary key projectName: String # customized foreign key for parent sort key}type Post @model { postId: ID! @primaryKey(sortKeyFields: ["title"]) title: String! comments: [Comment] @hasMany(indexName: "byPost", fields: ["postId", "title"])}
type Comment @model { commentId: ID! @primaryKey(sortKeyFields: ["content"]) content: String! postId: ID @index(name: "byPost", sortKeyFields: ["postTitle"]) # customized foreign key for parent primary key postTitle: String # customized foreign key for parent sort key}type Post @model { postId: ID! @primaryKey(sortKeyFields: ["title"]) title: String! comments: [Comment] @hasMany(indexName: "byPost", fields: ["postId", "title"])}
type Comment @model { commentId: ID! @primaryKey(sortKeyFields: ["content"]) content: String! post: Post @belongsTo(fields: ["postId", "postTitle"]) postId: ID @index(name: "byPost", sortKeyFields: ["postTitle"]) # customized foreign key for parent primary key postTitle: String # customized foreign key for parent sort key}セカンダリインデックスをGraphQLクエリなしで生成する
セカンダリインデックスに対するクエリ作成が自動的であるため、対応するクエリがAPIに存在しないセカンダリインデックスを定義する場合は、queryFieldパラメータをnullに設定します。
type Customer @model { id: ID! name: String! phoneNumber: String accountRepresentativeID: ID! @index(queryField: null)}GraphQLファイルを分割する
AWS Amplifyは、GraphQLスキーマを複数の.graphqlファイルに分割することをサポートしています。
amplify/backend/api/<api-name>/schema/ディレクトリを作成することで開始できます。例として、ブログサイトのスキーマをBlog.graphql、Post.graphql、およびComment.graphqlファイルを作成して分割することができます。
その後、amplify api gql-compileを実行し、出力ビルドスキーマにはスキーマファイル全体で宣言されたすべてのタイプが含まれます。
プロジェクトが成長するにつれて、プロジェクトのサイズとメンテナンス要件に応じて、カスタムクエリ、ミューテーション、およびサブスクリプションを整理することができます。それらをすべて1つのファイルに統合することも、対応するモデルと同じ場所に配置することもできます。
単一のQuery.graphqlファイルを使用する
この方法では、すべてのクエリを単一のQuery.graphqlファイルに統合することが含まれます。これは小規模なプロジェクトや、すべてのクエリを1つの場所に保ちたい場合に便利です。
-
amplify/backend/api/<api-name>/schema/ディレクトリで、Query.graphqlという名前のファイルを作成します。 -
複数のスキーマファイルからすべてのクエリタイプ定義を
Query.graphqlファイルにコピーします。 -
すべてのクエリが適切にフォーマットされ、単一の
type Query { ... }ブロック内に囲まれていることを確認します。
extendキーワードを使用する
複数のスキーマファイルでQueryタイプを宣言すると、amplify api gql-compileを実行するときに以下のようなスキーマ検証エラーが発生します:
🛑 Schema validation failed.
There can be only one type named "Query".AmplifyGraphQLスキーマはextendキーワードをサポートしており、これにより追加のフィールドを持つタイプを拡張できます。この場合、カスタムクエリ、ミューテーション、およびサブスクリプションを複数のファイルに分割することもできます。これは、より大規模でより複雑なプロジェクトに対してより理想的である可能性があります。
-
プロジェクトのアーキテクチャに従ってGraphQLスキーマを複数のファイルに整理します。
-
ファイルの1つ(例えば、
schema1.graphql)で、タイプを通常通り宣言します:
type Query { # initial custom queries myQuery: String @function(name: "myQueryFunction-${env}")}- 他のスキーマファイル(例えば、
schema2.graphql)で、extendキーワードを使用してタイプを追加します:
extend type Query { # additional custom queries myQuery2: String @function(name: "myQuery2Function-${env}")}- Amplifyディレクティブを使用して拡張型のフィールドに機能を追加します。Amplifyは
Query、Mutation、およびSubscription型拡張のフィールドで@auth、@function、および@httpディレクティブをサポートしています。または、extendキーワードを使用して、カスタムリゾルバーを使用するカスタムクエリ、ミューテーション、およびサブスクリプションを整理できます。
動作方法
モデルディレクティブ
@modelディレクティブは以下を生成します:
- デフォルトでPAY_PER_REQUEST課金モードが有効になっているAmazonDynamoDBテーブル。
- 上記のテーブルにアクセスするように構成されたAWS AppSync DataSource。
- 上記のテーブルを代理でAWS AppSyncを呼び出すことを可能にするAWS IAMロール。
- 最大8つのリゾルバー(作成、更新、削除、取得、リスト、onCreate、onUpdate、onDelete)ですが、これは@modelディレクティブの queries、mutations、および subscriptions引数を介して設定可能です。
- 作成、更新、および削除ミューテーション用の入力オブジェクト。
- リストクエリおよび関係フィールド内のオブジェクトをフィルタできるフィルタ入力オブジェクト。
- リストクエリについて、返されるオブジェクトのデフォルト数は100です。limit引数を設定することでこの動作をオーバーライドできます。
@modelディレクティブのタイプ定義
directive @model( queries: ModelQueryMap mutations: ModelMutationMap subscriptions: ModelSubscriptionMap timestamps: TimestampConfiguration) on OBJECT
input ModelMutationMap { create: String update: String delete: String}
input ModelQueryMap { get: String list: String}
input ModelSubscriptionMap { onCreate: [String] onUpdate: [String] onDelete: [String] level: ModelSubscriptionLevel}
enum ModelSubscriptionLevel { off public on}
input TimestampConfiguration { createdAt: String updatedAt: String}関係ディレクティブ
関係ディレクティブは@hasOne、@hasMany、@belongsToおよび@manyToManyです。
@hasOneは以下を生成します:
- 子モデルの主キーおよびソートキーフィールドを参照する親タイプの外部キーフィールド。
- 作成および更新ミューテーション親入力オブジェクトの外部キーフィールド。
@hasOneディレクティブのタイプ定義
directive @hasOne(fields: [String!]) on FIELD_DEFINITION@hasManyは以下を生成します:
- 親モデルの主キーおよびソートキーフィールドを参照する子タイプの外部キーフィールド。
- 作成および更新ミューテーション子入力オブジェクトの外部キーフィールド。
- 子タイプAmazonDynamoDBテーブルのグローバルセカンダリインデックス(GSI)。
@hasManyディレクティブのタイプ定義
directive @hasMany( indexName: String fields: [String!] limit: Int = 100) on FIELD_DEFINITION- 返されるネストされたオブジェクトのデフォルト数は100です。limit引数を設定することでこの動作をオーバーライドできます。
@belongsToは以下を生成します:
- 関連モデルの主キーおよびソートキーフィールドを参照する外部キーフィールド。
- 作成および更新ミューテーション入力オブジェクトの外部キーフィールド。
@belongsToディレクティブのタイプ定義
directive @belongsTo(fields: [String!]) on FIELD_DEFINITION@manyToManyは以下を生成します:
relationNameの名前を持つ中間モデルタイプを定義する結合テーブル。- 両方のモデルの主キーおよびソートキーフィールドを参照する結合テーブルの外部キーフィールド。
- 中間モデル入力オブジェクト作成および更新ミューテーションの外部キーフィールド。
@manyToManyディレクティブのタイプ定義
directive @manyToMany( relationName: String! limit: Int = 100) on FIELD_DEFINITION- 返されるネストされたオブジェクトのデフォルト数は100です。limit引数を設定することでこの動作をオーバーライドできます。