Cognito にユーザーがプールされたら、DynamoDBのUserテーブルにユーザー情報が追加され、AppSyncでクエリできるようにするには

要件は

  1. ユーザー閲覧機歴機能 (No.1) ユーザーとMovieのデータベースを紐づける
  2. ユーザー閲覧機歴機能 (No.2) ユーザーが閲覧したら、該当のサイドナビゲーションにチェックが付く
  3. ユーザー閲覧機歴機能 (No.3) ユーザーが閲覧したら、プロフィールに閲覧記録が書かれる

まずは、「1」の作成に必要な、全体工程は以下。

■「1. ユーザー閲覧機歴機能 (No.1) ユーザーとMovieのデータベースを紐づける工程の全体感

  1. Bitbucketへ、新たな空のリポジトリ「applat_release03」を作り、作業ディレクトリにクローンする。
  2. Cognito -Lambda - DynamoDB - AppSync のトリガーフローが通った、新しい開発(公開)環境「applat-release03」を、新しいプロファイル「dev00003」で構築する。3. の環境に新しいnextプロジェクトと共に構築。

    イベントは2つ。認証後とログイン後。

  3. 現在の開発環境「applat-release02」のデータベースのエクスポート先を、S3のパケットに作る。

    1. の「Movie」「Caption」(各言語分)のテーブルを全てエクスポート。
      1. で作成したパケットから、3. で新たに作った環境のDynamoDBへ、各言語のMovieとCaptionのデータをインポートする。
    1. で作った新しいローカル環境へ、「applat-release02」のフロントエンド部分を移行する。バックエンドに関わるsrcフォルダやAmplifyフォルダは絶対移動させないこと。
    1. で作った新しいリポジトリにpushする
  4. 全体をamplify push、そしてamplify publishする。

ここまでで、要件1 が完了する。

ここからは、

  • -

Cognito -Lambda - DynamoDB - AppSync のトリガーフローが通った、新しい開発(公開)環境「applat-release03」を、新しいプロファイル「dev00003」で構築する。3. の環境に新しいnextプロジェクトと共に構築。

イベントは2つ。認証後とログイン後。

  • -

について、記録していく。

■「2」Cognito -Lambda - DynamoDB - AppSync のトリガーフローについて

1. Amplify プロファイル設定

Amplify を使ったプロジェクト用のI AMユーザを、以下のコマンドで生成する。

amplify configure

そして、プロファイル名を設定。

2. Next.jsのインストール

next.jsフレームワークの新規作成

npx create-next-app .

3. 初期化

プロジェクトの初期化

amplify init

4. 最低限必要な依存関係のインストール

yarn add @aws-amplify/ui-react aws-amplify

参考URL

4. AppSyncとUserテーブルほか必要なテーブルの作成

amplify add api

スキーマのコードは以下。

ここでauthを仕込んでおけば、次のamplify pushでauthのフローが始まる。

