graphql模式循环引用是反模式吗?

graphql模式循环引用是反模式吗?,graphql,apollo,Graphql,Apollo,如下所示的graphql模式: 输入用户{ id:id! 地点:地点 } 类型位置{ id:id! 用户:用户 } 现在,客户端发送一个graphql查询。理论上,用户和位置可以无限循环引用 我认为这是一种反模式。就我所知,在graphql和apollo社区中都没有中间件或方法来限制查询的嵌套深度 这种无限嵌套深度的查询将为我的系统消耗大量资源,如带宽、硬件和性能。不仅是服务器端,而且是客户端 因此,如果graphql模式允许循环引用,那么应该有一些中间件或方法来限制查询的嵌套深度。或者,为查

如下所示的graphql模式:

输入用户{
id:id!
地点:地点
}
类型位置{
id:id!
用户:用户
}
现在,客户端发送一个
graphql
查询。理论上,
用户
位置
可以无限循环引用

我认为这是一种反模式。就我所知,在
graphql
apollo
社区中都没有中间件或方法来限制查询的嵌套深度

这种无限嵌套深度的查询将为我的系统消耗大量资源,如带宽、硬件和性能。不仅是服务器端,而且是客户端

因此,如果graphql模式允许循环引用,那么应该有一些中间件或方法来限制查询的嵌套深度。或者,为查询添加一些约束

也许不允许循环引用是更好的主意

我更喜欢发送另一个查询并在一个查询中执行多个操作。这要简单得多

更新


我找到了这个库:。如果graphql不限制循环引用。此库可以保护您的应用程序免受资源耗尽和DoS攻击。

您显示的模式对于“图形”来说是相当自然的,我不认为在GraphQL中特别不鼓励这种模式。当我想知道“人们如何构建更大的GraphQLAPI”时,我经常会看到这样的情况:a有一个对象循环,可以是a,它有一个
存储库的列表

至少。Apollo显然没有这个控件,但是您可以构建一个自定义的或使用库来避免重复获取您已经拥有的对象。

这取决于具体情况

记住,同一个解决方案在某些上下文中可能是一个好模式,而在其他上下文中可能是一个反模式,这一点很有用。解决方案的价值取决于您使用它的上下文。-

循环引用可能会带来额外的挑战,这一点是正确的。正如您所指出的,它们是一个潜在的安全风险,因为它们使恶意用户能够创建可能非常昂贵的查询。根据我的经验,它们还使客户团队更容易无意中过度获取数据

另一方面,循环引用增加了灵活性。以您的示例运行,如果我们假设以下模式:

type Query {
  user(id: ID): User
  location(id: ID): Location
}

type User {
  id: ID!
  location: Location
}

type Location {
  id: ID!
  user: User
}
很明显,我们可能会进行两种不同的查询,以有效地获取相同的数据:

{
  # query 1
  user(id: ID) {
    id
    location {
      id
    }
  }

  # query 2
  location(id: ID) {
    id
    user {
      id
    }
  }
}
如果API的主要使用者是一个或多个在同一项目上工作的客户机团队,那么这可能无关紧要。前端需要获取特定形状的数据,您可以围绕这些需求设计模式。如果客户端总是获取用户,可以通过这种方式获取位置,并且不需要该上下文之外的位置信息,那么只使用
user
查询并从
location
类型中省略
user
字段可能是有意义的。即使您需要一个
位置
查询,根据客户的需要,在其上公开
用户
字段也可能没有意义

另一方面,假设您的API被更多的客户机使用。可能您支持多个平台,或者支持多个应用程序,这些应用程序执行不同的操作,但共享相同的API来访问您的数据层。或者,您正在公开一个公共API,该API旨在让第三方应用程序与您的服务或产品集成。在这些场景中,您对客户需要什么的想法更加模糊。突然之间,更重要的是公开各种查询底层数据的方法,以满足当前客户机和未来客户机的需求。对于单个客户机的API来说也是如此,其需求可能会随着时间的推移而变化

按照您的建议,总是可以“展平”您的模式,并提供额外的查询,而不是实现关系字段。但是,对客户机来说,这样做是否“更简单”取决于客户机。最好的方法可能是让每个客户机选择适合其需要的数据结构

与大多数体系结构决策一样,存在一种权衡,您的正确解决方案可能与其他团队的不同

如果你有循环引用,所有的希望都不会失去。一些实现具有用于限制查询深度的内置控件。js没有,但是有一些库就是这样做的。值得指出的是,广度和深度是一个同样大的问题——无论是否有循环引用,在解析列表时都应该考虑使用最大限制实现分页,以防止客户端一次可能请求数千条记录


正如@DavidMaze所指出的,除了限制客户端查询的深度外,还可以使用
dataloader
来降低从数据层重复获取相同记录的成本。虽然
dataloader
通常用于批处理请求,以解决由于延迟加载关联而产生的“n+1问题”,但它在这里也有帮助。除了批处理,dataloader还缓存加载的记录。这意味着相同记录的后续加载(在同一请求中)不会命中数据库,而是从内存中获取
循环引用是非速率限制GraphQLAPI的反模式。具有速率限制的API可以安全地使用它们

详细回答:是的,真正的循环引用在更小/更简单的API上是一种反模式。。。但是,当你达到了限制API速率的程度时,你可以用这种限制来“一箭双雕”

另一个答案中给出了一个完美的例子:Github的GraphQLAPI让您请求一个存储库,以及它的所有者、它们的存储库、它们的所有者。。。我
repositories: [Repository]
repositories: [RepositoryLink]