2019年12月3日

プログラミング

ステップバイステップ AWS Amplify GraphQL API

目次

  1. はじめに
  2. React + AWS Amplify プロジェクトのセットアップ
  3.  React プロジェクト作成
  4.  AWS Amplifyのセットアップ
  5.  カテゴリの追加とデプロイ
  6. Amplify GraphQL Client を使った実装
  7. AWS AppSync SDK を使った実装
  8. Apollo Client を自分で設定する実装
  9. まとめ

はじめに

この記事は GraphQL Advent Calendar 2019 および bagelee Advent Calendar 2019 の3日目の記事になります。

GraphQL Advent Calendar は3日目が空いていたので飛び込みで参加させていただきましたが、昨日は yoshikyoto さんの RESTを採用して気づいた「GraphQLって結局何が良いの?」について という記事でした。 GraphQL の一番の強みは「型」というのを強調されていますが、自分もそこに可能性を見出しているところです。

bagelee では figma 上でアニメーションを追加する Smart Animate 機能をご紹介いたしました。

【Figma】Smart Animate機能でできること

当 bagelee を運営している株式会社palanでは、コードの生産性およびコンポーネントの再利用性のよさに着目し React および React Native を積極的に採用しています。 React は AWS Amplify や Firebase などの mBaaS (Mobile Backend as a Service) と呼ばれるサービスととても相性がよいのですが、2019年に入ってからは AWS の豊富な他サービスとシームレスに連携できる柔軟性から、 mBaaS として AWS Amplify を採用する機会が増えてきたという印象です。

AWS Amplify は様々な機能をサポートしておりますが、その中でも特徴的なのは AWS AppSync を使った GraphQL API 通信がデフォルトの選択肢になっているということだと思います。(選択次第で API Gateway を使った REST API にすることもできます)

基本は AWS Amplify のドキュメント に従っていけば実装できるのですが、AWS Amplify で GraphQL を使うには実は方法が複数あって、そこを把握していないと読み解くのに時間がかかると思います。
現在、 React + AWS Amplify で GraphQL 通信を行うには、大まかに以下の3つの方法があり、下に行くほど自由度が上がりますが知識が必要になります。

  1. 1
    Amplify GraphQL Client を使う方法
  2. 2
    AWS AppSync SDK を使う方法
  3. 3
    Apollo Client を自分で設定する方法

今回は、 create-react-app で作った React (TypeScript) のプロジェクトに AWS Amplify をセットアップし、 GraphQL 通信を行う3つの方法をそれぞれ見ていきます。

React + AWS Amplify プロジェクトのセットアップ

React プロジェクト作成

まずは create-react-app を使ってプロジェクトを作成します。

$ npm install -g create-react-app
$ create-react-app amplify-react-graphql-example --typescript
$ cd amplify-react-graphql-example
$ yarn start

これで localhost:3000 にローカルサーバーが立ち上がります。 --typescript オプションで TypeScript のテンプレートを指定しています。

AWS Amplifyのセットアップ

まず、インストールしていない方は Amplicy CLI をインストールします。

$ npm install -g @aws-amplify/cli

次にプロファイル(接続情報)をセットアップします。

$ amplify configure
Follow these steps to set up access to your AWS account:

Sign in to your AWS administrator account:
https://console.aws.amazon.com/
Press Enter to continue

Specify the AWS Region
? region:  ap-northeast-1
Specify the username of the new IAM user:
? user name:  amplify-*****

...

? accessKeyId:  (<YOUR_ACCESS_KEY_ID>)
? secretAccessKey:  (<YOUR_SECRET_ACCESS_KEY>)

ブラウザで AWS コンソールのログイン画面が開くのでログインして、ターミナルに戻って Enter を押しリージョンを選択すると amplify-***** (ランダムな文字列)という名前のユーザーを作成する画面が開くので、画面に従ってユーザーを作成し、アクセスキーIDとシークレットアクセスキーを控えておきます。再びターミナルに戻って Enter を押し、アクセスキーIDとシークレットアクセスキーを入力します。

POINT!!

