AWS AmplifyでAppSyncのGraphQLスキーマを作成する

2019.02.04

目指すもの

まずはログインしているユーザーがつぶやきを投稿できるだけのシンプルなアプリケーションを目指す。Amazon Cognito上ではユーザーは複数作成されるが、つぶやきにはユーザーは関連付けない。

学びたいこと

AWS Amplifyで定義したGraphQLスキーマがどのようにAppSyncのスキーマに変換されるのかを学びたい。

スキーマを定義してみる

現在のamplify/backend/api/amplifysample/schema.graphqlは以下のようになっている。



type Blog @model {
  id: ID!
  name: String!
  posts: [Post] @connection(name: "BlogPosts")
}
type Post @model {
  id: ID!
  title: String!
  blog: Blog @connection(name: "BlogPosts")
  comments: [Comment] @connection(name: "PostComments")
}
type Comment @model {
  id: ID!
  content: String
  post: Post @connection(name: "PostComments")
}

ルールに則ってこのファイルに@model@connectionをつけていくと、AWS Amplifyの機能であるGraphQL TransformによってGraphQLスキーマに変換されてAppSyncに設定される模様。

今回は既存のスキーマよりもっともっとシンプルにしてみる。



type Post @model {
  id: ID!
  content: String!
}

つぶやきを表すエンティティとしてPostのみ作成した。これをビルドしてみる。



$ amplify api gql-compile

ビルドした結果、リゾルバーの定義がほとんどなくなった。



$ git add .
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

	deleted:    build/resolvers/Blog.posts.request
	deleted:    build/resolvers/Blog.posts.response
	deleted:    build/resolvers/Comment.post.request
	deleted:    build/resolvers/Comment.post.response
	deleted:    build/resolvers/Mutation.createBlog.request
	deleted:    build/resolvers/Mutation.createBlog.response
	deleted:    build/resolvers/Mutation.createComment.request
	deleted:    build/resolvers/Mutation.createComment.response
	deleted:    build/resolvers/Mutation.deleteBlog.request
	deleted:    build/resolvers/Mutation.deleteBlog.response
	deleted:    build/resolvers/Mutation.deleteComment.request
	deleted:    build/resolvers/Mutation.deleteComment.response
	deleted:    build/resolvers/Mutation.updateBlog.request
	deleted:    build/resolvers/Mutation.updateBlog.response
	deleted:    build/resolvers/Mutation.updateComment.request
	deleted:    build/resolvers/Mutation.updateComment.response
	deleted:    build/resolvers/Post.blog.request
	deleted:    build/resolvers/Post.blog.response
	deleted:    build/resolvers/Post.comments.request
	deleted:    build/resolvers/Post.comments.response
	deleted:    build/resolvers/Query.getBlog.request
	deleted:    build/resolvers/Query.getBlog.response
	deleted:    build/resolvers/Query.getComment.request
	deleted:    build/resolvers/Query.getComment.response
	deleted:    build/resolvers/Query.listBlogs.request
	deleted:    build/resolvers/Query.listBlogs.response
	deleted:    build/resolvers/Query.listComments.request
	deleted:    build/resolvers/Query.listComments.response
	modified:   build/schema.graphql
	modified:   cloudformation-template.json
	modified:   schema.graphql

変更をプッシュする



$ amplify push

# GraphQL関連のコードをアップデートするかどうかを指定
? Do you want to update code for your updated GraphQL API Yes

# GraphQLスキーマに応じたQuery・Mutation・Subscriptionを生成するかどうかを指定
? Do you want to generate GraphQL statements (queries, mutations and subscription) based on your schema types? This wi
ll overwrite your current graphql queries, mutations and subscriptions Yes

CloudFormationが動作してバックエンドの構成を変更してくれる。AppSyncのマネジメントコンソールを見たら、ちゃんとスキーマが変わっていた。

AWS AmplifyでAppSyncのGraphQLスキーマを作成する

一方、ローカルのプロジェクトで生成されたファイルはamplify/backend/api/amplifysample/srcの中に入っている状態で、既存のsrc/API.tsは前のままだった。これでは最新のスキーマに合わせてコードを書いていくことができないような...



	new file:   amplify/backend/api/amplifysample/src/API.ts
	new file:   amplify/backend/api/amplifysample/src/graphql/mutations.ts
	new file:   amplify/backend/api/amplifysample/src/graphql/queries.ts
	new file:   amplify/backend/api/amplifysample/src/graphql/schema.json
	new file:   amplify/backend/api/amplifysample/src/graphql/subscriptions.ts

