Любой способ разделить несколько расширений фрагментов для запроса GraphQL на несколько вызовов?

#graphql #graphql-js #relayjs #relay

Вопрос:

Контекст

Эта проблема, скорее всего, связана с определенным выбором, некоторые из которых можно изменить, а некоторые-нет. Мы используем следующие технологии и структуры:

  • Ретрансляция / Реакция / МашиНопись
  • Контент-стек (CMS)

Проблема

Я пытаюсь создать настраиваемую страницу, которую можно создавать из нескольких типов компонентов пользовательского интерфейса на основе представленных данных (чтобы страницы можно было создавать с помощью CMS, используя сборный пользовательский интерфейс в непредсказуемом порядке).

Моей первой попыткой в этом было создать набор фрагментов для потенциальных компонентов пользовательского интерфейса, на которые можно ссылаться в массиве:

 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. Я не уверен, насколько сильно вы контролируете (если вообще контролируете) серверную часть, но для вашей проблемы нет серебряной пули.

Если вы не можете оптимизировать серверную часть, я бы посоветовал попытаться ограничить подключение. Похоже, они используют разбиение на страницы на основе курсора, поэтому вы начинаете с say first: 10 , и как только будет возвращена первая партия, вы можете запросить следующие элементы, установив значение after на последний курсор предыдущей партии:

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

В крайнем случае, вы можете попытаться сначала получить все идентификаторы, а затем выполнить последующие запросы к CMS для каждого идентификатора (я думаю, используя псевдонимы) или типа (если вы можете фильтровать поле подключения). Но я чувствую себя грязным, просто пишу это, так что избегайте этого, если можете.

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

Комментарии:

1. Хм, хорошая информация здесь. Я не уверен, что это действительно помогает моему делу, но я ценю это! Я буду копать дальше.