Интеграция GraphQL API в Angular приложение

Aleksandr Serenko
F.A.F.N.U.R
Published in
5 min readJan 3, 2020

В данной статье рассмотрим интеграцию graphql API в Angular приложение. Для разработанного ранее API сверстаем несколько страничек и отобразим загруженные данные.

Подолжим копировать сайт Lamborghini, и в данной статье реализуем раздел Событий (Мероприятий).

Установка зависимостей

Для настройки и подключения к серверу Apollo, можно использовать документацию — подключение Apollo.

Добавим apollo-angular:

ng add apollo-angular

Добавим в enviroments путь до apollo sever:

...
graphql: {
uri: 'http://127.0.0.1:3333/graphql'
},
...

Также добавим ряд библиотек, для работы с данными Apollo, часть из которых могли быть установлена ранее:

yarn add apollo-angular  apollo-angular-link-http apollo-cache-inmemory apollo-client apollo-link graphql-tag graphql-tag-pluck graphql-tools graphql-type-json

В core.module добавим полключение ApolloModule:

import { registerLocaleData } from '@angular/common';
import localeRu from '@angular/common/locales/ru';
import localeEn from '@angular/common/locales/en';
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { TranslateModule } from '@ngx-translate/core';
import { APOLLO_OPTIONS, ApolloModule } from 'apollo-angular';
import { HttpLink, HttpLinkModule } from 'apollo-angular-link-http';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { NxModule } from '@nrwl/angular';

import { API_SOURCES, APP_DIST } from '@medium-stories/common';
import { FOOTER_GROUPS_LINKS, FOOTER_NAV_LINKS, LayoutsCoreModule, NAV_LINKS } from '@medium-stories/layouts';
import { RESPONSIVE_SIZE_DEFAULT, ResponsiveModule } from '@medium-stories/responsive';
import { RootStoreModule } from '@medium-stories/store';

import { environment } from '../../environments/environment';
import { coreContainers, coreFooterGroupsLinks, coreFooterNavLinks, coreNavLinks, coreRoutes } from './core.common';

registerLocaleData(localeRu);
registerLocaleData(localeEn);

export function createApollo(httpLink: HttpLink) {
return {
link: httpLink.create({ uri: environment.graphql.uri }),
cache: new InMemoryCache()
};
}

@NgModule({
imports: [
NxModule.forRoot(),
LayoutsCoreModule,
ResponsiveModule.forRoot({
mode: {
mobile: 'lg',
sizes: RESPONSIVE_SIZE_DEFAULT
}
}),
RouterModule.forRoot(coreRoutes, { initialNavigation: 'enabled', scrollPositionRestoration: 'enabled' }),
RootStoreModule,
TranslateModule
],
declarations: [...coreContainers],
providers: [
{
provide: NAV_LINKS,
useValue: coreNavLinks
},
{
provide: FOOTER_NAV_LINKS,
useValue: coreFooterNavLinks
},
{
provide: FOOTER_GROUPS_LINKS,
useValue: coreFooterGroupsLinks
},
{
provide: APOLLO_OPTIONS,
useFactory: createApollo,
deps: [HttpLink]
},
{
provide: APP_DIST,
useValue: 'frontend/graphql'
},
{
provide: API_SOURCES, // need to set path's for sources
useValue: environment.api.sources
}
],
exports: [ApolloModule, HttpLinkModule]
})
export class CoreModule {}

Создание events lib

Так как статья является частью серии статьёй, посвящённых Angular и Nx, продолжим проект — medium-stories. Клонируем последние приложение, назвав его graphql.

Под клонированием, мы подразумеваем копирование и переименовывание папки markup в grapgql, добавление конфига в angular.json, nx.json. Результат можно найти в репозитирии, отдельным коммитом

Создадим библиотеку events, которая будет содержать:

  • state для работы с сущностями Event
  • event apollo — который будет предоставлять доступ к серверу Apollo
  • components, conteiners для отображения списка, одного события

Сгенерируем новую библиотеку

ng g lib events

Создадим базовый модуль (core), который будет содержать основные сервисы:

ng g module events-core --project=events

Опишем интерфейсы для работы с Events:

Создадим основные запросы:

Создадим новый state:

ng g @nrwl/angular:ngrx event --module=libs/events/src/lib/events-core/events-core.module.ts

Создадим необходимые action’ы, selector’ы, effect’ы и facade:

Как можно увидеть, в списке экшенов 3 основных действия:

  • Загрузка всего списка событий (мероприятий)
  • Загрузка последнего события (мероприятия)
  • Загрузка события по Id

Согласно описанному подходу организации State’ов, в — статье, создадим селекторы и эффекты.

Добавим имплементацию сервиса работы с Apollo сервером:

Как можно увидеть из реализации, работа с Apollo сервисом не отличается с работой стандартного Http сервиса.

В частности, каждый запрос будет ожидать в ответ Observable, который может быть как запрашиваемыми данными, так и ошибкой. Для того, чтобы разграничить error’ы, явно добавляем catchError, по аналогии с обычным http запросом:

// base-event-apollo.service.ts... 
loadEvent(id: number, queryParams: object = {}): ApolloResponse<Event> {
return this.apollo
.query<{ event: Event }>({ query: eventRequests.eventRequest.query, variables: { id } })
.pipe(
map(result => extractApolloResponse(result, eventRequests.eventRequest.keys)),
catchError((error: ApolloError) => throwError(error))
);
}
...

Так же отметим, что в данном примере используется — объект eventRequest, который состоит из двух свойств:

/**
* Apollo request
*/
export interface ApolloRequest {
/**
* Request entities
*/
keys: string[];

/**
* Request query
*/
query: DocumentNode;
}

Query — это graqhql запрос, который возвращает данные. Так как запрос может вернуть несколько сущностей, в свойстве keys, мы можем ограничить возвращаемые данные.

Подключим все в модуле:

Создание компонетов

Основная логика с загрузкой событий сделана, перейдём к разработке компонентов вывода.

Создадим модуль events в библиотеке events:

ng g module events --project=events

Создадим две папки components, containers.

Создадим компонент events, для вывода списка событий:

ng g component events --project=events --skip-import

Добавим реализацию:

Добавим компонент для вывода последнего события:

ng g component event-last --project=events --skip-import

Добавим логику:

Добавим компонент для вывода карточки события:

ng g component event-box --project=events --skip-import

Добавим отображение карточки:

Добавим компонент для вывода страницы события:

ng g component event --project=events --skip-import

Добавим отображение карточки:

Создадим модуль и подключаем ранее разработанные компоненты:

Отметим, что есть часть компонентов, которые не вошли в обзор статьи (breadcrumbs, localizedDate и др), которые нужны для максимального совпадения с источником, но не являются необходимой частью. Все используемые компоненты есть в репозитории.

Создадим lazy load module в нашем приложение и подключим библиотеку events и выведем наши компоненты:

Как видно в events.common.ts, мы обернули роуты из библиотеке events, в BaseLayoutComponent, сохранив при этом, структуру родительского роутинга.

Напоследок, немного поправим API, исправив EventResolver:

Если изучить код, можно увдитеть следующее:

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

Тестирование интеграции API

Запустим сервер базы данных:

yarn run backend:db:start

Запустим backend-api сервер:

yarn run serve:backend:api

Запустим дев сервер angular приложения:

yarn run serve:graphql

Откроем браузер по ссылке

http://localhost:4200/events

Заключение

В ходе данной статьи было сделано:

  • Создана библиотека для отображения событий (мероприятий)
  • Созданы классы для работы с Apollo сервером
  • Разработан state для событий

В следующей статье поговорим о dynamic forms.

Спасибо за внимание!

Исходники

Все исходники находятся на github, в репозитории https://github.com/Fafnur/medium-stories

Для того, чтобы посмотреть состояние проекта на момент написания статьи, нужно выбрать соответвующий tag — events.

git checkout events

Предыдущие статьи:

  1. Статья про Redux в Angular с помощью Ngrx. Создание Store в Angular
  2. Организация Stat’ов в Angular c Ngrx и Nx
  3. GraphQL API для Angular с помощью NX и Nest.

--

--

Aleksandr Serenko
F.A.F.N.U.R

Senior Front-end Developer, Angular evangelist, Nx apologist, NodeJS warlock