Заметные различия между buildSchema и GraphQLSchema?

Есть ли между ними какие-то заметные различия? Меня интересует все, от производительности во время выполнения и запуска до функций и различий в рабочих процессах. Документация плохо объясняет разницу и когда мне следует использовать одно вместо другого.

Пример в обеих версиях:

buildSchema

const { graphql, buildSchema } = require('graphql');

const schema = buildSchema(`
  type Query {
    hello: String
  }
`);

const root = { hello: () => 'Hello world!' };

graphql(schema, '{ hello }', root).then((response) => {
  console.info(response);
});

GraphQLSchema

const { graphql, GraphQLSchema, GraphQLObjectType, GraphQLString } = require('graphql');

const schema = new GraphQLSchema({
  query: new GraphQLObjectType({
    name: 'Query',
    fields: () => ({
      hello: {
        type: GraphQLString,
        resolve: () => 'Hello world!'
      }
    })
  })
});

graphql(schema, '{ hello }').then((response) => {
  console.info(response);
});
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Что такое Apollo Client и зачем он нужен?
Что такое Apollo Client и зачем он нужен?
Apollo Client - это полнофункциональный клиент GraphQL для JavaScript-приложений, который упрощает получение, управление и обновление данных в...
39
0
7 500
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Функция buildSchema принимает схему на SDL (языке определения схемы) и возвращает объект GraphQLSchema. Учитывая две идентичные схемы, созданные каждым методом, производительность во время выполнения будет одинаковой. Время запуска для сервера, использующего buildSchema, будет медленнее, поскольку синтаксический анализ SDL добавляет дополнительный шаг, которого в противном случае не существовало бы - будет ли разница заметный, я не могу сказать окончательно.

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

Схема, созданная с помощью buildSchema:

  • Невозможно указать функции разрешения для отдельных полей
  • Невозможно указать свойства resolveType или isTypeOf для типов, что делает невозможным использование Unions и Interfaces
  • Невозможно использовать специальные скаляры

Пункт № 1 нельзя переоценить - buildSchema не позволяет вам указывать функцию распознавателя для любого поля в вашей схеме. Сюда входят поля на ваших типах Query и Mutation. Примеры, использующие buildSchema, позволяют обойти эту проблему, полагаясь на поведение распознавателя GraphQL по умолчанию и передавая значение root.

По умолчанию, если для поля не указана функция resolve, GraphQL проверит родительское значение (возвращаемое преобразователем родительского поля) и (при условии, что это объект) попытается найти свойство в этом родительском значении, которое соответствует имени поля. Если он находит совпадение, он разрешает поле до этого значения. Если совпадение оказывается функцией, оно сначала вызывает эту функцию, а затем преобразуется в значение, возвращаемое функцией.

В приведенном выше примере поле hello в первой схеме не имеет преобразователя. GraphQL смотрит на родительское значение, которое для поля корневого уровня является значением корень, которое передается. Корневое значение имеет поле с именем hello, и это функция, поэтому он вызывает функцию, а затем преобразуется в значение, возвращаемое функцией. Вы можете добиться того же эффекта, просто сделав свойство hello String вместо функции.

Учитывая вышеизложенное, два примера в вопросе на самом деле нет одинаковы. Скорее, нам придется изменить вторую схему таким образом, чтобы она была эквивалентной:

const schema = new GraphQLSchema({
  query: new GraphQLObjectType({
    name: 'Query',
    fields: () => ({
      hello: {
        type: GraphQLString,
      }
    })
  })
});

const root = { hello: () => 'Hello world!' };

graphql(schema, '{ hello }', root).then((response) => {
  console.info(response);
});

Хотя передача резолвера через корень - это изящный трюк, опять же, он работает только для полей корневого уровня (например, полей типов Query, Mutation или Subscription). Если вы хотите предоставить преобразователь для поля другого типа, это невозможно сделать с помощью buildSchema.

Итог: не используйте buildSchema.

Но я хотел использовать SDL!

И вы все еще можете! Но ... не делайте этого с использованием vanilla GraphQL.js. Вместо этого, если вы хотите использовать SDL для генерации своей схемы, вам следует либо использовать graphql-tools 'makeExecutableSchema, либо использовать более полное решение, такое как apollo-server, которое использует makeExecutableSchema под капотом. makeExecutableSchema позволяет определять схему с помощью SDL, а также предоставляет отдельный объект resolvers. Итак, вы можете:

const typeDefs = `
  type Query {
    hello: String
  }
`

const resolvers = {
  Query: {
    hello: () => 'Hello!',
  },
}

const schema = makeExecutableSchema({ typeDefs, resolvers })

Разница в том, что, в отличие от buildSchema, вы также можете предоставить преобразователи для других типов и даже предоставить свойства resolveType для ваших интерфейсов или объединений.

const resolvers = {
  Query: {
    animals: () => getAnimalsFromDB(),
  }
  Animal: {
    __resolveType: (obj) => obj.constructor.name
  },
  Cat: {
    owner: (cat) => getOwnerFromDB(cat.ownerId),
  }
}

Используя makeExecutableSchema, вы также можете реализовать настраиваемые скаляры и директивы схемы, легко настроить различные правила проверки схемы и даже разрешить реализацию типов для наследования преобразователей от их интерфейсов. Хотя очень важно понимать основы GraphQL.js и то, как сгенерировать базовую схему с помощью конструктора GraphQLSchema, makeExecutableSchema является более полным и гибким решением, которое должно быть лучшим выбором для большинства проектов. См. Документацию для более подробной информации.

ОБНОВИТЬ

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

У меня все в порядке с vanilla buildSchema и JS TBH. Добавление накладных расходов на фреймворки или библиотеки для меня ничего не значит. Честно говоря, мне не нужны резолверы для отдельных полей

PositiveGuy 27.10.2020 02:53

Другие вопросы по теме