有啥方法可以将 GraphQL 查询的多个 Fragment 扩展拆分为多个调用?

Posted

技术标签:

【中文标题】有啥方法可以将 GraphQL 查询的多个 Fragment 扩展拆分为多个调用?【英文标题】:Any way to split up multiple Fragment expansions for a GraphQL query into multiple calls?有什么方法可以将 GraphQL 查询的多个 Fragment 扩展拆分为多个调用? 【发布时间】:2021-11-28 18:29:45 【问题描述】:

上下文

这个问题可能取决于某些选择,其中一些是可变的,而另一些则不是。我们正在使用以下技术和框架:

Relay / React / TypeScript 内容堆栈 (CMS)

问题

我正在尝试创建一个高度可定制的页面,该页面可以根据呈现给它的数据从多种 UI 组件构建(以允许使用 CMS 使用预制 UI 以不可预知的顺序构建页面)。

我的第一次尝试是为可能在数组中引用的潜在 UI 组件创建一组片段:

query CustomPageQuery 
    title
    description
    customContentConnection 
        edges 
            node 
                ... HeroFragment
                ... TweetBlockFragment
                ... EmbeddedVideoFragment
                
                """
                Further fragments are added here as we add more kinds of UI  
                """
            
        
    

在我们使用的 CMS (ContentStack) 中,此查询的复杂性已经增长到被拒绝的程度,因为它需要在单个查询中调用太多数据库。出于这个原因,我希望有一种方法可以拆分对片段的调用,这样它们就不会成为初始查询的一部分,或者是一些类似的解决方案会导致将此查询分成多个部分。

我希望 @defer 指令能为我解决这个问题,但 relay-compiler 不支持它。

有什么想法吗?

【问题讨论】:

【参考方案1】:

遗憾的是,@defer 仍然不是标准,因此大多数实现都不支持它(因为您还需要服务器来支持它)。

我不确定我是否正确理解了该问题,但您可能希望更多地使用@skip@include 根据事物的类型仅获取您需要的片段。但它需要前端知道它想事先查询什么。

query CustomPageQuery($hero: Boolean, $tweet: Boolean, $video: Boolean) 
    title
    description
    customContentConnection 
        edges 
            node 
                ... HeroFragment @include(if: $hero)
                ... TweetBlockFragment @include(if: $tweet)
                ... EmbeddedVideoFragment @include(if: $video)
            
        
    

通常,您希望能够区分类型而无需进行数据库查询。所以说:

type Hero 
  id: ID
  name: String

type Tweet 
  id: ID
  content: String

union Content = Hero | Tweet

  Content: 
    __resolveType: (parent, ctx) => 
      // That should be able to resolve the type without a DB query
    ,
  

一旦通过,每个片段都会被解析,从而进行更多的数据库查询。如果这些没有与数据加载器正确批处理,那么您将遇到 N+1 问题。我不确定您在后端有多少控制(如果有的话),但您的问题没有灵丹妙药。

如果您无法对后端进行优化,那么我建议您尝试限制连接。他们似乎在使用基于游标的分页,因此您从 first: 10 开始,一旦返回第一批,您可以通过将 after 设置为上一批的最后一个游标来查询下一个元素:

query CustomPageQuery($after: String) 
    customContentConnection(first: 10, after: $after) 
        edges 
            cursor
            node 
                ... HeroFragment
                ... TweetBlockFragment
                ... EmbeddedVideoFragment
            
        
        pageInfo 
          hasNextPage
        
    

作为最后的手段,您可以尝试首先获取所有 ID,然后针对每个 ID(我猜使用别名)或类型(如果您可以在连接字段上过滤)对 CMS 进行后续查询。但我觉得写它很脏,所以如果可以的话,请避免它。


  one: node(id: "UUID1") 
    ... HeroFragment
    ... TweetBlockFragment
    ... EmbeddedVideoFragment
  
  two: node(id: "UUID2") 
    ... HeroFragment
    ... TweetBlockFragment
    ... EmbeddedVideoFragment
  

【讨论】:

嗯,这里有很好的信息。我不确定这是否真的有助于我的情况,但我很感激!我会进一步挖掘。

以上是关于有啥方法可以将 GraphQL 查询的多个 Fragment 扩展拆分为多个调用?的主要内容,如果未能解决你的问题,请参考以下文章

操作参数和 GraphQL 变量有啥区别?

有啥方法可以导入带有自定义指令的查询吗?

将多个参数传递给 GraphQL 查询

400 对 GraphQL 端点的错误请求(我的测试查询有啥问题?)

Apollo (GraphQL) 在查询中获取多个元素

有啥方法可以避免一个查询中的多个联接