Наша библиотека для написания схем в JavaScript hits 1.0

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

Еще в январе 2016 года мы с Йонасом Хелфером искали способы упростить работу с GraphQL, и одна вещь привлекла наше внимание - язык определения схемы:

type Person {
  name: String
  age: Int
  picture: Url
}

Он появлялся повсюду в спецификации, но использовался просто как красивое сокращение. На самом деле вы не могли использовать его для создания сервера GraphQL. Но… что, если бы вы могли? К счастью, пока мы думали об этом, GraphQL.js быстро улучшал свою экспериментальную поддержку языка схемы в парсере. Еще не было достаточно функций для создания полноценного сервера с модульными частями, интерфейсами и многим другим, но наша миссия заключалась в том, чтобы максимально упростить разработку серверов GraphQL. Итак, Йонас построил прототип первой библиотеки GraphQL Apollo, graphql-tools, которая позволяет вам создавать GraphQL API, используя только язык схемы.

graphql-tools 1.0 🎉

Сегодня, через полтора года после начала проекта, мы с Джонасом рады объявить о выпуске 1.0 graphql-tools, с особой благодарностью давнему участнику проекта Hagai Cohen! graphql-tools готов к производству и был протестирован десятками компаний в своих производственных приложениях.

Вот что можно делать с graphql-tools:

  1. Создайте схему с помощью языка определения схем GraphQL, используя при этом все функции GraphQL.js, включая преобразователи, интерфейсы и объединения.
  2. Импортируйте настраиваемые скалярные типы из npm, а также создайте свои собственные
  3. Полностью смоделируйте свой API на основе определения схемы и настройте эти макеты.
  4. Обработка некоторых распространенных ошибок, возникающих при разработке сервера.

В конце вы получаете обычный объект схемы GraphQL.js, точно так же, как и напрямую с эталонной реализацией. Вы можете использовать это с вашим любимым промежуточным программным обеспечением сервера GraphQL, таким как graphql-server-express или express-graphql. Если вы считаете, что чего-то не хватает, сообщите о проблеме или отправьте PR - мы хотим, чтобы graphql-tools был очень легким и непредвзятым, но мы всегда ищем возможные улучшения!

Создание простого сервера GraphQL

Если вы новичок в GraphQL, graphql-tools - отличный способ создать свой первый сервер. В этом посте мы рассмотрим некоторые из самых простых способов использования, в том числе:

  1. Схема приветственного мира (и введение в Launchpad, инструмент для изучения серверов GraphQL).
  2. Простая схема только для чтения с авторами и сообщениями.
  3. Та же схема с добавленной мутацией.

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

Привет, мир с инструментами graphql

Вот как написать схему GraphQL hello world с graphql-tools:

import { makeExecutableSchema } from 'graphql-tools';
// Construct a schema using the GraphQL schema language
const typeDefs = `
  type Query {
    hello: String
  }
`;
// Provide resolver functions for your schema fields
const resolvers = {
  Query: {
    hello: (root, args, context) => {
      return 'Hello world!';
    },
  },
};
// Get a GraphQL.js Schema object
export const schema = makeExecutableSchema({
  typeDefs,
  resolvers,
});

Щелкните здесь, чтобы увидеть живую демонстрацию и отредактировать код в своем браузере!

В этом сообщении в блоге мы собираемся включать в каждый фрагмент кода ссылку на живой пример на Launchpad, новой демонстрационной платформе сервера GraphQL. Войдите в систему и нажмите Fork, чтобы отредактировать код, или Download, чтобы получить приложение Node.js, которое вы можете запускать локально!

Каждая схема GraphQL состоит из двух частей:

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

А теперь давайте сделаем это немного поинтереснее…

Вложенные типы и запросы

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

type Author {
  id: Int!
  firstName: String
  lastName: String
  posts: [Post]
}
type Post {
  id: Int!
  title: String
  author: Author
}
type Query {
  posts: [Post]
  author(id: Int!): Author
}