ここで作られるユーザーは AdministratorAccess 権限であるため、認証情報の取り扱いには十分注意してください。自信がない場合は詳しい人に見てもらいながらセットアップしましょう。

次にプロジェクトに Amplify の設定をしていきますが、 create-react-app の場合デフォルトの設定が正しい状態なのでほぼ Enter を押すだけで大丈夫ですが、 Enter a name for the environment のみ入力が必須です。作成されるリソース名の末尾についたりするのですが今回は自由に入力してしまって大丈夫です。最後に、先ほど作成したプロファイル名を選択します。

$ amplify init
? Enter a name for the project amplifyreactgraphqle
? Enter a name for the environment dev # 自由に入力

...

? Do you want to use an AWS profile?: Yes
? Please choose the profile you want to use
> amplify-*****

カテゴリの追加とデプロイ

Amplify に Auth カテゴリ (Cognito) と API カテゴリ (AppSync) を追加していきます。別々に追加していっても良いのですが API カテゴリを追加する際に認証方法として Amazon Cognito User Pool を選択することで Auth カテゴリも同時に追加されます。

$ amplify add api
? Please select from one of the below mentioned services GraphQL
? Provide API name amplifyreactgraphqle
? Choose the default authorization type for the API
> Amazon Cognito User Pool
Using service: Cognito, provided by: awscloudformation

...

? What best describes your project
> Single object with fields (e.g., “Todo” with ID, name, description)
? Do you want to edit the schema now? No

GraphQLのスキーマ amplify/backend/api/(プロジェクト名)/schema.graphql に生成されます。最後から2番目の質問の選択肢によって生成されるスキーマが変わるのですが、 Single object with fields を選択した場合以下のような Todo type のみが定義されたスキーマが生成されます。

type Todo @model {
  id: ID!
  name: String!
  description: String
}

@model というのは Amplify 特有のディレクティブです。 @model がついている type はデプロイ時に DynamoDB にテーブルが作成され、 AppSync と接続されます。
ここまで完了したら、実際に AWS 上にデプロイします。デプロイが完了すると TypeScript の実装で使えるコードが生成されるのですが、途中の選択肢によって、生成先や型のネストの深さが変わってきます。

$ amplify push
✔ Successfully pulled backend environment dev from the cloud.

Current Environment: dev

| Category | Resource name                | Operation | Provider plugin   |
| -------- | ---------------------------- | --------- | ----------------- |
| Auth     | amplifyreactgraphqle***** | Create    | awscloudformation |
| Api      | amplifyreactgraphqle         | Create    | awscloudformation |
? Are you sure you want to continue?: Yes

...

? Do you want to generate code for your newly created GraphQL API Yes
? Choose the code generation language target typescript
? Enter the file name pattern of graphql queries, mutations and subscriptions src/graphql/**/*.ts
? Do you want to generate/update all possible GraphQL operations - queries, mutations and subscriptions Yes
? Enter maximum statement depth [increase from default if your schema is deeply nested] 2
? Enter the file name for the generated code src/API.ts

...

✔ Generated GraphQL operations successfully and saved at src/graphql
✔ Code generated successfully and saved in file src/API.ts
✔ All resources are updated in the cloud

GraphQL endpoint: https://***.appsync-api.ap-northeast-1.amazonaws.com/graphql

デプロイの最後に GraphQL のエンドポイントの URL が出力されますが、これを普通にブラウザで開くと認証エラーとなり、 Cognito User Pool 認証が有効になっていることが確認できます。

https___irpgqnkgezf5ndgwwc4ruibqji_appsync-api_ap-northeast-1_amazonaws_com_graphql.png (7.8 kB)

ここまで来ればもう少しです! React プロジェクトで使う AWS Amplify のライブラリを追加していきます。

$ yarn add aws-amplify aws-amplify-react

そして、 App.tsx で必要な import と Amplify.configure を実行し、 App コンポーネントを withAuthenticator HOC で囲います。

// ... 

import Amplify from 'aws-amplify';
import { withAuthenticator } from 'aws-amplify-react';
import awsconfig from './aws-exports';

