Amplify でチャット風なアプリケーションを書いてみました。
初期設定
- 現時点では Amplify は Angular 9 をサポートしていません。Angular CLI の Version 8 をインストールします。
1
sudo npm install @angular/cli@8 --update -g
- Amplify の設定を行います
1
amplify configure
- Angular のプロジェクトを作成します。
1
ng new amplify-test --style=scss --routing
- Amplify の Angular サポートをインストールします。
1
2cd amplify-test
npm install aws-amplify aws-amplify-angular --save-dev src/polyfills.tsに以下を追加します。1
2
3
4(window as any).global = window;
(window as any).process = {
env: { DEBUG: undefined },
};- Amplify のセットアップをします。
1
amplify init
- CloudFormation のスタックが作成されます。
- Amplify の機能を追加します。認証を追加します。
1
2
3
4
5
6
7
8
9
10
11$ amplify add auth
Using service: Cognito, provided by: awscloudformation
The current configured provider is Amazon Cognito.
Do you want to use the default authentication and security configuration? Default configuration
Warning: you will not be able to edit these selections.
How do you want users to be able to sign in? Email
Do you want to configure advanced settings? No, I am done.
Successfully added resource amplifytestab53a8bb locally
... - push します。
1
amplify push
src/tsconfig.app.jsonに以下を追加します。1
2
3"compilerOptions": {
"types" : ["node"]
}main.tsに以下を追加します。1
2
3
4import Amplify from 'aws-amplify';
import awsconfig from './aws-exports';
Amplify.configure(awsconfig);src/app/app.module.tsに以下を追加します。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15import { AmplifyAngularModule, AmplifyService } from 'aws-amplify-angular';
({
...
imports: [
...
AmplifyAngularModule
],
...
providers: [
...
AmplifyService
]
...
});npm startしてみます。
認証画面を作る
- 認証画面のコンポーネントを作成します。
1
ng g component pages/auth
src/app/pages/auth/auth.component.htmlを以下のようにします。1
<amplify-authenticator></amplify-authenticator>
src/app/app.component.htmlを以下のようにします。1
<router-outlet></router-outlet>
src/app/app-routing.module.tsにパスを追加します。1
2
3const routes: Routes = [
{ path: 'auth', component: AuthComponent },
];npm startします。- ブラウザで http://localhost:4200/auth にアクセスします。

- アカウントを作ってみます。「Create account」をクリックします。

- 項目を埋めます。Username はメールアドレスである必要があります。
- 「Create account」を押します。
- メールで確認コードが飛んできますのでそれを入力します。