Вы можете видеть, что у нас здесь отношения как «один-ко-многим», так и «один-к-одному»: у авторов есть несколько сообщений, но у каждого сообщения ровно один автор.

Язык схемы довольно интуитивно понятен - ключевое слово type определяет «тип объекта», который может иметь несколько полей, и вы можете использовать [ ], чтобы указать список, и !, чтобы указать ненулевое поле (все элементы в этой схеме должны иметь id ). Тип Query является особенным, поскольку он определяет набор полей, доступных на первом уровне запроса.

Давайте посмотрим на резолверы здесь:

const resolvers = {
  Query: {
    posts: () => posts,
    author: (_, args) => find(authors, { id: args.id }),
  },
  Author: {
    posts: (author) => filter(posts, { authorId: author.id }),
  },
  Post: {
    author: (post) => find(authors, { id: post.authorId }),
  },
};

Эти преобразователи представляют собой простые функции, и в этом примере они считывают данные из массивов authors и posts прямо в коде, поскольку мы пока не хотим углубляться в выборку данных. Давайте посмотрим на эти фальшивые данные:

const authors = [
  { id: 1, firstName: 'Tom', lastName: 'Coleman' },
  { id: 2, firstName: 'Sashko', lastName: 'Stubailo' },
  { id: 3, firstName: 'Mikhail', lastName: 'Novikov' },
];
const posts = [
  { id: 1, authorId: 1, title: 'Introduction to GraphQL' },
  { id: 2, authorId: 2, title: 'GraphQL Rocks' },
  { id: 3, authorId: 2, title: 'Advanced GraphQL' },
  { id: 4, authorId: 3, title: 'Launchpad is Cool' },
];

Обратите внимание, что нам не нужно указывать преобразователи для каждого поля, GraphQL.js имеет концепцию преобразователей по умолчанию, что означает, что он будет подбирать поля firstName, lastName и title на объектах за нас. Давайте проверим работу, выполнив запрос ниже в GraphiQL на Launchpad:

{
  posts {
    title
    author {
      firstName
    }
  }
}

У нас есть отличные результаты! Мы также можем получить с другой точки зрения, начиная с автора:

{
  author(id: 2) {
    firstName
    posts {
      title
    }
  }
}

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

Добавление мутации

Пока мы работали только с данными, доступными только для чтения. Что, если мы хотим обновить и данные? В GraphQL для этого есть концепция, называемая «мутации». Подобно тому, как запросы используются для получения данных, мутации используются для обновления чего-либо, а затем получения новых результатов.

Во-первых, давайте добавим в нашу схему новый тип:

type Mutation {
  addPost(authorId: Int!, title: String!): Post
}

Теперь все, что нам нужно сделать, это добавить преобразователь для этого поля:

const resolvers = {
  // ... existing resolvers
  Mutation: {
    addPost: (_, args) => {
      const post = {
        id: posts.length,
        authorId: args.authorId,
        title: args.title,
      };
      posts.push(post);
      
      return post;
    }
  }
};

После того, как вы закончите, он должен выглядеть вот так. Теперь мы можем запустить мутацию в Graph i QL:

mutation AddPost {
  addPost(authorId: 1, title: "Added via launchpad") {
    id
    title
    author {
      firstName
    }
  }
}

Обратите внимание, что при мутации GraphQL мы можем извлекать столько данных, сколько захотим. Затем, когда мы запросим все сообщения, мы получим новое сообщение, которое мы только что добавили в список! Даже не вспотел, и мы уже можем увидеть часть работы, которую GraphQL делает за нас, извлекая связанные данные после мутации. Чтобы убедиться, что вы следовали инструкциям, просмотрите полный пример.

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

Теперь, когда у вас есть базовая установка с несколькими типами, вы можете разветвить проект и продолжить работу внутри Launchpad или нажать «Загрузить», чтобы отредактировать код локально.

Если вы так же взволнованы развитием технологии GraphQL и предоставлением возможностей разработчикам, как и мы, присоединяйтесь к нам! Мы активно нанимаем сотрудников с открытым исходным кодом, backend и другие роли для работы над технологией API следующего поколения.