首页 > 解决方案 > 使用 AWS Amplify/AppSync 的嵌套 GraphQL 突变

问题描述

我已经联系了 AWS 论坛,但我希望在这里得到更广泛受众的关注。我正在寻找有关以下问题的任何指导。

我将在下面发布问题:

您好,提前感谢您的帮助。

我是 Amplify/GraphQL 的新手,正在努力让突变发挥作用。具体来说,当我向模型添加连接时,它们永远不会出现在模拟 api 生成器中。如果我把它们写出来,他们会说“输入不存在”。我四处搜索,人们似乎说“在主要项目之前创建子项目,然后更新主要项目”,但我不希望那样。我有一个大型表单,其中包含多个多对多关系,并且它们都需要有效,然后我才能保存主表单。我看不到如何创建每个子项,然后是主项。

但是,这些项目列在响应的可用数据中。在下面的示例中,输入中都缺少地址、股东、董事会。

带有“@connection”的字段都没有作为输入出现在 create api 中。我会接受我能得到的任何帮助/指导。我似乎不理解这里的核心内容。

这是我的模型:

type Company @model(queries: { get: "getEntity", list: "listEntities" }, subscriptions: null) {
id: ID!
name: String!
president: String
vicePresident: String
secretary: String
treasurer: String
shareholders: Shareholder @connection
boardOfDirectors: BoardMember @connection
addresses: [Address]! @connection
...
}

type Address @model{
id: ID!
line1: String!
line2: String
city: String!
postalCode: String!
state: State!
type: AddressType!
}

type BoardMember @model{
id: ID!
firstName: String!
lastName: String!
email: String!
}

type Shareholder @model {
id: ID!
firstName: String!
lastName: String!
numberOfShares: String!
user: User!
}

----一天后----

我已经取得了一些进展,但仍然缺乏对正在发生的事情的一些了解。

我已将架构更新为:

type Company @model(queries: { get: "getEntity", list: "listEntities" }, subscriptions: null) {
id: ID!
name: String!
president: String
vicePresident: String
secretary: String
treasurer: String
...
address: Address @connection
...
}

type Address @model{
id: ID!
line1: String!
line2: String
city: String!
postalCode: String!
state: State!
type: AddressType!
}

我删除了我尝试的多对多关系,现在我仅限于一家只有 1 个地址的公司。我想这是一个未来的问题。但是,现在在输入列表中,“CompanyAddressId”在输入列表中。这表明它希望我在公司之前保存地址。地址只是公司的一部分,如果地址无效并且表单的其他部分失败并且用户退出,我不想保存地址。

我不明白为什么我不能一次写出所有字段?按照上面的架构,我还会有股东、董事会成员等。所以我必须在创建公司之前创建董事会成员和股东的列表?这似乎倒退了。

同样,任何帮助我弄清楚我所缺少的东西的尝试都将不胜感激。

谢谢

--编辑-- 我在资源管理器中看到的 在此处输入图像描述

-- 编辑 2-- 这是根据您的示例新生成的操作。您会看到 Company 现在采用地址 ID —— 我们之前讨论过。但这并不涉及股东的任何事情。为了写出股东,我必须使用需要公司 ID 的“createShareholder”,但公司尚未创建。彻底糊涂了。

在此处输入图像描述

@engam 我希望你能帮助解决新问题。非常感谢你!

标签: graphqlaws-amplifyaws-appsync

解决方案


以下是一些您可以尝试的概念:

对于 @model 指令,请在不重命名查询的情况下试用。AWS Amplify 为自动生成的查询提供了很好的名称。例如,要获得一家公司,它将是 getCompany,而对于列表,它将是 listCompanys。如果您仍想给它起新名称,您可以稍后更改。

对于@connection 指令:@connection需要在连接的两个表上设置。此外,如果您想要多对多连接,则需要添加第三个处理连接的表。当您的模式中有许多连接时,给连接命名也很有用。

只有您在架构中创建的标量类型、String、Int、Float 和 Boolean 等标准 schalar 以及 AWS 特定 schalar(如 AWSDateTime)可以用作架构中的 schalar。查看此链接: https ://docs.aws.amazon.com/appsync/latest/devguide/scalars.html

这是我认为您想要实现的一些示例:

type Company @model {
   id: ID!
   name: String
   president: String
   vicePresident: String
   secretary: String
   treasurer: String
   shareholders: [Shareholder] @connection(name: "CompanySharholderConnection")
   address: Address @connection(name: "CompanyAdressConnection") #one to many example
   # you may add more connections/attributes ...
}