type User @model
@auth(
  rules: [
    {allow: groups, groups: ["Admin"]},
    {allow: owner, ownerField: "username", operations: [read]}
  ]
){
  id: ID!
  username: String
  email: String
  moviecheck: MovieCheck @hasOne
}
type MovieCheck @model
{
  id: ID!
  user: User @belongsTo
  checkuser: String
  checklist:[CheckList] @hasMany
}
type CheckList @model
{
  id: Int!
  lang: String!
  module: String!
  part: String!
  page: String!
  check: Boolean
  moviecheck: MovieCheck @belongsTo
}
type MovieEn @model
{
  id: ID!
  lang: String!
  module: String!
  part: String!
  page: String!
  url: String!
  pagetitle: String!
  playalltime: String!
  captions: [CaptionEn] @hasMany
  outurl: String
  outurltitle: String
  movieEnPlaymovieId: String!
}
type MovieInd @model
{
  id: ID!
  lang: String!
  module: String!
  part: String!
  page: String!
  url: String!
  pagetitle: String!
  playalltime: String!
  captions: [CaptionInd] @hasMany
  outurl: String
  outurltitle: String
  movieIndPlaymovieId: String!
}
type MovieVn @model
{
  id: ID!
  lang: String!
  module: String!
  part: String!
  page: String!
  url: String!
  pagetitle: String!
  playalltime: String!
  captions: [CaptionVn] @hasMany
  outurl: String
  outurltitle: String
  movieVnPlaymovieId: String!
}
type MovieTha @model
{
  id: ID!
  lang: String!
  module: String!
  part: String!
  page: String!
  url: String!
  pagetitle: String!
  playalltime: String!
  captions: [CaptionTha] @hasMany
  outurl: String
  outurltitle: String
  movieThaPlaymovieId: String!
}
type MovieTgl @model
{
  id: ID!
  lang: String!
  module: String!
  part: String!
  page: String!
  url: String!
  pagetitle: String!
  playalltime: String!
  captions: [CaptionTgl] @hasMany
  outurl: String
  outurltitle: String
  movieTglPlaymovieId: String!
}
type MovieRus @model
{
  id: ID!
  lang: String!
  module: String!
  part: String!
  page: String!
  url: String!
  pagetitle: String!
  playalltime: String!
  captions: [CaptionRus] @hasMany
  outurl: String
  outurltitle: String
  movieRusPlaymovieId: String!
}
type MovieNep @model
{
  id: ID!
  lang: String!
  module: String!
  part: String!
  page: String!
  url: String!
  pagetitle: String!
  playalltime: String!
  captions: [CaptionNep] @hasMany
  outurl: String
  outurltitle: String
  movieNepPlaymovieId: String!
}
type MovieKhm @model
{
  id: ID!
  lang: String!
  module: String!
  part: String!
  page: String!
  url: String!
  pagetitle: String!
  playalltime: String!
  captions: [CaptionKhm] @hasMany
  outurl: String
  outurltitle: String
  movieKhmPlaymovieId: String!
}
type MovieBur @model
{
  id: ID!
  lang: String!
  module: String!
  part: String!
  page: String!
  url: String!
  pagetitle: String!
  playalltime: String!
  captions: [CaptionBur] @hasMany
  outurl: String
  outurltitle: String
  movieBurPlaymovieId: String!
}
type MovieBen @model
{
  id: ID!
  lang: String!
  module: String!
  part: String!
  page: String!
  url: String!
  pagetitle: String!
  playalltime: String!
  captions: [CaptionBen] @hasMany
  outurl: String
  outurltitle: String
  movieBenPlaymovieId: String!
}
type CaptionEn @model
{
  id: ID!
  lang: String!
  module: String!
  part: String!
  page: String!
  content: Int
  startTime: String
  endTime: String
  body: String
  movie: MovieEn @belongsTo
  movieEnCaptionsId: String!
}
type CaptionInd @model
{
  id: ID!
  lang: String!
  module: String!
  part: String!
  page: String!
  content: Int
  startTime: String
  endTime: String
  body: String
  movie: MovieInd @belongsTo
  movieIndCaptionsId: String!
}
type CaptionVn @model
{
  id: ID!
  lang: String!
  module: String!
  part: String!
  page: String!
  content: Int
  startTime: String
  endTime: String
  body: String
  movie: MovieVn @belongsTo
  movieVnCaptionsId: String!
}
type CaptionTha @model
{
  id: ID!
  lang: String!
  module: String!
  part: String!
  page: String!
  content: Int
  startTime: String
  endTime: String
  body: String
  movie: MovieTha @belongsTo
  movieThaCaptionsId: String!
}
type CaptionTgl @model
{
  id: ID!
  lang: String!
  module: String!
  part: String!
  page: String!
  content: Int
  startTime: String
  endTime: String
  body: String
  movie: MovieTgl @belongsTo
  movieTglCaptionsId: String!
}
type CaptionRus @model
{
  id: ID!
  lang: String!
  module: String!
  part: String!
  page: String!
  content: Int
  startTime: String
  endTime: String
  body: String
  movie: MovieRus @belongsTo
  movieRusCaptionsId: String!
}
type CaptionNep @model
{
  id: ID!
  lang: String!
  module: String!
  part: String!
  page: String!
  content: Int
  startTime: String
  endTime: String
  body: String
  movie: MovieNep @belongsTo
  movieNepCaptionsId: String!
}
type CaptionKhm @model
{
  id: ID!
  lang: String!
  module: String!
  part: String!
  page: String!
  content: Int
  startTime: String
  endTime: String
  body: String
  movie: MovieKhm @belongsTo
  movieKhmCaptionsId: String!
}
type CaptionBur @model
{
  id: ID!
  lang: String!
  module: String!
  part: String!
  page: String!
  content: Int
  startTime: String
  endTime: String
  body: String
  movie: MovieBur @belongsTo
  movieBurCaptionsId: String!
}
type CaptionBen @model
{
  id: ID!
  lang: String!
  module: String!
  part: String!
  page: String!
  content: Int
  startTime: String
  endTime: String
  body: String
  movie: MovieBen @belongsTo
  movieBenCaptionsId: String!
}

