X

Руководство по GraphQL для начинающих

Аббревиатура API означает Application Programming Interface (программный интерфейс приложения). Это, как следует из названия, интерфейс, с помощью которого взаимодействуют стороны обмена.

В этой статье вы познакомитесь с основами разработки API с применением стандарта GraphQL.

Что такое GraphQL

GraphQL — язык запросов с открытым исходным кодом, разработанный Facebook. Он создавался как более эффективная альтернатива REST для разработки и использования программных интерфейсов приложений.

GraphQL обладает множеством достоинств, например:

  1. Вы получаете информацию именно в том объёме, в котором запрашиваете. В отличие от REST, ответ на запрос не будет содержать ненужных данных.
  2. Вам будет необходима всего одна конечная точка, никаких дополнительных версий для единственного API.
  3. GraphQL — сильно типизированный язык, что позволяет предварительно оценить корректность запроса в рамках системы типов этого синтаксиса, до исполнения. Это позволяет разрабатывать более мощные API.

С чего начать

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

Для начала нужно создать новую папку для проекта. В данном случае имя папки будет graphql-server, однако название не принципиально.

Откройте терминал и введите:

mkdir graphql-server

На вашем компьютере должен быть установлен npm или yarn.

Перейдите в созданную вами папку, если вы ещё этого не сделали и введите команду в зависимости от используемого менеджера:

npm init -y

или

yarn init 

npm создаст файл package.json, в котором будут храниться все созданные вами зависимости и команды.

Теперь необходимо установить одну зависимость, которую мы будем использовать в рамках этой статьи.

Graphpack позволяет создать сервер GraphQL с базовой конфигурацией. Эта функция позволит продолжить работу, не тратя времени на настройку.

Используя терминал, в корневой папке проекта установите Graphpack с помощью следующей команды:

npm install --save-dev graphpack

Или, если вы используете yarn:

yarn add --dev graphpack

После установки Graphpack перейдите к файлу package.json и добавьте следующий код:

"scripts": {
    "dev": "graphpack",
    "build": "graphpack build"
}

Создайте на сервере папку src. В данном примере это будет единственная папка на сервере, в которой необходимо будет создать три файла.

В папке src создайте файл schema.graphql. В этот файл добавьте код:

type Query {
  hello: String
}

В этом файле будет находиться вся схема GraphQL.

Создайте второй файл в той же папке, назовите его resolvers.js. Разместите там следующий код:

import { users } from "./db";

const resolvers = {
  Query: {
    hello: () => "Hello World!"
  }
};

export default resolvers;

В этом файле будут размещены инструкции по выполнению операций GraphQL.

Создайте третий файл, db.js, содержащий код:

export let users = [
  { id: 1, name: "John Doe", email: "john@gmail.com", age: 22 },
  { id: 2, name: "Jane Doe", email: "jane@gmail.com", age: 23 }
];

Для обучения работе с GraphQL нет нужды использовать настоящие данные. Этот файл нужен для симуляции обращений к базе данных.

После выполнения операций папка src должна выглядеть следующим образом:

src
  |--db.js
  |--resolvers.js
  |--schema.graphql

Теперь необходимо выполнить команду npm run dev для npm или yarn dev для yarn. Терминал должен вывести информацию об успешном запуске сервера:

Теперь можно перейти к localhost:4000. Система готова к работе над API на основе GraphQL. Для разработки используется IDE GraphQL Playground.

Схема

Для создания схем в GraphQL используется собственный язык Schema Definition Language (SDL). SDL обладает интуитивно понятным синтаксисом и универсален для любой используемой технологии.

Типы

Типы — одна из основных особенностей GraphQL. Это кастомные объекты, которые определяют, как будет выглядеть API. Например, при разработке программного интерфейса для приложения, взаимодействующего с соцсетями, в API стоит объявить типы PostsUsersLikesGroups.

В типах есть поля, возвращающие определённые разновидности данных. Например, при создании типа User, в него стоит включить поля nameemail, и age. Поля типов могут быть любыми и всегда возвращают данные в формате Int, Float, String, Boolean, ID, List of Object Types, или Custom Objects Types.

Чтобы создать первый тип, откройте файл schema.graphql и замените ранее прописанный там тип Query следующим кодом:

type User {
  id: ID!
  name: String!
  email: String!
  age: Int
}

Каждая запись типа User должна иметь идентификационный номер, поэтому поле id содержит данные соответствующего типа. Поля name и email содержат String (переменную типа строки символов), а age — целочисленную переменную.

Восклицательный знак в конце определения поля означает, что это поле не может быть пустым. Единственное поле без восклицательного знака — age.

GraphQL оперирует тремя основными концепциями:

  1. queriesзапросы — с их помощью получают данные с сервера.
  2. mutations, изменения — модификация данных на сервере и их обновление.
  3. subscriptions , подписки — методы поддержания постоянной связи с сервером.

Запросы

Откройте файл schema.graphql и добавьте тип Query:

type Query {
  users: [User!]!
}

Запрос users будет возвращать массив из одной и более записей типа User. Поскольку в определении использованы восклицательные знаки, ответ на запрос не может быть пустым.

Для получения конкретной записи User необходимо создать соответствующий запрос. В данном случает это будет запрос user в типе Query. Добавьте в код следующую строку:

user(id: ID!): User!

