AWS AppSync +  ApolloではじめるGraphQL

この記事は [Angular Advent Calendar 2018](https://qiita.com/advent-calendar/2018/angular) の 22 日目の記事です。

# 概要
* AWS AppSyncを使って画面にデータを表示してみたい方
* フロントエンドエンジニアでも簡単にGraphQLを触ってみたい方
* GraphQLを導入するか迷っている方向けの記事

# RESTがつらい


**（例）AWS構成図**
![aws.png](https://qiita-image-store.s3.amazonaws.com/0/54858/c4f8096e-c228-540b-9cf2-56a3480b7945.png)



上の図のようにAngular + AWSを使い
REST APIから返ってきた情報を表示するサービスを運用しています。
ただ、サービスの要件が増えるにつれて画面で取得したい情報など多くなるもので

* 複数のAPIを呼び出さないと欲しい情報が取れない
* レスポンス内の一部のデータしか利用しないのに、余分なデータも返される
* APIのリクエスト数やRTTの増加

など色々つらい部分が多くなってきました…。

## なぜGraphQL？
じゃあこの問題をどう解決するか？

* 欲しい情報だけ返す新しいAPIを増やす
* 既存のAPIにfieldsパラメータなどを実装する

など方法は色々あると思うのですが

GraphQLの場合、スキーマ設定次第で

* 複数のサービスからシンプルに無駄なく欲しい情報を取得できる
* リポジトリに対してクエリを生やすことでデータの取得が可能になるため、既存のプロジェクトに影響を与えず小さく導入できそう

という点で魅力的に思えたので、
今回、GraphQLのサービスである [AWS AppSync](https://aws.amazon.com/jp/appsync/) を使い
Angular + Apolloを利用してデータを実装してみたので、その情報を共有していきたいと思います。

# AWS + Angularで簡単にGraphQLをはじめるには？

## おおまかな流れ
1. [こちら](https://dev.classmethod.jp/cloud/aws/appsync-try-sample-schema/)を参考にしながらAWS AppSyncを作成 （構築の手順は省略させていただきます。）
2. Apollo + AWS AppSync JavaScript SDK のインストール
3. Angularで実装

AWS構成図のイメージはこんな感じです。
![appsync.png](https://qiita-image-store.s3.amazonaws.com/0/54858/376aa50b-56fd-0ff7-9884-d21dfa5f3f0b.png)

## 完成コード
今回の完成コードはこちらに置いてあります。
https://github.com/hiroyuki-nishi/apollo-angular

### 今回利用するサービスの説明
**AWS AppSync（バックエンド）**
![appsync (1).png](https://qiita-image-store.s3.amazonaws.com/0/54858/ffd0181c-550d-3351-db82-e5273f953d47.png)


AWS AppSyncは、AWSが提供しているフルマネージドなサーバーレスなGraphQLサービスです。
他のAWS リソースとの連携が容易であったり、DynamoDB をデータソースとすると、GraphQLのスキーマが自動生成や、AppSyncのコンソール上で結果を確認することができます。

**Apollo（フロントエンド）**
![apollo-logo.png](https://qiita-image-store.s3.amazonaws.com/0/54858/5afd7a78-05ee-f3a5-edea-b3728324b686.png)

[Apollo](https://www.apollographql.com/)は、[Meteor Development Group]()が開発している
バックエンド　&& フロントエンドでGraphQLが使えるライブラリです。

Angular、React、Vue、Meteor、Ember、Polymerなどのフレームワークなどに対応しており
今回はApolloを使ってGraphQLのクライアントサイドを実装してみたいと思います。

## まずはインストール！

AWS AppSyncが無事作成できましたら、Angularの実装を行なっていきます。
まずは、新しいプロジェクトを作成しApolloと[AWS AppSyncSDK](https://github.com/awslabs/aws-mobile-appsync-sdk-js)を追加します。

```
$ ng new apollo-in-angular
$ ng add apollo-angular
$ npm install --save aws-appsync
```

[Angular 6.x以降](https://aws-amplify.github.io/docs/js/ionic#angular-6-support)でAWS AppSyncSDKを動作させるために、polyfills.tsに以下を追加します。

```.ts
// polyfills.ts
(window as any).global = window;
```

## Angularからデータを取得

次に、AWS AppSyncからデータを取得するサービスを作成します。
ここでは主に、AWS AppSyncのエンドポイントの設定を行なっています。

```.ts
//graphql.service.ts
import AWSAppSyncClient from 'aws-appsync';
import { AUTH_TYPE } from 'aws-appsync/lib';
import { Apollo } from 'apollo-angular';
import { Injectable } from '@angular/core';


@Injectable()
export class GraphqlService {
  constructor(private apollo: Apollo) {}

  hydrated() {
    const appsyncClient = new AWSAppSyncClient({
      disableOffline: true,
      url: '<Your AppSync Api Url>',
      region: '<Your AppSync Api Region>',
      auth: {
        type: AUTH_TYPE.API_KEY,
        apiKey: '<Your AppSync ApiKey>',
      },
    });
    this.apollo.setClient(appsyncClient);
    return appsyncClient.hydrated();
  }
}
```

次にQueryサービスを定義します。
Apolloの[ドキュメント](https://www.apollographql.com/docs/angular/basics/services.html)を参照しながら
今回は、複数のリポジトリから情報を取得することを想定したクエリを実装しました。

```.ts
//query.service.ts
import {Injectable} from '@angular/core';
import {Query} from 'apollo-angular';
import gql from 'graphql-tag';

export interface Application {
  id: string;
  itunesstore_id: string;
  updated: string;
}

export interface Profile {
  identifier: string;
}

export interface ApplicationsWithProfiles {
  listApplications: {
    items: Application[];
  };
  listProfiles: {
    items: Profile[];
  };
}


@Injectable({
  providedIn: 'root',
})
export class ApplicationsGQL extends Query<ApplicationsWithProfiles> {
  document = gql`
    query {
      listApplications {
        items {
          id
          itunesstore_id
          updated
        }
      }
    listProfiles {
        items {
          identifier
        }
      }
    }
  `;
}
```

最後に表示するコンポーネントを作成します。


```.ts
// app.component.ts
import { Component, OnInit } from '@angular/core';
import { map } from 'rxjs/operators';
import { Application, QueryGQL, Profile } from './query.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
  title = 'GraphQL with Apollo';
  applications: Application[];
  profiles: Profile[];

  constructor(private service: QueryGQL) {}

  ngOnInit(): void {
    this.service.watch().valueChanges.pipe(
      map(v => v.data)
    ).subscribe(res => {
      this.applications = res.listApplications.items;
      this.profiles = res.listProfiles.items;
    });
  }
}
```

こちらが完成した画面になります。
無事１リクエストで複数のリポジトリから、欲しい情報だけ取得できました。すごい。

<img width="1129" alt="スクリーンショット 2018-12-21 23.07.51.png" src="https://qiita-image-store.s3.amazonaws.com/0/54858/feafb96a-fddd-7d06-c753-9fb11bb9d847.png">


# まとめ
実際にAWS AppSyncを実運用するにあたって
[AWSの制限](https://docs.aws.amazon.com/ja_jp/general/latest/gr/aws_service_limits.html#limits_appsync)や実際に使ってみないとわからないことがあると思いますが
一つのクエリで複数のデータソースを組み合わせ欲しい情報に到達することができるのは
魅力的で、それを AWS リソースに対し手軽に行えるのはやはりよかったです。

今年も残りわずかですが
今からでもGraphQLを始めてみてはいかがでしょうか！

２３日目は @studioTeaTwo さんです。よろしくお願いします。