# table handling many-to-many connections between users and companies, called Shareholder.
type Shareholder @model {
   id: ID!
   company: Company @connection(name: "CompanySharholderConnection")
   user: User @connection(name: "UserShareholderConnection")
   numberOfShares: Int #or String
}

type User @model {
   id: ID!
   firstname: String
   lastname: String
   company: [Shareholder] @connection(name: "UserShareholderConnection")
   #... add more attributes / connections here
}

# address table, one address may have many companies
type Address @model {
  id: ID!
  street: String
  city: String
  code: String
  country: String
  companies: [Company] @connection(name: "CompanyAdressConnection") #many-to-one connection
}

每个这种类型...@model 都会生成一个新的 dynamoDB 表。此示例将使您可以创建多个公司和多个用户。要将用户作为股东添加到公司,您只需在 Shareholder 表中创建一个新项目,方法是在 User 表中创建一个新项目,其中用户的 ID 和 Company 表中的公司 ID + 添加多少股。

编辑

请注意,当您在两个表之间生成连接时,放大 cli(它使用 cloudformation 进行后端更改)将为一个或多个 dynamodb 表生成一个新的全局索引,以便 appsync 可以有效地为您提供数据。

dynamodb 的限制使得在编辑表时一次只能生成一个索引 (@connection)。我认为您可以在创建新表 (@model) 时做更多事情。因此,当您编辑一个或多个表时,每次只能在每次放大推送/放大发布之间删除或添加一个连接。否则,当您推送更改时,cloudformation 将失败。这可能是一团糟。因此,我不得不多次删除整个环境,幸运的是不在生产环境中。

更新

(我还用 som 值更新了架构中的地址表);要在创建新公司时连接新地址,您首先必须在 dynamoDb 的地址表中创建一个新地址项。

从 appsync 生成的这个突变可能被命名为 createAddress() 并接受一个 createAddressInput。

创建地址后,您将收到整个新创建的项目,包括自动创建的 ID(如果您没有自己添加)。

现在您可以保存您正在创建的新公司。createCompany 突变采用的属性之一是您创建的地址的 id,可能命名为 companyAddressId。在此处存储地址 ID。然后,当您使用 getCompany 或 listCompanys 检索您的公司时,您将获得您公司的地址。

Javascript 示例:

const createCompany = async (address, company) => {
   // api is name of the service with the mutations and queries
   try {
      const newaddress = await this.api.createAddress({street: address.street, city: address.city, country: address.country});
      const newcompany = await this.api.createCompany({
        name: company.name,
        president: company.president,
        ...
        companyAddressId: newaddress.id
      })
   } catch(error) {
      throw error
   }
   
  
}

// and to retrieve the company including the address, you have to update your graphql statement for your query:

const statement = `query ListCompanys($filter: ModelPartiFilterInput, $limit: Int, $nextToken: String) {
   listCompanys(filter: $filter, limit: $limit, nextToken: $nextToken) {
      __typename
      id
      name
      president
      ...
      address {
         __typename
         id
         street
         city
         code
         country
      }
   }

}

`

AppSync 现在将检索您的所有公司(取决于您的过滤器和限制)以及您已将地址连接到的那些公司的地址。

编辑 2 每个带有 @model 的类型都是对 aws 中的 dynamoDb 表的引用。因此,当您在两个表之间创建一对多关系时,当两个项目都是新的时,您首先必须在一对多关系中创建“多”。在 dynamoDb Company 表中,当一个地址可以有多个公司,而一个公司只能有一个地址时,您必须将地址的 id(dynamoDB 主键)存储在公司上。您当然可以在前端生成地址 id,并将其用于地址的 id,并将其用于公司的 addressCompanyId 并使用await Promise.all([createAddress(...),createCompany(...)),但如果一个失败,另一个将被创建(但通常 appsync api 非常稳定,因此如果您发送的数据正确,则不会失败)。

另一种解决方案,如果您通常不必在多个表中创建/更新多个项目,则可以将地址直接存储在公司项目中。

type Company @model {
  name: String
  ...
  address: Address # or [Address] if you want more than one Address on the company
}
type Address {
   street: String
   postcode: String
   city: string
}

那么地址类型将是 dynamoDb 中同一个表中同一个项目的一部分。但是您将失去对地址(或股东)进行查询以查找地址并查看哪些公司位于那里(或类似地查找一个人并查看该人拥有哪些公司的股份)的能力。一般来说,我不喜欢这种方法,因为它将您的应用程序锁定在一个特定的事物上,并且以后更难创建新功能。

据我所知,不可能在一个 graphql(Amplify/AppSync)突变中的多个 dynamoDb 表中创建多个项目。因此,使用 Promise.all() 进行异步等待并在创建项目之前手动生成前端的 id 属性可能是您的最佳选择。


推荐阅读