Name:
interface
Value:
Amplify has re-imagined the way frontend developers build fullstack applications. Develop and deploy without the hassle.

Page updated Feb 1, 2025

Maintenance ModeYou are viewing Amplify Gen 1 documentation. Amplify Gen 1 has entered maintenance mode and will reach end of life on May 1, 2027. New project should use Amplify Gen 2. For existing Gen 1 projects, a migration guide and tooling are available to help you upgrade. Switch to the latest Gen 2 docs →

データモデルをカスタマイズする

You are currently viewing the new GraphQL transformer v2 docs Looking for legacy docs?

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は、タイプにcreatedAtupdatedAtヘルパーフィールドも追加します。これらのフィールドの値は、明示的に上書きされない限り、クライアント側では読み取り専用です。詳細については、作成および更新タイムスタンプをカスタマイズするを参照してください。

次のクエリを実行して、すべての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」、および「多対多」の関係を作成します。

関係説明
@hasOne2つのモデル間に単方向の1対1関係を作成します。例えば、プロジェクトは1つのチームを「持つ」場合があります。これにより、プロジェクトレコードからチームをクエリできます。
@hasMany2つのモデル間に単方向の1対多関係を作成します。例えば、投稿は多くのコメントを持ちます。これにより、投稿レコードからすべてのコメントをクエリできます。
@belongsTo「1対1」または「1対多」の関係を双方向にするために「属する」関係を使用します。例えば、プロジェクトは1つのチームを持ち、チームはプロジェクトに属します。これにより、プロジェクトレコードからチームをクエリでき、その逆も可能です。
@manyToMany2つのモデル間の多対多関係を容易にするための「結合テーブル」を構成します。例えば、ブログは多くのタグを持ち、タグは多くのブログを持ちます。

With versions of Amplify CLI @aws-amplify/cli@12.12.2 and API Category@aws-amplify/amplify-category-api@5.11.5, an improvement was made to how relational field data is handled in subscriptions when different authorization rules apply to related models in a schema. The improvement redacts the values for the relational fields, displaying them as null or empty, to prevent unauthorized access to relational data. This redaction occurs whenever it cannot be determined that the child model will be protected by the same permissions as the parent model.

Because subscriptions are tied to mutations and the selection set provided in the result of a mutation is then passed through to the subscription, relational fields in the result of mutations must be redacted.

If an authorized end-user needs access to the redacted relational field they should perform a query to read the relational data.

Additionally, subscriptions will inherit related authorization when relational fields are set as required. To better protect relational data, consider modifying the schema to use optional relational fields.

Based on the security posture of your application, you can choose to revert to the subscription behavior before this improvement was made.

To do so, use the subscriptionsInheritPrimaryAuth feature flag under graphqltransformer in the amplify/backend/cli.json file.

  • If enabled, subscriptions will inherit the primary model authorization rules for the relational fields.
  • If disabled, relational fields will be redacted in mutation response when there is a difference between auth rules between primary and related models.

Has One関係

The @hasOne and @hasMany directives do not support referencing a model which then references the initial model via @hasOne or @hasMany if DataStore is enabled.

@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関係

The @hasOne and @hasMany directives do not support referencing a model which then references the initial model via @hasOne or @hasMany if DataStore is enabled.

@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 post
const result = await client.graphql({
query: createPost,
variables: {
input: { title: 'Hello World!!' }
}
});
const post = result.data.createPost;
// create comment
await client.graphql({
query: createComment,
variables: {
input: { content: 'Hi!', postID: post.id }
}
});
// get post
const result = await client.graphql({
query: getPost,
variables: { id: post.id }
});
const postWithComments = result.data.createPost;
const postComments = postWithComments.comments.items; // access comments from post

Belongs 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 team
const result = await client.graphql({
query: createTeam,
variables: {
input: { name: 'New Team' }
}
});
const team = result.data.createTeam;
// create project
const 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 team
const 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 team
type 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 post
const result = await client.graphql({
query: createPost,
variables: {
input: { title: 'Hello World!!' }
}
});
const post = result.data.createPost;
// create comment
await client.graphql({
query: createComment,
variables: {
input: { content: 'Hi!', postID: post.id }
}
});
// get post
const 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;

@belongsTofields引数なしで使用できます。その場合、親の主キーを参照するフィールドが自動的に生成されます。