Теперь код должен выглядеть так:

type Query {
  users: [User!]!
  user(id: ID!): User!
}

Как видите, в запросах GraphQL можно передавать аргументы. В данном случае для получения конкретной записи в запросе в качестве аргумента используется её поле id.

Местонахождение данных, которые будут обрабатываться в соответствии с запросом, определяется в файле resolvers.js. Откройте этот файл и импортируйте учебную базу данных db.js:

import { users } from "./db";

const resolvers = {
  Query: {
    hello: () => "Hello World!"
  }
};

export default resolvers;

Затем замените функцию hello на user и users:

import { users } from "./db";

const resolvers = {
  Query: {
    user: (parent, { id }, context, info) => {
      return users.find(user => user.id === id);
    },
    users: (parent, args, context, info) => {
      return users;
    }
  }
};

export default resolvers;

В каждом резолвере запроса есть четыре аргумента. В запросе user в качестве аргумента передаётся содержимое поля id записи базы данных. Сервер возвращает содержимое подходящей записи. Запрос users не содержит аргументов и всегда возвращает весь массив целиком.

Для тестирования получившегося кода перейдите к localhost:4000.

Следующий код должен вернуть список всех записей db.js:

query {
  users {
    id
    name
    email
    age
  }
}

Получить первую запись из базы можно с помощью этого кода:

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

Изменения

В GraphQL изменения — способ модифицировать данные на сервере и получить обработанную информацию. Этот процесс можно рассматривать как аналогичный концепции CUD (Create, Update, Delete) в стандарте REST.

Для создания изменения откройте файл schema.graphqlи добавьте новый тип mutation:

type Mutation {
  createUser(id: ID!, name: String!, email: String!, age: Int): User!
  updateUser(id: ID!, name: String, email: String, age: Int): User!
  deleteUser(id: ID!): User!
}

В данном случае указано три различных изменения:

  • createUser: необходимо передать значение полей id, name, email и age. Функция возвращает запись типа User.
  • updateUser: необходимо передать значение поля id, новое значение поля name, email или age. Функция возвращает запись типа User.
  • deleteUser: необходимо передать значение поля id. Функция возвращает запись типа User.

Теперь откройте файл resolvers.js и ниже объекта Query создайте новый объект mutation:

Mutation: {
    createUser: (parent, { id, name, email, age }, context, info) => {
      const newUser = { id, name, email, age };

      users.push(newUser);

      return newUser;
    },
    updateUser: (parent, { id, name, email, age }, context, info) => {
      let newUser = users.find(user => user.id === id);

      newUser.name = name;
      newUser.email = email;
      newUser.age = age;

      return newUser;
    },
    deleteUser: (parent, { id }, context, info) => {
      const userIndex = users.findIndex(user => user.id === id);

      if (userIndex === -1) throw new Error("User not found.");

      const deletedUsers = users.splice(userIndex, 1);

      return deletedUsers[0];
    }
  }

Полный код файла resolvers.js должен выглядеть так:

import { users } from "./db";

const resolvers = {
  Query: {
    user: (parent, { id }, context, info) => {
      return users.find(user => user.id === id);
    },
    users: (parent, args, context, info) => {
      return users;
    }
  },
  Mutation: {
    createUser: (parent, { id, name, email, age }, context, info) => {
      const newUser = { id, name, email, age };

      users.push(newUser);

      return newUser;
    },
    updateUser: (parent, { id, name, email, age }, context, info) => {
      let newUser = users.find(user => user.id === id);

      newUser.name = name;
      newUser.email = email;
      newUser.age = age;

      return newUser;
    },
    deleteUser: (parent, { id }, context, info) => {
      const userIndex = users.findIndex(user => user.id === id);

      if (userIndex === -1) throw new Error("User not found.");

      const deletedUsers = users.splice(userIndex, 1);

      return deletedUsers[0];
    }
  }
};

export default resolvers;

Сделайте запрос к localhost:4000:

mutation {
  createUser(id: 3, name: "Robert", email: "robert@gmail.com", age: 21) {
    id
    name
    email
    age
  }
}

Он должен вернуть новую запись типа User. Опробуйте также остальные функции изменения.

Подписки

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

subscription {
  users {
    id
    name
    email
    age
  }
}

Несмотря на то что этот код выглядит похожим на запрос, работает он несколько иначе. При обновлении данных сервер выполняет определённый в подписке запрос GraphQL и рассылает обновлённые данные клиентам. Более подробно о работе с подписками можно почитать в этом материале.

Заключение

Стандарт GarphQL набирает популярность. В рамках опроса State of JavaScript, проведённого среди JS-разработчиков, более половины респондентов указали, что слышали об этой технологии и хотели бы с ней ознакомиться, а пятая часть уже её использует и не намерена отказываться. Если верить этому тренду, GraphQL ожидает активное развитие. Надеемся, что данный материал дал вам общее представление о GraphQL и заинтересовал в дальнейшем его изучении.

Перевод статьи A Beginner’s Guide to GraphQL

Хинт для программистов: если зарегистрироваться на соревнования Huawei Honor Cup, бесплатно получите доступ к онлайн-школе для участников. Можно прокачаться по разным навыкам и выиграть призы в самом соревновании. Перейти к регистрации.

Также рекомендуем:

Рубрика: Переводы