Amplify.configure(awsconfig);

// ...

export default withAuthenticator(App, true);

コンポーネントを withAuthenticator で囲うだけでなんと実際にユーザー登録、ログイン、ログアウト等が可能な認証画面が追加されます。実際にサービスとして公開するにはデザイン等を調整する必要がありますが、ひとまず動くものとしては十分すぎる機能です。

React_App.png (12.2 kB)

Amplify GraphQL Client を使った実装

Amplify GraphQL Client を使う場合、今までのセットアップが済んでいればライブラリを追加する必要はありません。

Query でデータを取ってくるには、以下のようなコードになります。

// ... 

import Amplify, { API, graphqlOperation } from 'aws-amplify';
import awsconfig from './aws-exports';
import * as queries from './graphql/queries';

Amplify.configure(awsconfig);

const App: React.FC = () => {
  const [todos, setTodos] = useState<any[]>([]);

  useEffect(() => {
    (API.graphql(graphqlOperation(queries.listTodos)) as Promise<any>).then(
      result => {
        setTodos(result.data.listTodos.items);
      }
    );
  }, []);

  return (
    // ...
      <ul>
        {todos.map(todo => (
          <li key={todo.id}>{todo.name}</li>
        ))}
      </ul>
    // ...

Mutation も含んだ全てのサンプルコードは こちら です。

補足: useEffect の中で async / await を使うとかえって複雑になるため Promise ベースの書き方にしています

Fetch API や axios を使った通常の API 通信に近い手続き型のコードです。ある程度の規模や複雑さになってきたら Redux の非同期ミドルウェアなどと組み合わせたくなってくると思いますが、その前にこれから紹介する他の方法に乗り換えることがおすすめです。

また、生成された TypeScript の型には GraphQL で定義している type 自体の型定義が含まれていないので、ちゃんと型をつけようと思ったら工夫が必要です(サンプルコードでは手を抜いて any にしてしまいました)

Amplify GraphQL Client を使う方法

  • – Amplify をセットアップすればすぐ使える
  • – 複雑な処理には不向き
  • – TypeScript の型をしっかり定義しようと思うと難しい

AWS AppSync SDK を使った実装

この方法では、 AWS AppSync SDK を別途追加する必要があります。

$ yarn add aws-appsync react-apollo@2.5.8

一緒に追加している react-apollo というのは Apollo Client を React で利用するためのライブラリです。実は AWS AppSync SDK はオープンソースの Apollo Client をベースに AWS が独自に機能を追加したものになっています。ただし、内部の Apollo Client のバージョンが長い間 2.4.6 で止まってしまっており、 react-apollo も 2.5.8 までバージョンを落とさないと動きませんでした。

AWS AppSync SDK を使う場合、ApolloProvider でアプリ全体を囲う必要があるため、コードを分割すると見通しがよくなります。

import React from 'react';
import Amplify, { Auth } from 'aws-amplify';
import { withAuthenticator } from 'aws-amplify-react';
import AWSAppSyncClient, { AUTH_TYPE } from 'aws-appsync';
import { ApolloProvider } from 'react-apollo';
import awsconfig from './aws-exports';
import Form from './Form';
import List from './List';

Amplify.configure(awsconfig);

const client = new AWSAppSyncClient({
  url: awsconfig.aws_appsync_graphqlEndpoint,
  region: awsconfig.aws_appsync_region,
  auth: {
    type: AUTH_TYPE.AMAZON_COGNITO_USER_POOLS,
    jwtToken: async () =>
      (await Auth.currentSession()).getAccessToken().getJwtToken(),
  },
});

const App: React.FC = () => {
  return (
    <ApolloProvider client={client}>
      <Form />
      <List />
    </ApolloProvider>
  );
};

export default withAuthenticator(App, true);

Query を使う List コンポーネントと、 Mutation を使う Form コンポーネントに分離しました。こちらもボリュームの都合上 List コンポーネントのみコードを掲載します。

import React from 'react';
import { graphql, DataProps } from 'react-apollo';
import gql from 'graphql-tag';
import * as queries from './graphql/queries';
import { ListTodosQuery, ListTodosQueryVariables } from './API';

type Props = {} & DataProps<ListTodosQuery, ListTodosQueryVariables>;

const List: React.FC<Props> = props => {
  const { data } = props;
  const todos = (data.listTodos && data.listTodos.items) || [];
  return (
    <ul>
      {todos.map(todo => (todo ? <li key={todo.id}>{todo.name}</li> : null))}
    </ul>
  );
};

export default graphql<{}, ListTodosQuery, ListTodosQueryVariables, Props>(
  gql(queries.listTodos)
)(List);

全てのサンプルコードは こちら です。

これは AWS AppSync SDK ではなく Apollo Client の機能ですが、特筆すべきは Query の結果の状態を Apollo Client 側で持ってくれるということです。 Amplify GraphQL Client を使った実装のように自分で通信まわりの state を管理する必要がないので、 API 通信のために Redux を導入する必要はなくなります。

Mutation が成功したときに Query をリロードする処理も、 refetchQueries というオプションを使うことで簡単に実現できます。

mutate({
  variables: {
    input: {
      name,
    },
  },
  refetchQueries: ['ListTodos'],
});

Amplify から出力された型定義をつかってうまく any を消すことができました。しかし同じ型をコンポーネント側と graphql 側に複数回渡す必要があり、毎回書いていくのは少し大変です。

AWS AppSync SDK を使う方法

  • – Apollo Client の機能が活用できる
  • – TypeScript の型をちゃんと定義できるが、記述量は多い
  • – 依存している Apollo Client のバージョンが古い

Apollo Client を自分で設定する実装

前述の通り AWS AppSync SDK は内部で Apollo Client を使っていますが、そのバージョンが 2.4.6 で止まってしまっています。しかし、2019年10月にこっそりと aws-appsync-auth-link パッケージが公開され、これを使うと最新の Apollo Client のバージョンに追従することが可能になりました。(以前から aws-appsync のコードを参考にすれば自分で接続することは可能でしたがオフィシャルな方法になりました)

最新の Apollo Client では React Hooks を使うことで TypeScript の型もより簡潔に定義できるようになっています。せっかくなので React Hooks を使ってみましょう。

AWS AppSync SDK を削除し、必要なパッケージを追加していきましょう。

$ yarn remove aws-appsync react-apollo
$ yarn add apollo-client apollo-cache-inmemory apollo-link-http @apollo/react-hooks aws-appsync-auth-link

Apollo Client のセットアップの記述を以下のように変更します。変更点は、 AWSAppSyncClient ではなく ApolloClient を使うのと、認証に関する設定を createAuthLink に渡すところです。

import { ApolloClient } from 'apollo-client';
import { ApolloProvider } from '@apollo/react-hooks';
import { ApolloLink } from 'apollo-link';
import { createHttpLink } from 'apollo-link-http';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { createAuthLink } from 'aws-appsync-auth-link';
import awsconfig from './aws-exports';

const client = new ApolloClient({
  link: ApolloLink.from([
    createAuthLink({
      url: awsconfig.aws_appsync_graphqlEndpoint,
      region: awsconfig.aws_appsync_region,
      auth: {
        type: 'AMAZON_COGNITO_USER_POOLS',
        jwtToken: async () =>
          (await Auth.currentSession()).getAccessToken().getJwtToken(),
      },
    }),
    createHttpLink({
      uri: awsconfig.aws_appsync_graphqlEndpoint,
    }),
  ]),
  cache: new InMemoryCache(),
});

Apollo Client のバージョンが最新になったことで、関連ツールも最新のものを使うことができるようになりました。詳しい設定は今回は割愛しますが、 GraphQL Code Generator を使うと、クエリを引数として渡すところや TypeScript の型定義が全て済んでおりすぐに使える Hooks を出力できます。

こうして実装した List コンポーネントは以下のようになります。

import React from 'react';
import { useListTodosQuery } from './graphql/generated';

const List: React.FC = () => {
  const { data } = useListTodosQuery();
  const todos = (data && data.listTodos && data.listTodos.items) || [];
  return (
    <ul>
      {todos.map(todo => (todo ? <li key={todo.id}>{todo.name}</li> : null))}
    </ul>
  );
};

export default List;

最終的なサンプルコードは こちら です。

自分で型定義する必要がなくなり、行数も減って非常にシンプルになりました。自分で書くコードが減るということは、メンテナンスのコストも減り、バグを生み出す可能性が少なくなるということです。

補足: && を使っている箇所は TypeScript 3.7.2 から導入された Optional Chaining と Nullish Coalescing を使うことでより簡潔に書けます

Apollo Client を自分で設定する方法

  • – 最新のライブラリやツールを使うことができる
  • – React Hooks を使ってシンプルに実装できる
  • – Apollo Client の設定を自分でする必要がある

まとめ

React + AWS Amplify で GraphQL 通信を行う方法を3種類、サンプルコードを交えてご紹介いたしました。

POINT!!

今回 Amplify で作成したリソースは、必要がなくなったら amplify delete コマンドを使うか CloudFormation のコンソール上から削除しておくとよいでしょう。

2019年10月のタイミングで aws-appsync-auth-link が公開されたということは Apollo Client と組み合わせて使える人は自由に使っていいというメッセージだと捉えることもできますが、一度に全部理解しようとすると大変なので、習熟度などに合わせて選択していくのがいいと思います。

0

0

AUTHOR

しんのき エンジニア

主に React Native を使ったアプリ開発と AWS や Firebase を使ったサーバーレスアーキテクチャを担当しています。元々はインフラとかPHPをやっていました。

アプリでもっと便利に!気になる記事をチェック!

記事のお気に入り登録やランキングが表示される昨日に対応!毎日の情報収集や調べ物にもっと身近なメディアになりました。

palanでは一緒に働く仲間を募集しています

正社員や業務委託、アルバイトやインターンなど雇用形態にこだわらず、
ベテランの方から業界未経験の方まで様々なかたのお力をお借りしたいと考えております。

話を聞いてみたい

運営メンバー

eishis

Eishi Saito 総務

SIerやスタートアップ、フリーランスを経て2016年11月にeishis, Inc.を設立。 マーケター・ディレクター・エンジニアなど何でも屋。 COBOLからReactまで色んなことやります。

sasakki デザイナー

アメリカの大学を卒業後、日本、シンガポールでデザイナーとして活動。

しまだ

しまだ デザイナー

WebAR/VRのデザインと3DCG制作がメインです。 肩書きは「アニメ案件に関わりたいデザイナー」。

Miu マーケター

ドイツでWEBマーケティングしています。

しんのき エンジニア

主に React Native を使ったアプリ開発と AWS や Firebase を使ったサーバーレスアーキテクチャを担当しています。元々はインフラとかPHPをやっていました。

yamakawa

やまかわたかし デザイナー

フロントエンドデザイナー。デザインからHTML / CSS、JSの実装を担当しています。最近はReact NativeやReact360をよく触っています。

furuya エンジニア

サーバーサイド、フロントエンド、Unityと色々手を出してる雑食系エンジニア。ReactNativeが最近のマイブーム。

Sayaka Osanai デザイナー

Sketchだいすきプロダクトデザイナー。シンプルだけどちょっとかわいいデザインが得意。 好きな食べものは生ハムとお寿司とカレーです。

はらた

はらた エンジニア

サーバーサイドエンジニア Ruby on Railsを使った開発を行なっています

うえまつゆい エンジニア

サーバーサイドエンジニアからフロントエンドエンジニアになりました。主にReact Nativeでのアプリ開発をしています。

kobori

こぼり ともろう エンジニア

サーバーサイドエンジニア。SIerを経て2019年7月に入社。日々学習しながらRuby on Railsを使った開発を行っています。

sasai

ささい エンジニア

フロントエンドエンジニア WebGLとReactが強みと言えるように頑張ってます。

damien

Damien

WebAR/VRを中心に企画やディレクションやエンジニアもちょっとやっています。森に住んでいます。

CONTACT PAGE TOP