Первый опыт GraphQL, Node.js Build GraphQL API Руководство API

Node.js GraphQL
Первый опыт GraphQL, Node.js Build GraphQL API Руководство API

Поиск в WeChat【Front-end разработчик полного стека] Обратите внимание на это выпадение волос, установите киоски,продавать товары

оригинал:blog.heroku.com

GraphQL

В традиционном методе на основе REST API клиент делает запрос, сервер определяет ответ:

curl https://api.heroku.space/users/1

{
  "id": 1,
  "name": "Luke",
  "email": "luke@heroku.space",
  "addresses": [
    {
    "street": "1234 Rodeo Drive",
    "city": "Los Angeles",
    "country": "USA"
    }
  ]
}

Однако в GraphQL клиент может точно определить, какие данные он получает с сервера. Например, клиенту может потребоваться только имя пользователя и адрес электронной почты без какой-либо информации об адресе:

curl -X POST https://api.heroku.space/graphql -d '
query {
  user(id: 1) {
    name
    email
  }
}


{
  "data":
    {
    "name": "Luke",
    "email": "luke@heroku.space"
    }
}

С помощью этого нового режима клиенты могут удовлетворять свои потребности за счет уменьшения отклика, чтобы сделать более эффективные запросы к серверу. Для одностраничных приложений (SPA) или других клиентских приложений с большим весом время рендеринга можно ускорить за счет уменьшения размера полезной нагрузки. Однако, как и любой фрейм или язык, Graphql также нуждается в обмене. В этой статье мы рассмотрим плюсы и минусы языка запросов с использованием Graphql в качестве API, а также то, как приступить к реализации.

Почему стоит выбрать GraphQL?

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

Рассмотрим приложение SaaS, использующее API для подключения к удаленной базе данных. Вы хотите отобразить страницу профиля пользователя, вам может понадобиться сделать APIGETВызывается для получения информации о пользователе, такой как имя пользователя или адрес электронной почты. Затем вам может понадобиться сделать еще один вызов API, чтобы получить информацию об адресе, который хранится в другой таблице. По мере роста вашего приложения вам может потребоваться продолжать делать больше вызовов API в разные места из-за того, как оно построено. Хотя каждый вызов API может выполняться асинхронно, вам также необходимо обрабатывать их ответы, будь то ошибки, тайм-ауты сети или даже приостановка рендеринга страницы до тех пор, пока не будут получены все данные. Как упоминалось выше, полезная нагрузка этих ответов может превышать то, что необходимо для отображения вашей текущей страницы, а поскольку каждый вызов API имеет сетевую задержку, общая задержка может составить значительную сумму.