念の為もう一度$ amplify pushしてみると、今度はsrc/API.ts等が変更された。 気になって$ amplify statusを実行してみるとプッシュした後にも関わらずAPIがUpdateになっている... 何か設定を間違えたんだろうか。

本番環境で運用しているときにこういったトラブルが発生したらテンパってしまうかも。今のうちに経験できてよかった。



$ amplify status
| Category | Resource name   | Operation | Provider plugin   |
| -------- | --------------- | --------- | ----------------- |
| Api      | amplifysample   | Update    | awscloudformation |
| Auth     | cognito1b62e7cb | No Change | awscloudformation |

もう一度$ amplify pushしてみるも、依然としてUpdateになっている。AppSyncのスキーマやリゾルバーはちゃんと変更されているようなのでここは無視して先へ進んでみることにした。

Postの読み込みと作成を行う処理を実装する

HomePageコンポーネントには画面遷移関連の実装が既にあり、サンプルとして見づらくなってしまうのでPostComponentをSharedModuleに定義してそちらに実装してみた。



<ion-list>
  <ion-item *ngFor="let post of posts">
    <ion-label>{{post.content}}</ion-label>
  </ion-item>

  <ion-item *ngIf="posts.length === 0">
    つぶやきがありません。
  </ion-item>
</ion-list>

<ion-fab vertical="bottom" horizontal="end" slot="fixed">
  <ion-fab-button (click)="onAddPostClicked()">
    <ion-icon name="add"></ion-icon>
  </ion-fab-button>
</ion-fab>



import { Component, OnInit } from '@angular/core';
import { AmplifyService } from 'aws-amplify-angular';
import { graphqlOperation } from 'aws-amplify';
import * as queries from '../../../../graphql/queries';
import * as mutations from '../../../../graphql/mutations';
import * as subscriptions from '../../../../graphql/subscriptions';

@Component({
  selector: 'app-posts',
  templateUrl: './posts.component.html',
  styleUrls: ['./posts.component.scss']
})
export class PostsComponent implements OnInit {

  posts = [];

  constructor(
    private amplifyService: AmplifyService,
  ) { }

  ngOnInit() {
    // サーバーからプッシュされる情報を受け取る
    (<any>this.amplifyService.api().graphql(graphqlOperation(subscriptions.onCreatePost))).subscribe(data => {
      this.posts.push(data.value.data.onCreatePost);
    });

    // 初回のデータ読み込みを行う
    this.loadData();
  }

  async onAddPostClicked() {
    this.amplifyService.api().graphql(graphqlOperation(mutations.createPost, { input: {
      content: `つぶやきテスト ${new Date()}`,
    }}));
  }

  private async loadData() {
    const ret = await this.amplifyService.api().graphql(graphqlOperation(queries.listPosts));
    this.posts = (<any>ret).data.listPosts.items;
  }
}

this.amplifyService.api().graphql()API.d.tsの定義によると以下のようになっている。



graphql({ query: paramQuery, variables }: GraphQLOptions): Promise<GraphQLResult> | Observable<object>;

Promise<GraphQLResult> | Observable<object>を戻り値の型として持つ関数に対してどうやって後続の処理を書いたら良いか分からず、<any>で型情報を無視してsubscribeしてしまった。

ひとまずこれで、アプリを開いてログインすると以下のような動作をするアプリを作ることができた。

  • 初回に全てのつぶやきを読み込む
  • プラスボタンを押すと「つぶやき テスト(日時)」というつぶやきを投稿する
  • つぶやき作成時に自動的に更新される

まとめ

AWS Amplifyを使ってバックエンドを構築するときに若干不具合っぽい動作が見られてしまったが、とりあえずGraphQLで言うところのQuery・Mutation・Subscriptionを試すことができた。

次はaws-amplify-angularが提供するgraphqlメソッドではなく、AWS AppSync SDKのAWSAppSyncClientクラスを使ってAppSyncにアクセスするように変更してみたい。