Часть 1 - интерфейс: декларативная выборка данных и имитация с помощью Apollo

В этом руководстве используется старая версия Apollo Client, и в ближайшее время мы работаем над ее обновлением. Чтобы узнать больше о новом API конструктора, ознакомьтесь с этим руководством по началу работы. Большая часть этого руководства будет идентична и может быть завершена с новым API.

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

Однако, несмотря на огромные преимущества использования GraphQL, первый шаг может быть немного сложным. Вот почему я начал писать серию руководств, которые шаг за шагом проведут вас через создание полнофункционального приложения React с GraphQL и Apollo Client. Эта серия проведет вас через весь процесс создания приложения для обмена мгновенными сообщениями, которое использует GraphQL повсюду:

Этот учебник - первый в серии - посвящен началу работы с GraphQL во внешнем интерфейсе. Это займет около 20–30 минут, и к концу у вас будет очень простой пользовательский интерфейс React UI, который загружает свои данные с помощью GraphQL и выглядит примерно так:

Давайте начнем!

1. Подготовка к настройке

Примечание. Для работы с этим руководством вам потребуется установить на вашем компьютере node, npm и git, а также немного знать о React.

Мы собираемся использовать create-react-app в этом руководстве, поэтому установите следующее:

> npm install -g create-react-app

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

> git clone https://github.com/apollographql/graphql-tutorial.git
> cd graphql-tutorial

Затем мы создаем наше приложение для реагирования с create-react-app.

> create-react-app client
> cd client

Чтобы убедиться, что он работает, запустим наш сервер:

> npm start

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

2. Написание первого компонента

Поскольку здесь мы создаем приложение с Apollo, давайте изменим логотип и CSS, скопировав logo.svg и App.css из ../resources