С GraphQL вам не нужно делать несколько вызовов API (например,GET /user/:idа такжеGET /user/:id/addresses

query {
  user(id: 1) {
    name
    email
    addresses {
    street
    city
    country
    }
  }
}

		addresses {
      street
+     apartmentNumber   # new information
      city
      country
    }

Это значительно упрощает разработку во время вашего процесса применения.

Определить схему GraphQL

Существуют реализации сервера GraphQL на разных языках программирования, но прежде чем приступить к работе, вам необходимо идентифицировать объекты в вашей бизнес-области, как и любой API. Подобно тому, как REST API может использовать схему JSON, GraphQL использует SDL или язык определения схемы для определения своей схемы, которая представляет собой идемпотентный способ описания всех объектов и полей, доступных для GraphQL API. Общий формат записи SDL выглядит следующим образом:

type $OBJECT_TYPE {
  $FIELD_NAME($ARGUMENTS): $FIELD_TYPE
}

Давайте продолжим предыдущий пример и определим, как выглядят записи для пользователя и адреса.

type User {
  name:     String
  email:    String
  addresses:   [Address]
}

type Address {
  street:   String
  city:     String
  country:  String
}

userопределяет дваStringполя, соответственноnameа такжеemail, который также включает в себяaddressesполе, этоAddressesмассив объектов.AddressesТакже определяет несколько собственных полей. (Кстати, схемы GraphQL имеют больше, чем просто объекты, поля и скалярные типы, и вы также можете комбинировать интерфейсы, объединения и параметры для создания более сложных моделей, но это не рассматривается в этой статье.)

Нам также необходимо определить тип, который является точкой входа в наш GraphQL API. Вы помните, ранее мы говорили, что запрос GraphQL выглядит так:

query {
  user(id: 1) {
    name
    email
  }
}

ДолженqueryПоля относятся к специальному зарезервированному типу, называемомуQuery, который указывает основную точку входа для получения объектов. (а также для модификации объектовMutationТипы. ) здесь мы определяемuserполе, которое возвращаетUserобъект, поэтому наша схема также должна определить это поле:

type Query {
  user(id: Int!): User
}

type User { ... }
type Address { ... }

Аргументы в поле представляют собой списки, разделенные запятыми, в формате$NAME: $TYPE.!GraphQL указывает, что параметр требуется, это способ, он необязательно опущен.

Согласно языку по вашему выбору, этот шаблон, включенный в сервер процесса, будет варьироваться, но, как правило, информация используется в качестве строки достаточно. Там node.js.graphqlпакет для подготовки схемы GraphQL, но мы будем использоватьgraphql-toolsМешок заменяется, потому что он дает больше преимуществ. Давайте импортируем пакет и прочитаем определение нашего типа, подумаем о подготовке к будущей разработке:

const fs = require('fs')
const { makeExecutableSchema } = require("graphql-tools");

let typeDefs = fs.readFileSync("schema.graphql", {
  encoding: "utf8",
  flag: "r",
});

Установить парсер

Схема определяет способ построения запросов, но построение схемы для определения модели данных — это только часть спецификации GraphQL. Другая часть включает в себя фактическое получение данных, что делается с помощью синтаксического анализатора, который представляет собой функцию, возвращающую базовое значение поля.

Давайте посмотрим, как парсер реализован в Node.js. Мы намерены закрепить концепции того, как анализатор работает со схемой, поэтому мы не будем вдаваться в подробности о том, как настроить хранилище данных. В «реальном мире» мы могли бы использовать что-то вродеknex

const users = {
  1: {
    name: "Luke",
    email: "luke@heroku.space",
    addresses: [
    {
      street: "1234 Rodeo Drive",
      city: "Los Angeles",
      country: "USA",
    },
    ],
  },
  2: {
    name: "Jane",
    email: "jane@heroku.space",
    addresses: [
    {
      street: "1234 Lincoln Place",
      city: "Brooklyn",
      country: "USA",
    },
    ],
  },
};

Преобразователь GraphQL эквивалентен объекту Node.js, имя ключевого поля должно быть извлечено, значение является функцией возвращаемых данных. Давайте инициалuserПростой пример поиска по id начинается:

const resolvers = {
  Query: {
    user: function (parent, { id }) {
      // 用户查找逻辑
    },
  },
}

Этот синтаксический анализатор принимает два параметра: объект, представляющий родителя (который обычно не используется в исходном корневом запросе), и объект JSON, содержащий параметры, переданные вашим полям. Не каждое поле будет иметь параметры, но в этом случае они будут, потому что нам нужно получить их пользователя по идентификатору пользователя. Остальная часть функции проста:

const resolvers = {
  Query: {
    user: function (_, { id }) {
      return users[id];
    },
  }
}

Вы заметите, что мы не определяем явноUserилиAddressesпарсер,graphql-toolsПакет Smart достаточно, чтобы автоматически сопоставить их для нас. Если мы выберем, мы можем переопределить их, но теперь мы определили определения наших типов и парсер, мы можем построить наш полный рисунок:

const schema = makeExecutableSchema({ typeDefs, resolvers });

запустить сервер

Наконец, давайте запустим демо! Поскольку мы используем Express, мы можем использоватьexpress-graphqlpackage, чтобы представить нашу схему в качестве конечной точки. Для пакета требуется два параметра: схема и корневое значение, у него есть один необязательный параметр.graphiql, который мы обсудим позже.

Используйте настройки сервера промежуточного программного обеспечения GraphQL Express на своем любимом порту следующим образом:

const express = require("express");
const express_graphql = require("express-graphql");

const app = express();
app.use(
  "/graphql",
  express_graphql({
    schema: schema,
    graphiql: true,
  })
);
app.listen(5000, () => console.log("Express is now live at localhost:5000"));

Этоgraphiql: true

query {
  user(id: 1) {
    name
    email
  }
}

Чтобы изучить типизированные возможности GraphQL, попробуйте передать строку вместо целого числа для параметра ID.

# 这不起作用
query {
  user(id: "1") {
    name
    email
  }
}

Вы даже можете попробовать запросить несуществующие поля:

# 这不起作用
query {
  user(id: 1) {
    name
    zodiac
  }
}

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

Вопросы производительности

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

тайник

API на основе REST не нужно слишком заботиться о кэшировании, поскольку они могут основываться на существующих стратегиях заголовков HTTP, используемых в других частях Интернета. GraphQL не имеет этих механизмов кэширования, что создает ненужную нагрузку на обработку повторяющихся запросов. Рассмотрим следующие два запроса:

query {
  user(id: 1) {
    name
  }
}

query {
  user(id: 1) {
    email
  }
}

Без какого-либо кэширования простое получение двух разных столбцов привело бы к двум запросам к базе данных для получения идентификаторов как1изUser. Фактически, поскольку GraphQL также допускает псевдонимы, следующий запрос работает и также выполняет два поиска:

query {
  one: user(id: 1) {
    name
  }
  two: user(id: 2) {
    name
  }
}

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

dataloaderПакет предназначен для решения этих двух проблем. Учитывая массив идентификаторов, мы получим все эти одноразовые идентификаторы из базы данных; Точно так же последующие вызовы одного и того же идентификатора также будут использовать кеш для получения проекта. нужно использоватьdataloaderЧтобы построить это, нам нужны две вещи. Во-первых, нам нужна функция для загрузки всех запрошенных объектов. В нашем примере это выглядит так:

const DataLoader = require('dataloader');
const batchGetUserById = async (ids) => {
   // 在现实生活中,这将是数据库调用
  return ids.map(id => users[id]);
};
// userLoader现在是我们的“批量加载功能”
const userLoader = new DataLoader(batchGetUserById);

Это решает проблему пакетной обработки. Для загрузки данных и использования кеша мы будем использоватьloadвызов метода для замены предыдущего поиска данных, передавая наш идентификатор пользователя:

const resolvers = {
  Query: {
    user: function (_, { id }) {
      return userLoader.load(id);
    },
  },
}

Разрешить

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

Чтобы исправить это, нам нужно изменить функцию парсера. Помимо параметров поля, парсер имеет доступ к его родительскому узлу, а также к переданным в спец.контекстЗначения, предоставляющие информацию о текущем аутентифицированном пользователе. Поскольку мы знаем, что адрес является конфиденциальным полем, нам нужно изменить наш код, чтобы вызов пользователя не просто возвращал список адресов, а фактически вызывал некоторую бизнес-логику для проверки запроса:

const getAddresses = function(currUser, user) {
  if (currUser.id == user.id) {
    return user.addresses
  }

  return [];
}

const resolvers = {
  Query: {
    user: function (_, { id }) {
      return users[id];
    },
  },
  User: {
    addresses: function (parentObj, {}, context) {
      return getAddresses(context.currUser, parentObj);
    },
  },
};

Опять же, нам не нужноUserПоля явно определяют Resolver, просто определите Resolver, который мы хотим изменить.

по умолчанию,express-graphqlHTTP-запростак какконтекст

app.use(
  "/graphql",
  express_graphql({
    schema: schema,
    graphiql: true,
    context: {
      currUser: user // 当前经过身份验证的用户
    }
  })
);

Кроме того, вы должны попытаться отделить бизнес-логику от логики парсера. Ваша бизнес-логика должна быть единственным источником истины для всего приложения. Заманчиво выполнять проверки в синтаксическом анализаторе, но по мере роста схемы это становится сложной стратегией для поддержки.

Когда GraphQL неуместен?

Graphql не точно соответствует потребностям отдыха HTTP-общения, как то же самое. Например, запрос будет ли успешным или нет, GraphQL указывает только код состояния -200 OK.在这个响应中会返回一个特殊的错误键,供客户端解析和识别出错的地方,因此,错误处理可能会有些棘手。

Опять же, GraphQL — это просто спецификация, она не решит автоматически все проблемы, с которыми сталкивается ваше приложение. Проблемы с производительностью никуда не денутся, запросы к базе данных не станут быстрее, да и вообще вам нужно переосмыслить все в своем API: авторизацию, логирование, мониторинг, кеширование. Версии вашего GraphQL API также могут быть проблемой, поскольку официальная спецификация в настоящее время не поддерживает обработку критических изменений, что является неизбежной частью создания любого программного обеспечения. Если вы заинтересованы в изучении GraphQL, вам нужно будет потратить некоторое время на изучение того, как наилучшим образом интегрировать его с вашими потребностями.

понять больше

Сообщество собирается вокруг этой новой парадигмы и предоставляет фронтенд- и бэкенд-инженеров.Потрясающий список ресурсов GraphQL. Доступен как фронтенд-, так и бэкенд-инженерам. Вы также можете использоватьофициальная площадкаСделайте реальный запрос, чтобы увидеть, как выглядит запрос и тип.

у нас есть другойКод [иш] Эпизод подкаста, посвященный преимуществам и затратам GraphQL.