3. プッシュとauth

amplify push

すると、authのフローが始まる。

? Are you sure you want to continue?

Yes
Do you want to use the default authentication and security configuration?

Manual configuration
Select the authentication/authorization services that you want to use:

User Sign-Up, Sign-In, connected with AWS IAM controls (Enables per-user Storage features for images or other content, Analyti
cs, and more)
Provide a friendly name for your resource that will be used to label this category in the project:

(そのままenter)
Enter a name for your identity pool.

(そのままenter)
Allow unauthenticated logins? (Provides scoped down permissions that you can control via AWS IAM)

No
 Do you want to enable 3rd party authentication providers in your identity pool?

No
Provide a name for your user pool:

(そのままenter)
 How do you want users to be able to sign in?

Username
Do you want to add User Pool Groups?

Yes
? Provide a name for your user pool group:

Admin
? Do you want to add another User Pool Group

No
✔ Sort the user pool groups in order of preference ·

Admin
Do you want to add an admin queries API?

No
Multifactor authentication (MFA) user login options:

OFF
Email based user registration/forgot password:

Enabled (Requires per-user email entry at registration)
Specify an email verification subject:

Your verification code
Specify an email verification message:

Your verification code is {####}
 Do you want to override the default password policy for this User Pool?

No
What attributes are required for signing up?

Email
Specify the app's refresh token expiration period (in days):

7
 Do you want to specify the user attributes this app can read and write?

No
Do you want to enable any of the following capabilities?

(何も選ばずそのままenter)
Do you want to use an OAuth flow?

No

ここでやっとLambdaとの連携話が登場!

? Do you want to configure Lambda Triggers for Cognito?

Yes
? Which triggers do you want to enable for Cognito

Post Confirmation
? What functionality do you want to use for Post Confirmation

Create your own module
? Do you want to edit your custom function now?

Yes

これで、Lambda用のcustom.jsが立ち上がるので、以下の通り入れる。

 var aws = require('aws-sdk');
 var ddb = new aws.DynamoDB();
 exports.handler = async (event, context) => {

     let date = new Date();
     if (event.request.userAttributes.sub) {
         let params = {
             Item: {
                 'id': {S: event.request.userAttributes.sub},
                 '__typename': {S: 'User'},
                 'username': {S: event.userName},
                 'email': {S: event.request.userAttributes.email},
                 'createdAt': {S: date.toISOString()},
                 'updatedAt': {S: date.toISOString()},
             },
             TableName: process.env.USERTABLE
         };
         // Call DynamoDB
         try {
             await ddb.putItem(params).promise()
             console.log("Success");
         } catch (err) {
             console.log("Error", err);
         }
         console.log("Success: Everything executed correctly");
         context.done(null, event);
     } else {
         // Nothing to do, the user's email ID is unknown
         console.log("Error: Nothing was written to DynamoDB");
         context.done(null, event);
     }
 };

上記が立ち上がると

? Do you want to generate code for your newly created GraphQL API

Yes

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

GraphQL API KEY: da2-w7uygek4yjhutlsyyuqbq2uihq

4. Amplifyのコンソール画面から

amplify console

「AppSyncで表示」をクリック