Apollo Server
Apollo Server - открытый соответствующий спецификации
GraphQL-сервер
, совместимый с любымGraphQL-клиентом
, включая Apollo Client. Это лучший способ создания готового к продакшну, самодокументируемогоGraphQL API
, который может использовать данные из любого источника.
Начало работы
Создание проекта
- Создаем директорию для проекта и заходим в нее:
mkdir graphql-server-example
cd !$
- Инициализируем Node.js-проект:
yarn init -y
# or
npm init -y
Установка зависимостей
Для Сервера требуется 2 зависимости:
apollo-server
- библиотека, позволяющая определять форму данных (data shape) и способы их полученияgraphql
- библиотека для создания GraphQL-схемы и выполнения запросов
yarn add apollo-server graphql
# or
npm i ...
Создаем файл index.js
:
touch index.js
Определение схемы
Каждый сервер GraphQL
(включая Apollo Server
) нуждается в схеме, определяющей структуру данных, которые могут запрашиваться клиентом. В следующем примере мы получаем коллекцию книг по названию и автору:
const { ApolloServer, gql } = require('apollo-server')
// схема - это коллекция типов (`typeDefs`),
// которые определяют "форму" выполняемых запросов
const typeDefs = gql`
# Пример комментария
# Тип "Book" определяет поля, которые можно получить для книги
type Book {
title: String
author: String
}
# Тип "Query" является особым: в нем указываются все запросы, которые
# могут выполняться клиентом, а также тип, возвращаемый запросом. В данном случае
# запрос "books" возвращает массив из 0 или более книг
type Query {
books: [Book]
}
`
Определение набора данных
После определения структуры мы можем определить данные. Сервер может получать данные из любого источника (база данных, REST API
, статический объект - хранилище или даже другой GraphQL-сервер).
Определяем данные в index.js
:
const books = [
{
title: 'The Awakening',
author: 'Kate Chopin'
},
{
title: 'City of Glass',
author: 'Paul Auster'
}
]
Обратите внимание, что оба объекта в массиве соответствуют типу Book
, определенному в схеме.
Определение резолвера
Резолверы сообщают Серверу, как получать данные того или иного типа. Создаем резолвер в index.js
:
// Резолверы определяют способ получения типов, определенных в схеме.
// Данный резолвер извлекает книги из соответствующего массива
const resolvers = {
Query: {
books: () => books,
}
}
Создание экземпляра Сервера
Схема, данные и резолвер должны быть переданы Серверу.
Создаем экземпляр Сервера в index.js
:
// В конструктор Сервера передается 2 аргумента:
// схема и набор резолверов
const server = new ApolloServer({ typeDefs, resolvers })
// Запускаем сервер
server.listen().then(({ url }) => {
console.log(`🚀 Сервер запущен по адресу: ${url}`)
})
Запуск сервера
Все готово к запуску сервера. Выполняем команду:
node index.js
Получаем сообщение:
🚀 Сервер запущен по адресу: http://localhost:4000/
Выполнение запроса
Для выполнения запросов можно использовать ApolloSandbox
.
Переходим по адресу http://localhost:4000
и нажимаем на кнопку Query your server
.
Интерфейс песочницы включает в себя следующее:
- панель операций (Operations) для создания и выполнения запросов (посередине)
- панель ответа (Response) для отображения результатов запросов (справа)
- вкладку для изучения, поиска и настроек схемы (слева)
URL
для подключения к другим серверам (наверху слева)
Запрос на получение книг может выглядеть так:
query GetBooks {
books {
title
author
}
}
Вставляем данную строку в панель операций и нажимаем синюю кнопку наверху справа. Результат запроса отображается в панели ответа.
Одной из ключевых особенностей GraphQL
является то, что клиент может запрашивать только те данные, которые ему нужны. Удалите author
из запроса и выполните его повторно. Вы увидите, что ответ теперь содержит только названия книг.
Основы работы со схемой
Сервер использует схему для описания формы доступных данных. Схема определяет иерархию типов с полями, которые наполняются (populate) данными из серверного хранилища. Она также определяет, какие запросы и мутации могут выполняться клиентом.
Язык для определения схемы
Спецификация GraphQL
определяет высокоуровневый язык определения схемы (schema definition language, SDL), который используется для определения схемы и ее хранения в виде строки.
Пример определения 2 объектных типов:
type Book {
title: String
author: Author
}
type Author {
name: String
books: [Book]
}
Схема определяет коллекцию типов и отношения между ними. В приведенном примере Book
имеет связанного с ней author
, а Author
- список book
.
Обратите внимание: структура схемы не зависит от реализации.
Определение полей
Как правило, типы им еют одно или более поле:
# Тип Book имеет 2 поля: `title` и `author`
type Book {
title: String # возвращает `String`
author: Author # возвращает `Author`
}
Каждое поле возвращает данные определенного типа. Возвращаемым типом может быть scalar
, object
, enum
, union
или interface
(⬇).
Списки
Поле может возвращать список, содержащий элементы определенного типа. Индикатором списка являются квадратные скобки ([]
):
type Author {
name: String
books: [Book] # Список `Books`
}
Поля с нулевым значением
По умолчанию поле может возвращать null
вместо определенного типа. Указание !
после типа делает поле ненулевым - это означает, что такое поле не может возвращать null
:
type Author {
name: String! # Не может возвращать `null`
books: [Book]
}
При попытке сервера вернуть null
для ненулевого поля будет выброшено исключение.
Нулевые значения и списки