Лучшая практика для объединения нескольких вызовов rest для заполнения 1 типа graphQL в apollo-сервере - PullRequest
0 голосов
/ 07 апреля 2020

У меня есть тип пользователя graphql, которому нужна информация от нескольких API REST и разных серверов. Basi c пример: получить имя пользователя из остального домена 1 и получить фамилию из остального домена 2. Оба остальных домена имеют общий атрибут "userID".

Простой пример кода моего преобразователя atm:

user: async (_source, args, { dataSources }) => {
  try {
    const datasource1 = await dataSources.RESTAPI1.getUser(args.id);
    const datasource2 = await dataSources.RESTAPI2.getUser(args.id);

    return { ...datasource1, ...datasource2 };
  } catch (error) {
    console.log("An error occurred.", error);
  }
  return [];
}

Это прекрасно работает для этой простой версии, но у меня есть 2 проблемы с этим решением: во-первых, в IRL много логик c, которые объединяются в результаты 2 json. Поскольку некоторые поля являются общими, но имеют разные данные (или являются пустыми). Так что это как вишня, выбирающая оба результата, чтобы создать объединенный результат.

Моя вторая проблема заключается в том, что это все еще метод водопада. Сначала получите данные от restapi1, когда это будет сделано, вызовите restapi2. По сути, apollo-server повторно вводит rest-waterfall-fetch, который пытается решить graphql.

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

Большое спасибо!

Ответы [ 3 ]

1 голос
/ 07 апреля 2020

Что касается производительности, если два вызова не зависят друг от друга, вы можете использовать Promise.all для их параллельного выполнения:

const [dataSource1,dataSource2] = await Promise.all([
  dataSources.RESTAPI1.getUser(args.id),
  dataSources.RESTAPI2.getUser(args.id),
])

Обычно мы разрешаем логарифму распознавателя по умолчанию GraphQL c делать тяжелая работа, но если вы обнаружите, что вам нужно «выбрать вишню» данных обоих вызовов, вы можете вернуть что-то вроде этого в свой root resolver:

return { dataSource1, dataSource2 }

и затем написать решатели для каждого поля:

const resolvers = {
  User: {
    someField: ({ dataSource1, dataSource2 }) => {
      return dataSource1.a || dataSource2.b
    },
    someOtherField: ({ dataSource1, dataSource2 }) => {
      return someCondition ? dataSource1.foo : dataSource2.bar
    },
  }
}
1 голос
/ 07 апреля 2020

При условии, что ваш user распознаватель возвращает type User оставленный ...

type User {
  id: ID!
  datasource1: RandomType
  datasource1: RandomType
}

Вы можете создать отдельные преобразователи для каждого поля в type User, это может снизить сложность запроса user , только для запрошенных полей.

query {
 user {
  id
  datasource1 {
    ...
  }
 }
}
const resolvers = {
    Query: {
        user: () => {
            return { id: "..." };
        }
    },
    User: {
        datasource1: () => { ... },
        datasource2: () => { ... } // i wont execute
    }
};

datasource1 & datasource2 распознаватели будут выполняться только параллельно, после выполнения Query.user.

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

Для параллельного вызова.

const users = async (_source, args, { dataSources }) => {
  try {
    const promises = [
      dataSources.RESTAPI1,
      dataSources.RESTAPI2
    ].map(({ getUser }) => getUser(args.id));
    const data = await Promise.all(promises);
    return Object.assign({}, ...data);
  } catch (error) {
    console.log("An error occurred.", error);
  }
  return [];
};
...