> cd src
> cp ../../resources/* .

Чтобы сделать это начальное руководство кратким, сегодня мы создадим только простой список. Давайте изменим несколько вещей в App.js:

  1. Измените «Добро пожаловать в React» на «Добро пожаловать в Apollo». Apollo - это имя клиента GraphQL, который мы будем использовать в этой серии руководств.
  2. Удалите абзац «Для начала ...» и замените его чистым компонентом React, который отображает неупорядоченный список <ul> с двумя элементами списка <li>, «Канал 1» и «Канал 2» (да, как вы уже догадались, мы собираемся создать приложение для обмена сообщениями!). Назовем наш компонент списка ChannelsList.

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

import React, { Component } from 'react';
 import logo from './logo.svg';
 import './App.css';
const ChannelsList = () =>
     (<ul>
       <li>Channel 1</li>
       <li>Channel 2</li>
     </ul>);
class App extends Component {
   render() {
     return (
       <div className="App">
         <div className="App-header">
           <img src={logo} className="App-logo" alt="logo" />
           <h2>Welcome to Apollo</h2>
         </div>
         <ChannelsList />
       </div>
     );
   }
 }
export default App;

create-react-app устанавливает для вас горячую перезагрузку, поэтому, как только вы сохраните файл, окно браузера с вашим приложением должно обновиться, чтобы отразить изменения:

3. Написание схемы GraphQL

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

export const typeDefs = `
type Channel {
   id: ID!                # "!" denotes a required field
   name: String
}
# This type specifies the entry points into our API. In this case
# there is only one - "channels" - which returns a list of channels.
type Query {
   channels: [Channel]    # "[]" means this is a list of channels
}
`;

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

query ChannelsListQuery {
  channels {
    id
    name
  }
}

4. Связывание вашего компонента с запросом GraphQL.

Хорошо, теперь, когда у нас есть схема и запрос, нам просто нужно подключить наш компонент к Apollo Client! Давайте установим Apollo Client и несколько вспомогательных пакетов, которые нам понадобятся для добавления GraphQL в наше приложение:

> npm i -S react-apollo

react-apollo - это аккуратная интеграция Apollo Client с React, которая позволяет вам украсить ваши компоненты компонентом более высокого порядка под названием graphql, чтобы получить ваши данные GraphQL в компонент с нулевыми усилиями. React Apollo также поставляется с ApolloClient, который является ядром Apollo, который обрабатывает все операции по извлечению, кэшированию и оптимистическим обновлениям данных (мы поговорим об этом в другом руководстве).

Теперь давайте добавим несколько операций импорта в верхнюю часть нашего App.js и создадим экземпляр Apollo Client:

import {
  ApolloClient,
  gql,
  graphql,
  ApolloProvider,
} from 'react-apollo';
const client = new ApolloClient();

Затем мы украшаем исходный ChannelsList компонентом высшего порядка GraphQL, который принимает запрос и передает данные нашему компоненту:

const channelsListQuery = gql`
   query ChannelsListQuery {
     channels {
       id
       name
     }
   }
 `;
const ChannelsListWithData = graphql(channelsListQuery)(ChannelsList);

Если обернуть graphql HOC, наш ChannelsList компонент получит свойство с именем data, которое будет содержать channels, если оно доступно, или error, если возникнет ошибка. Вдобавок data также содержит свойство loading, которое равно true, когда Apollo Client все еще ожидает получения данных.

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

const ChannelsList = ({ data: {loading, error, channels }}) => {
   if (loading) {
     return <p>Loading ...</p>;
   }
   if (error) {
     return <p>{error.message}</p>;
   }
   return <ul>
     { channels.map( ch => <li key={ch.id}>{ch.name}</li> ) }
   </ul>;
 };

Наконец, мы должны заменить ChannelsList внутри функции рендеринга нашего приложения на ChannelsListWithData. Чтобы сделать экземпляр Apollo Client доступным для только что созданного компонента, мы также оборачиваем наш компонент приложения верхнего уровня с помощью ApolloProvider, который помещает экземпляр клиента в пользовательский интерфейс.

Ваш App компонент теперь должен выглядеть так:

class App extends Component {
   render() {
     return (
       <ApolloProvider client={client}>
         <div className="App">
           <div className="App-header">
             <img src={logo} className="App-logo" alt="logo" />
             <h2>Welcome to Apollo</h2>
           </div>
           <ChannelsListWithData />
         </div>
       </ApolloProvider>
     );
   }
 }

Хорошо, мы почти закончили! Если вы попытаетесь запустить это сейчас, вы должны увидеть следующую ошибку:

В чем дело? Что ж, мы правильно подключили все наши компоненты, но мы еще не написали сервер, поэтому, конечно, нет данных для выборки или отображения! Если вы не укажете URL-адрес для своей конечной точки GraphQL, Apollo Client будет считать, что он работает в том же источнике под /graphql. Чтобы это изменить, нам нужно создать сетевой интерфейс с настраиваемым URL-адресом.

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

npm i -S graphql-tools apollo-test-utils graphql

Мы будем использовать эти пакеты для создания имитационного сетевого интерфейса для Apollo Client на основе схемы, которую мы написали ранее. Добавьте следующие элементы импорта и определения в начало App.js:

import { 
  makeExecutableSchema,
  addMockFunctionsToSchema
} from 'graphql-tools';
 import { mockNetworkInterfaceWithSchema } from 'apollo-test-utils';
 import { typeDefs } from './schema';
const schema = makeExecutableSchema({ typeDefs });
addMockFunctionsToSchema({ schema });
const mockNetworkInterface = mockNetworkInterfaceWithSchema({ schema });

Теперь все, что вам нужно сделать, это передать mockNetworkInterface конструктору Apollo Client…

const client = new ApolloClient({
   networkInterface: mockNetworkInterface,
 });

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

Примечание. Hello World - это просто текст по умолчанию, используемый для строк. Если вы хотите сделать свои макеты супер-модными, прочтите этот пост, который я написал некоторое время назад.

Если что-то не работает, и вы не можете понять почему, вы можете сравнить это с этим файлом, чтобы увидеть, что вы сделали по-другому. Кроме того, вы можете заглянуть в t1-end ветку Git, чтобы проверить рабочий код.

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

Если вам понравился этот учебник и вы хотите продолжить изучение Apollo и GraphQL, не забудьте нажать кнопку Follow ниже и подписаться на нас в Twitter по адресу @apollographql и @helferjs.