- 元の画面に戻ります。
- メールアドレスとパスワードを入力します
- ログインに成功します。
トップページの作成
- トップページのコンポーネントを作成します。
1
ng g component pages/top
src/app/app-routing.module.tsにパスを追加します。1
2
3
4const routes: Routes = [
...
{ path: '', component: TopComponent },
];
DataStore の作成
- 必要なパッケージをインストールします。
1
2npx amplify-app@latest
npm i @aws-amplify/core @aws-amplify/datastore --save-dev - Amplify API を追加します。
1
2
3
4
5
6
7
8
9
10
11
12
13$ amplify add api
? Please select from one of the below mentioned services: GraphQL
? Provide API name: amplifyTest
? Choose the default authorization type for the API Amazon Cognito User Pool
Use a Cognito user pool configured as a part of this project.
? Do you want to configure advanced settings for the GraphQL API No, I am done.
? Do you have an annotated GraphQL schema? No
? Do you want a guided schema creation? Yes
? What best describes your project: One-to-many relationship (e.g., “Blogs” with “Posts” and “Comments”)
? Do you want to edit the schema now? Yes
Please edit the file in your editor: /home/vagrant/amplify-test/amplify/backend/api/amplifyTest/schema.graphql
? Press enter to continue amplify/backend/api/amplifyTest/schema.graphqlファイルを編集します。1
2
3
4
5
6
7
8type Status
(rules: [{allow: owner}]) {
id: ID!
posted: AWSDateTime!
content: String!
poster: String
}npm run amplify-modelgenを実行します。npm run amplify-pushを実行します。- AppSync に API が作成されます。
- 投稿ボタン、削除ボタンを実装します。
- top.component.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
import { DataStore, Predicates } from '@aws-amplify/datastore';
import { AmplifyService } from 'aws-amplify-angular';
import { from } from 'rxjs';
import { Status } from '../../../models';
({
selector: 'app-top',
templateUrl: './top.component.html',
styleUrls: ['./top.component.scss']
})
export class TopComponent implements OnInit {
// データ
data = from(this.query());
// 投稿
fcStatus = new FormControl('');
formGroup = new FormGroup({
status: this.fcStatus,
});
constructor(
private amplifyService: AmplifyService,
) { }
ngOnInit() {
this.subscription();
}
isLoggedIn(): boolean {
return this.amplifyService.auth().user !== null;
}
onSubmit() {
DataStore.save(new Status({
content: this.fcStatus.value,
posted: new Date().toISOString(),
poster: this.amplifyService.auth().user.attributes.email,
}));
this.fcStatus.setValue('');
}
doDelete(id: string) {
DataStore.query(Status as any, id).then((status) => {
return DataStore.delete(status);
// this.list();
}).catch((error) => {
console.error(error);
});
}
private async query() {
const retVal = DataStore.query(Status, Predicates.ALL).then((statuses: Status[]) => {
statuses.sort((l, r) => {
if (l.posted > r.posted) {
return -1;
} else if (l.posted < r.posted) {
return 1;
} else {
return 0;
}
});
return statuses;
});
return retVal;
}
private subscription() {
DataStore.observe(Status as any).subscribe((status) => {
this.list();
console.log(status);
});
}
private list() {
this.data = from(this.query());
}
} - top.component.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29<div class="wrapper">
<div class="left">
<div class="post_box" *ngIf="isLoggedIn()">
<form [formGroup]="formGroup" (ngSubmit)="onSubmit()">
<textarea placeholder="何しとんじゃわれぇ" formControlName="status"></textarea>
<div class="post_box_action">
<button type="submit">投稿</button>
</div>
</form>
</div>
<div *ngIf="!isLoggedIn()">
<p>今なら無料! 今すぐ契約!!</p>
</div>
</div>
<div class="right">
<div class="contents">
<div class="status" *ngFor="let status of data | async">
<div class="status_poster"><span class="poster">{{status.poster}}</span></div>
<div class="status_inner">
<div class="status_content">{{status.content}}</div>
<div class="status_posted">
<a class="delete_link" href="javascript:void(0);" (click)="doDelete(status.id)">Delete</a>
<span class="posted_time">{{status.posted}}</span>
</div>
</div>
</div>
</div>
</div>
</div>
- top.component.ts
npm startで実行します。
ところが…
DynamoDB にいつまでたっても反映されません。ブラウザのコンソールを見ると以下のようなエラーが発生していました。
1 | [WARN] 27:23.221 DataStore - Sync error subscription failed Connection |
作り方を変えてみる
今まで作ったものを捨てて作り方を変えてみます。
- まず
amplify deleteで CloudFormation スタックを削除します。 - プロジェクトを作成します。
1
2
3
4ng new fs-amplify-test --style=scss --routing
cd fs-amplify-test
npx amplify-app@latest
npm i @aws-amplify/core @aws-amplify/datastore --save-dev - Amplify Auth を追加します。
1
2amplify init
amplify add auth amplify/backend/api/amplifyDatasource/schema.graphqlを編集します。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15type Status
(
rules: [
{
allow: owner,
operations: [create, update, delete, read]
}
])
{
id: ID!
posted: AWSDateTime!
content: String!
poster: String!
}- モデルを生成します。
1
2amplify update api
npm run amplify-modelgen npm install aws-amplify aws-amplify-angular @aws-amplify/ui --save-devamplify pushを実行します。ディレクトリを掘らなかったので1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20$ amplify push
✔ Successfully pulled backend environment dev from the cloud.
Current Environment: dev
| Category | Resource name | Operation | Provider plugin |
| -------- | ------------------------ | --------- | ----------------- |
| Api | amplifyDatasource | Create | awscloudformation |
| Auth | fsamplifytestcce6b8e1 | Create | awscloudformation |
? Are you sure you want to continue? Yes
GraphQL schema compiled successfully.
Edit your schema at /home/vagrant/fs-amplify-test/amplify/backend/api/amplifyDatasource/schema.graphql or place .graphql files in a directory at /home/vagrant/fs-amplify-test/amplify/backend/api/amplifyDatasource/schema
? Do you want to generate code for your newly created GraphQL API Yes
? Choose the code generation language target angular
? Enter the file name pattern of graphql queries, mutations and subscriptions src/graphql/**/*.graphql
? 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/app/service/api.service.tssrc/app/service/api.service.tsの作成に失敗しました。- 古いのから移植します。
- 起動します。
1
npm start
- ユーザーを作成します。
- 今度はうまくいきました。
まとめ
- 単純なシステムであれば、簡単に実装することができる。
- スキーマ設計は GraphQL だけで行うので、DynamoDB 側のことは意識する必要がなくなる。
- 自動的に CloudFormation スタックが構築されるので、システム開発に集中できる。ただし、インフラの細かい制御ができるかは不明。
- DataStore はオフラインでも動作するのでスマホサイトには最適
- プロジェクトの作り方に気を付けないとハマる。
- バックエンドがシンプルになる分、フロントエンドの負荷が高くなる。
- フロントエンドとバックエンドの両方から DynamoDB を触りたい場合、どう設計するべきか ?
- ログインが遅い。
- データのオーナーが自動的に設定されるせいか、チャットアプリにならなかった…