使用 AWS Amplify/AppSync 的嵌套 GraphQL 突变

Posted

技术标签:

【中文标题】使用 AWS Amplify/AppSync 的嵌套 GraphQL 突变【英文标题】:Nested GraphQL mutations with AWS Amplify/AppSync 【发布时间】:2021-04-24 10:00:12 【问题描述】:

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

我将在下面发布问题:

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

我是 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 我希望你能帮助解决新问题。非常感谢!

【问题讨论】:

【参考方案1】:

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

对于@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 公司表中,当一个地址可以有多个公司,而一个公司只能有一个地址时,您必须将地址的 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 属性可能是您的最佳选择。

【讨论】:

非常感谢您的回复!我最终只是使用您的示例来使事情正常进行,并且在资源管理器中看到了同样的事情。输入列表采用 addressid,而不是实际的地址对象。我添加了一个屏幕截图,显示了我所看到的。我想我期待“地址:“地址”:“1234 st。 City State Zip" 作为 createCompany 的输入——不是暗示地址已经存在的 id。 会不会是我在 RDS 方面想太多了?与单独的表相比,nosql 设计是否会更好地将数据扁平化为单个键/值对? 或者如果我将graphql与rds而不是dynamo一起使用,这个问题会变得更容易吗? 如果我理解正确,在我的示例中,当您向公司添加新地址时,您首先必须在 dynamodb 的地址表中创建地址项。您从 amplify 获得的生成的 graphQl 突变的名称和输入可能是 createAddress(createAddressInput)。成功创建新地址后,您需要使用该新项目的 id (address.id),然后仅将地址中的 id 存储在新公司项目上(可能命名为 companyAddressId)。我将在我的答案中添加更详细的解释 实际上,我正在尝试与公司同时创建地址,而不是先于。就像地址的 json 将嵌套在公司下

以上是关于使用 AWS Amplify/AppSync 的嵌套 GraphQL 突变的主要内容,如果未能解决你的问题,请参考以下文章

在 React 中使用 AWS Amplify Appsync 上传图像

AWS Amplify AppSync 订阅无法正常工作

aws amplify appsync 中的 Graphql 突变错误

AWS Amplify (AppSync + Cognito) 使用每个组织/租户的动态组进行授权

AWS Amplify AppSync 使用在 Cognito 中创建的 Google 用户登录

AWS Amplify AppSync 订阅:数据返回 null