または、親オブジェクトの参照を保存するカスタムフィールドをセットアップします。双方向「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 post
const result = await client.graphql({
query: createPost,
variables: {
input: { title: 'Hello World' }
}
});
const post = result.data.createPost;
// create tag
const tagResult = await client.graphql({
query: createTag,
variables: {
input: {
label: 'My Tag'
}
}
});
const tag = tagResult.data.createTag;
// connect post and tag
await client.graphql({
query: createPostTags,
variables: {
input: {
postId: post.id,
tagId: tag.id
}
}
});
// get posts
const listPostsResult = await client.graphql({ query: listPosts });
const posts = listPostsResult.data.listPosts;
const postTags = posts[0].tags; // access tags from post

重要:Amplifyが多対多関係の「結合テーブル」に適用する認可ルールは、個々のモデルの認可ルールの合併です。詳細については、この議論を参照してください。

フィールドのデフォルト値を割り当てる

@defaultディレクティブを使用して、IntStringなど、オプションのスカラータイプフィールドのデフォルト値を指定できます。

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ディレクティブは、各エンティティにcreatedAtupdatedAtタイムスタンプを自動的に追加します。タイムスタンプフィールド名は、ディレクティブに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ファイルを分割する

Amplify StudioはGraphQLスキーマの分割をサポートしていません。

Amplify Studioを使用している場合は、Amplify Studioのデータモデリングドキュメントの制限セクションに従ってください。

AWS Amplifyは、GraphQLスキーマを複数の.graphqlファイルに分割することをサポートしています。

amplify/backend/api/<api-name>/schema/ディレクトリを作成することで開始できます。例として、ブログサイトのスキーマをBlog.graphqlPost.graphql、およびComment.graphqlファイルを作成して分割することができます。

その後、amplify api gql-compileを実行し、出力ビルドスキーマにはスキーマファイル全体で宣言されたすべてのタイプが含まれます。

プロジェクトが成長するにつれて、プロジェクトのサイズとメンテナンス要件に応じて、カスタムクエリ、ミューテーション、およびサブスクリプションを整理することができます。それらをすべて1つのファイルに統合することも、対応するモデルと同じ場所に配置することもできます。

単一のQuery.graphqlファイルを使用する

この方法では、すべてのクエリを単一のQuery.graphqlファイルに統合することが含まれます。これは小規模なプロジェクトや、すべてのクエリを1つの場所に保ちたい場合に便利です。

  1. amplify/backend/api/<api-name>/schema/ディレクトリで、Query.graphqlという名前のファイルを作成します。

  2. 複数のスキーマファイルからすべてのクエリタイプ定義をQuery.graphqlファイルにコピーします。

  3. すべてのクエリが適切にフォーマットされ、単一のtype Query { ... }ブロック内に囲まれていることを確認します。

extendキーワードを使用する

複数のスキーマファイルでQueryタイプを宣言すると、amplify api gql-compileを実行するときに以下のようなスキーマ検証エラーが発生します:

🛑 Schema validation failed.
There can be only one type named "Query".

AmplifyGraphQLスキーマはextendキーワードをサポートしており、これにより追加のフィールドを持つタイプを拡張できます。この場合、カスタムクエリ、ミューテーション、およびサブスクリプションを複数のファイルに分割することもできます。これは、より大規模でより複雑なプロジェクトに対してより理想的である可能性があります。

  1. プロジェクトのアーキテクチャに従ってGraphQLスキーマを複数のファイルに整理します。

  2. ファイルの1つ(例えば、schema1.graphql)で、タイプを通常通り宣言します:

schema1.graphql
type Query {
# initial custom queries
myQuery: String @function(name: "myQueryFunction-${env}")
}
  1. 他のスキーマファイル(例えば、schema2.graphql)で、extendキーワードを使用してタイプを追加します:
schema2.graphql
extend type Query {
# additional custom queries
myQuery2: String @function(name: "myQuery2Function-${env}")
}

Query型を拡張する順序は、個別のスキーマファイルのコンパイルに影響しません。ただし、別のスキーマファイルで同じフィールド名を持つカスタムQuery、Mutation、および/またはSubscription拡張を宣言すると、以下のようなスキーマ検証エラーが発生します:

🛑 Object type extension 'Query' cannot redeclare field getBlogById

  1. Amplifyディレクティブを使用して拡張型のフィールドに機能を追加します。AmplifyはQueryMutation、およびSubscription型拡張のフィールドで@auth@function、および@httpディレクティブをサポートしています。または、extendキーワードを使用して、カスタムリゾルバーを使用するカスタムクエリ、ミューテーション、およびサブスクリプションを整理できます。

Amplifyディレクティブは、拡張型定義自体(例えば、extend type Todo @auth...)、またはQueryMutation、およびSubscription以外の拡張型のフィールドではサポートされていません。

動作方法

モデルディレクティブ

@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引数を設定することでこの動作をオーバーライドできます。