GraphQL стежка и объединение - PullRequest
0 голосов
/ 03 апреля 2020

Мне нужно объединить несколько сервисов graphQl (с одинаковой схемой) в единый сервис только для чтения (только запрос), предоставляющий данные всех сервисов. Например:

---- domain 1 ----
    "posts": [
      {
        "title": "Domain 1 - First post",
        "description": "Content of the first post"
      },
      {
        "title": "Domain 1 - Second post",
        "description": "Content of the second post"
      }
    ]

---- domain 2 ----
    "posts": [
      {
        "title": "Domain 2 - First post",
        "description": "Content of the first post"
      },
      {
        "title": "Domain 2 - Second post",
        "description": "Content of the second post"
      }
    ]

Я понимаю, что «сшивание» предназначено не для U C, как это, а для объединения разных микроуслуг в один и тот же API. Чтобы иметь одинаковые типы (имена) в одном API, я реализовал «пространства имен бедняков», добавляя доменное имя «на лету» ко всем типам данных. Тем не менее, я могу только сделать запрос с двумя различными типами, как это:

query {
  domain_1_posts {
    title
    description
  }
  domain_2_posts {
    title
    description
  }
}

, но это приводит к тому, что набор данных состоит из двух массивов:

{
  "data": {
    "domain_1_posts": [
      { ...},
    ],
    "domain_2_posts": [
      { ...},
    ]
  }
}

I хотели бы услышать ваши идеи, что я могу сделать, чтобы объединить их в один набор данных, содержащий только posts? Одна идея состоит в том, чтобы добавить собственный распознаватель, который может вызывать фактические распознаватели и объединять результаты в один массив (если это вообще поддерживается). Кроме того, как план B, я мог бы посылать запрос 'domain' для запроса, а затем строить запрос к первому или второму домену (но, чтобы сохранить первоначальный запрос 'domain-agnosti c', например, без использования доменных имен в запросе сама?

Заранее спасибо за все предложения ...

1 Ответ

0 голосов
/ 15 апреля 2020

Мне удается найти решение для моего варианта использования, поэтому я оставлю его здесь на случай, если кто-нибудь столкнется с этим потоком ...

Как уже упоминалось, для создания единой конечной точки следует использовать вышивание из нескольких сегментов API (микросервисы). В случае, если вы попытаетесь сшить схемы, содержащие одинаковые типы или запросы, ваш запрос будет «перенаправлен» на предварительно выбранный экземпляр (т.е. только на один).

Как предложено @xadm, ключ для «объединения» данных из нескольких схем в единый набор данных используется пользовательская логика выборки c для Link, используемой для удаленной схемы, как объяснено:

1) Определите пользовательскую функцию выборки, соответствующую потребностям вашего бизнеса (упрощенный пример):

const customFetch = async (uri, options) => {

    // do not merge introspection query results!!!
    // for introspection query always use predefined (first?) instance
    if( operationType === 'IntrospectionQuery'){
      return fetch(services[0].uri, options);
    }

    // array fecth calls to different endpoints
    const calls = [
        fetch(services[0].uri, options),
        fetch(services[1].uri, options),
        fetch(services[2].uri, options),
        ...
    ];

    // execute calls in parallel
    const data = await Promise.all(fetchCalls);

    // do whatever you need to merge data according to your needs
    const retData = customBusinessLogic();

    // return new response containing merged data
    return new fetch.Response(JSON.stringify(retData),{ "status" : 200 });

}

2) Определить ссылку с помощью пользовательской функции выборки. Если вы используете идентичные схемы, вам не нужно создавать ссылки на каждый экземпляр, достаточно одной.

const httpLink = new HttpLink(services[0].uri, fetch: customFetch });

3) Используйте Link для создания удаленной исполняемой схемы:

const schema =  await introspectSchema(httpLink );
return makeRemoteExecutableSchema({ 
    schema, 
    link: httpLink,
    context: ({ req }) => {
        // inject http request headers into context if you need them
        return {
            headers: {
                ...req.headers,
            }
        }
      }, 
})

4) Если вы хотите перенаправить заголовки http полностью в функцию выборки, используйте apollo ContextLink:

   // link for forwarding headers through context
   const contextLink = setContext( (request, previousContext) => {
     if( previousContext.graphqlContext ){
         return {
            headers: {
                ...previousContext.graphqlContext.headers
            }
          }   
    }
  }).concat(http);  

Просто упомяните, что для этого используются зависимости:

const { introspectSchema, makeRemoteExecutableSchema, ApolloServer } = require('apollo-server');
const fetch = require('node-fetch');
const { setContext } = require('apollo-link-context');
const { HttpLink } = require('apollo-link-http');

Я надеюсь, что это будет кому-то полезно ...

...