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

Any way to split up multiple Fragment expansions for a GraphQL query into multiple calls?

上下文

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

问题

我正在尝试创建一个高度可定制的页面,该页面可以根据提供给它的数据从多种 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 不支持它。

有什么想法吗?

遗憾的是 @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
  }
}