【SAM + Cognito + Amplify ライブラリでログイン機能を実装した React アプリを作成】Part1 - SAM で API 作成


みなさん、こんにちは。イノベーションLABのハヤシです。

今回は、以下の構成の Web アプリをさくっと作る手順をご紹介します。
 バックエンド:Node.js の REST API(Amazon API Gateway / AWS Lambda)
 フロントエンド:TypeScript の React

今回は全 3 回のうちの 1 回です。
必要な部分だけでも、ぜひ参考にしてみてください。

環境構築手順はこちらで紹介しています
blog.css-net.co.jp


1. 前提条件

1-1. 想定読者

「さくっとログイン機能を実装した Web アプリが作りたい」
「せっかくなら SPA + REST API でやりたい」
「AWS SAM や Amplify ライブラリを使ってみたい」
といった方々を想定しています。


必要最低限の方法のみ解説しているので、ほぼサンプルコードのままです。
その後のカスタマイズには、 Node.js や TypeScript / React の知識が必要になります。


とはいえ、この記事は知識があることを前提としていませんので、手順通りに実施していく分にはそれらの知識は不要です。
雰囲気をつかむために、まずはチャレンジしてみるのもおすすめです!

1-2. 作成するもの

Part 3 までで、以下の内容を実装します。

  • DynamoDB を利してデータを登録/取得する API(Part1 / 2 / 3)
  • サインイン画面(Part3)

  • サインイン後にAPIからデータを取得して表示する画面(Part2 / 3)


1-3. 前提条件

OS - Windows 10 Pro (Home でも問題ありません)
VSCode - 1.69.1
Git - 2.37.1.windows.1
AWS CLI - 2.7.14
SAM CLI - 1.53.0
Node.js - 16.16.0


aws configure で認証設定がすんでいること
以下コマンドで、「Account」が自分が操作可能な Account ID になっていることを確認する

aws sts get-caller-identity

# Profile を指定している場合は今後のコマンドも全て Profile を指定してください
# aws sts get-caller-identity --profile <Profile名>


この後の手順は特に指示がない限り、エディタは VSCode でターミナルは Git Bash での操作となります

環境構築手順はこちらで紹介しています
blog.css-net.co.jp


2. 事前準備

2-1. 作業ディレクトリ作成

エクスプローラで作業用のディレクトリを作成しておきましょう。
私は「C:\work\tutorial-app」というディレクトリを作成して、 VSCode で開きました。
この状態からスタートです。

2-2. リポジトリ作成

まずは フロントエンド、バックエンドともにソースコードを管理する Git リポジトリを作成します。
GitHub でも良いのですが、今回は CodeCommit に作成します。


マネジメントコンソールから手動で作っても良いですが、CloudFormation で作ってみましょう。
VSCode のターミナルで作業します。


作業用ディレクトリに「templates」ディレクトリを作成し、以下の内容で「01_repo_template.yaml」というファイルを作成します。
templates/01_repo_template.yaml

AWSTemplateFormatVersion: "2010-09-09"

Description:
  CodeCommit Repository

Parameters:
  BackendRepoName:
    Type: String
    Default: "tutorial-app-back"
  FrontendRepoName:
    Type: String
    Default: "tutorial-app-front"

Resources:
  BackendRepository:
    Type: AWS::CodeCommit::Repository
    Properties: 
      RepositoryName: !Ref BackendRepoName
  FrontendRepository:
    Type: AWS::CodeCommit::Repository
    Properties: 
      RepositoryName: !Ref FrontendRepoName

Outputs:
  BackendHttpsUrl:
    Description: The URL to use for cloning the repository over HTTPS.
    Value: !GetAtt BackendRepository.CloneUrlHttp
    Export:
      Name: !Sub "${AWS::StackName}-Backend-HttpsUrl"
  BackendSshUrl:
    Description: The URL to use for cloning the repository over SSH.
    Value: !GetAtt BackendRepository.CloneUrlSsh
    Export:
      Name: !Sub "${AWS::StackName}-Backend-SSHUrl"
  FrontendHttpsUrl:
    Description: The URL to use for cloning the repository over HTTPS.
    Value: !GetAtt FrontendRepository.CloneUrlHttp
    Export:
      Name: !Sub "${AWS::StackName}-Frontend-HttpsUrl"
  FrontendSshUrl:
    Description: The URL to use for cloning the repository over SSH.
    Value: !GetAtt FrontendRepository.CloneUrlSsh
    Export:
      Name: !Sub "${AWS::StackName}-Frontend-SSHUrl"


バックエンド用に「tutorial-app-back」、フロントエンド用に「tutorial-app-front」という名前でリポジトリを作成するテンプレートです。


templates ディレクトリに移動して、デプロイコマンドを実行します。

cd templates
TEMPLATE_NAME="01_repo_template.yaml"
REPO_STACK_NAME="tutorial-app-repo-stack"

BACKEND_REPO_NAME="tutorial-app-back"
FRONTEND_REPO_NAME="tutorial-app-front"

aws cloudformation deploy --template ./${TEMPLATE_NAME} \
--stack-name ${REPO_STACK_NAME} \
--parameter-overrides \
  BackendRepoName=${BACKEND_REPO_NAME} \
  FrontendRepoName=${FRONTEND_REPO_NAME}


マネジメントコンソールで作業します。
以下のものができあがっていることを確認しましょう。

◆CloudFormation -> スタック
「tutorial-app-repo-stack」という名前の Stack

◆CodeCommit -> ソース -> リポジトリ
tutorial-app-back」「tutorial-app-front」という名前のリポジトリ


今後の設定のために、リポジトリの HTTPS パスを取得します。

◆CodeCommit -> ソース -> リポジトリ -> <対象リポジトリ名> -> URLのクローン -> HTTPSのクローン

※ back / front それぞれコピーして、メモしておきましょう


または、 CloudFormation からも確認できます。
◆CloudFormation -> スタック -> tutorial-app-repo-stack -> 出力
BackendHttpsUrl / FrontendHttpsUrl の値が HTTPS パスです。

2-3. ターミナルで git 設定

ターミナルで git 用の設定をしておきましょう。
以下を参考に進めていきます。
docs.aws.amazon.com

VSCode のターミナルで作業します。

以下のコマンドを実行して、 CodeCommit への認証に AWS の認証情報を使用する設定をします。

git config --global credential.helper '!aws codecommit credential-helper $@'
# aws configure で profile 指定をしている場合はこちら
# git config --global credential.helper '!aws codecommit credential-helper --profile <Profile名> $@'
git config --global credential.UseHttpPath true


※複数の AWS アカウントを利用する可能性がある場合は以下を指定してリポジトリごとの設定にしておきます。

git config --global credential.<HTTPSパス>.helper '!aws codecommit credential-helper $@'
# aws configure で profile 指定をしている場合はこちら
# git config --global credential.<HTTPSパス>.helper '!aws codecommit credential-helper --profile <Profile名> $@'
git config --global credential.<HTTPSパス>.UseHttpPath true


以下のコマンドで、 git を利用する際のユーザ情報をセットしておきます。

USER_NAME="<ユーザ名(Taro Hayashi など)>"
USER_MAIL="<メールアドレス>"

git config --global user.name "${USER_NAME}"
git config --global user.email ${USER_MAIL}


参考
AWS CodeCommit リポジトリを作成する - AWS CodeCommit
AWS::CodeCommit::Repository - AWS CloudFormation
AWS CloudFormation で CodeCommit リソースを作成する - AWS CodeCommit
変換を伴うテンプレートの迅速なデプロイ - AWS CloudFormation

3. バックエンド API

3-1. API 作成

VSCode のターミナルで作業します。
作業用ディレクトリ(C:\work\tutorial-app)にいる前提で進めていきます。
ターミナルで以下コマンド(現在位置を出力)を打って、「/c/work/tutorial-app」であることを確認してください。

pwd

※「/c/work/tutorial-app/templates」にいる場合は「cd ../」で1階層上に戻ってください。


以下のコマンドで、 SAM のテンプレートに従ってプロジェクトを作成していきます

sam init

選択肢は以下のものを選んでみましょう。

  • Which template source would you like to use?
    • 1 - AWS Quick Start Templates
  • Choose an AWS Quick Start application template
    • 3 - Serverless API
  • Which runtime would you like to use?
    • 3 - nodejs16.x
  • Would you like to enable X-Ray tracing on the function(s) in your application? [y/N]:
    • n
    • ※ X-Ray は Lambda の監視をするための設定ですが、今回は設定しないようにします。
  • Project name [sam-app]:
    • tutorial-app-back


以下のような結果になります。

$ sam init

You can preselect a particular runtime or package type when using the `sam init` experience.
Call `sam init --help` to learn more.

Which template source would you like to use?
        1 - AWS Quick Start Templates
        2 - Custom Template Location
Choice: 1

Choose an AWS Quick Start application template
        1 - Hello World Example
        2 - Multi-step workflow
        3 - Serverless API
        4 - Scheduled task
        5 - Standalone function
        6 - Data processing
        7 - Infrastructure event management
        8 - Machine Learning
Template: 3

Which runtime would you like to use?
        1 - dotnet6
        2 - dotnetcore3.1
        3 - nodejs16.x
        4 - nodejs14.x
        5 - nodejs12.x
        6 - python3.9
        7 - python3.8
Runtime: 3

Based on your selections, the only Package type available is Zip.
We will proceed to selecting the Package type as Zip.

Based on your selections, the only dependency manager available is npm.
We will proceed copying the template using npm.

Would you like to enable X-Ray tracing on the function(s) in your application?  [y/N]: n

Project name [sam-app]: tutorial-app-back

Cloning from https://github.com/aws/aws-sam-cli-app-templates (process may take a moment)

    -----------------------
    Generating application:
    -----------------------
    Name: tutorial-app-back
    Runtime: nodejs16.x
    Architectures: x86_64
    Dependency Manager: npm
    Application Template: quick-start-web
    Output Directory: .

    Next steps can be found in the README file at ./tutorial-app-back/README.md


    Commands you can use next
    =========================
    [*] Create pipeline: cd tutorial-app-back && sam pipeline init --bootstrap
    [*] Validate SAM template: sam validate
    [*] Test Function in the Cloud: sam sync --stack-name {stack-name} --watch


最新の SAM では以下のエラーが出る方がいらっしゃるかと思います。
これは Windows のロングパス設定によるもので、 SAM init を実行するにはこれを ON にする必要があります。

Cloning from https://github.com/aws/aws-sam-cli-app-templates (process may take a moment)Error: Unstable state when updating repo. Check that you have permissions to create/delete files in C:\Users\xxxxx\AppData\Roaming\AWS SAM directory or file an issue at https://github.com/aws/aws-sam-cli/issues

上記エラーが発生した場合は↓の手順を実施してみてください

◆---------- SAM init エラー対応(クリックで開く) ----------◆

PowerShell を管理者モードで開きます。 VSCode からであれば、ターミナルの「+」マークの右横をクリックし、「PowerShell」を開きます。


以下のコマンドを打つと、 PowerShell が管理者モードで別ウィンドウで開きます。

Start-Process PowerShell.exe -Verb runas


管理者モードの PowerShell で以下のコマンドを実行すると、 Windows のロングパスの設定を ON にすることができます。

New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" `
>> -Name "LongPathsEnabled" -Value 1 -PropertyType DWORD -Force

※レジストリエディタで「HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem」の「LongPathsEnabled」を 1 にしても OK です


参考
Maximum Path Length Limitation - Win32 apps | Microsoft Learn


この設定が終わったら、あらためて「SAM init」を実行してみてください。

◆---------- SAM init エラー対応 ここまで ----------◆


3-2. API デプロイ

VSCode のターミナルで作業します。

無事にコマンドが終了すると、プロジェクトのディレクトリが作成されます。
以下のコマンドで、ビルドとデプロイをしてみましょう。

# /c/work/tutorial-app/ で実行
cd tutorial-app-back
# ビルド
sam build


ここで、Lambda 用のライブラリのインストールやパッケージ化が行われるので、少し時間がかかります。

ビルドが終わったら、いよいよデプロイしましょう。

# デプロイ
sam deploy --guided


選択肢は以下のものを選んでみましょう。

  • Stack Name [sam-app]:
    • tutorial-app-back-stack
    • ※CloudFormation で作成されるスタック名です
  • AWS Region [ap-northeast-1]:
    • そのまま Enter
    • ※デプロイするリージョンです。そのまま「ap-northeast-1(東京)」で良いので Enter です
  • Confirm changes before deploy [y/N]:
    • n
    • ※deploy 前に変更を確認するアクションを入れるかどうかです。ひとまず No としておきます
  • Allow SAM CLI IAM role creation [Y/n]:
    • y
    • ※IAM Role を作成することを許可するかどうかです。作成したいので Yes です
  • Disable rollback [y/N]:
    • n
    • ※ロールバックを許可しない設定です。失敗したらロールバックさせたいので No にします
  • getAllItemsFunction may not have authorization defined, Is this okay? [y/N]:
    • y
    • ※ここから 3 つは、それぞれの API に認証がついていないが良いか?というものです。後でつけるので、今は Yes にしておきます
  • getByIdFunction may not have authorization defined, Is this okay? [y/N]:
    • y
  • putItemFunction may not have authorization defined, Is this okay? [y/N]:
    • y
  • Save arguments to configuration file [Y/n]:
    • y
    • ※今回の設定を設定ファイルに保存するかどうか。保存するので Yes です
  • SAM configuration file [samconfig.toml]:
    • そのまま Enter
    • ※設定ファイルの名前です。デフォルトの「samconfig.toml」でよいのでそのまま Enter です
  • SAM configuration environment [default]:
    • dev
    • ※今回のデプロイ設定の環境名です。default のままでもよいですが、ここは「dev」としておきます


以下のような結果になるはずです。

$ sam deploy --guided

Configuring SAM deploy
======================

        Looking for config file [samconfig.toml] :  Not found

        Setting default arguments for 'sam deploy'
        =========================================
        Stack Name [sam-app]: tutorial-app-back-stack
        AWS Region [ap-northeast-1]: 
        #Shows you resources changes to be deployed and require a 'Y' to initiate deploy
        Confirm changes before deploy [y/N]: n
        #SAM needs permission to be able to create roles to connect to the resources in your template
        Allow SAM CLI IAM role creation [Y/n]: y
        #Preserves the state of previously provisioned resources when an operation fails
        Disable rollback [y/N]: n
        getAllItemsFunction may not have authorization defined, Is this okay? [y/N]: y
        getByIdFunction may not have authorization defined, Is this okay? [y/N]: y
        putItemFunction may not have authorization defined, Is this okay? [y/N]: y
        Save arguments to configuration file [Y/n]: y
        SAM configuration file [samconfig.toml]: 
        SAM configuration environment [default]: dev

########## 中略 ##########

Successfully created/updated stack - tutorial-app-back-stack in ap-northeast-1


マネジメントコンソールで作業します。
以下のものができあがっていることを確認しましょう。

◆CloudFormation -> スタック
「tutorial-app-back-stack」という名前の Stack

◆DynamoDB -> テーブル
「tutorial-app-back-stack-SampleTable-<ランダム文字列>」という名前のテーブル

◆API Gateway -> API
「tutorial-app-back-stack」という名前の API

◆Lambda -> アプリケーション
「tutorial-app-back-stack」という名前のアプリケーション

◆Lambda -> 関数
「tutorial-app-back-stack-getAllItemsFunction-<ランダム文字列>」
「tutorial-app-back-stack-getByIdFunction-<ランダム文字列>」
「tutorial-app-back-stack-putItemFunction-<ランダム文字列>」
という名前の関数

◆IAM -> Role
「tutorial-app-back-stack-getAllItemsFunctionRole-<ランダム文字列>」
「tutorial-app-back-stack-getByIdFunctionRole-<ランダム文字列>」
「tutorial-app-back-stack-putItemFunctionRole-<ランダム文字列>」
という名前のロール


このアカウント・リージョンで初めてデプロイする場合は、デプロイ用の S3 バケットも作成されているはずです。
このバケットに、デプロイに必要なものがアップロードされます。

◆CloudFormation -> スタック
「aws-sam-cli-managed-default」という名前の Stack

◆S3 -> バケット
「aws-sam-cli-managed-default-samclisourcebucket-<ランダム文字列>」という名前のバケット

3-3. API 動作確認

デプロイした API の動作確認をしてみましょう。
マネジメントコンソールで作業します。

◆API Gateway -> API -> tutorial-app-back-stack

GET -> テスト をクリックします。


「テスト」ボタンをクリックすると、「tutorial-app-back-stack-getAllItemsFunction-<ランダム文字列>」関数が実行されて、結果が取得されます。
※まだ DynamoDB にデータが入っていないので、空([])です


今度は POST -> テスト をクリックします


リクエスト本文に以下を入力し「テスト」ボタンをクリックすると、「tutorial-app-back-stack-putItemFunction-<ランダム文字列>」関数が実行されて、結果が取得されます。

※リクエスト本文

{
    "id": "id1",
    "name": "name1"
}


DynamoDB にもデータが登録されているかどうか確認しましょう。

◆DynamoDB -> テーブル -> tutorial-app-back-stack-SampleTable-<ランダム文字列>
テーブルアイテムの検索 をクリックします。


データが登録されていることが確認できます。


もう一度 GET でテストをして、今度はデータが返ってくることを確認します。


※Postman などを利用してデータの登録 / 確認をすることもできます
www.postman.com



API を利用してデータが登録/取得できることを確認できました。

3-4. リポジトリに登録

ここまでのデータを最初に作成したリポジトリに Push しておきましょう。
VSCode のターミナルで作業します。
バックエンド用ディレクトリ(C:\work\tutorial-app\tutorial-app-back)にいる前提で進めていきます。

以下のコマンドを実行して git リポジトリとして初期化します。

# /c/work/tutorial-app/tutorial-app-back/ で実行
git init


SAM で作成したファイルのうち、 git 管理から外したいファイルを「.gitignore」ファイルに追記します。
以下のコマンドを実行してください。

echo .aws-sam/ >> .gitignore
echo samconfig.toml >> .gitignore


以下のコマンドで、管理対象ファイルを確認します。

git status


.aws-sam/ と samconfig.toml が入っていないことを確認して、以下コマンドで全てを Add / Commit します

git add .
git commit -m "first commit"


以下のコマンドで、ローカルリポジトリとリモートリポジトリを紐づけます

git remote add origin https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/tutorial-app-back

※「https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/tutorial-app-back」の部分は、 リポジトリ作成時に確認した back 用のリポジトリの HTTPS パスを指定してください


長らく git のメインブランチは master でしたが、今後は main という名前を主流とすることになっています。
参考
The default branch for newly-created repositories is now main | GitHub Changelog


デフォルトだとブランチが「master」になっていますので、以下のコマンドで「main」に変えておきましょう。

git branch -m main


以下のコマンドで、リモートリポジトリに Push します。

git push origin main


マネジメントコンソールで作業します。
ファイルがアップされていることを確認します。
◆CodeCommit -> ソース -> リポジトリ -> tutorial-app-back

3-5. API 設定を分離

この後認証設定などをやりやすくするため、 API の作りをデフォルトから少し変えておきます。
VSCode のターミナルで作業します。
バックエンド用ディレクトリ(C:\work\tutorial-app\tutorial-app-back)にいる前提で進めていきます。


template.yaml を修正します。

  • getAllItemsFunction / getByIdFunction / putItemFunction それぞれの Events の最後に 2 行「RestApiId」設定を追加する
  • SampleTable の後、Outputs の前に API Gateway の設定を追加
  • Outputs の WebEndpoint の値を変更

template.yaml

# getAllItemsFunction / getByIdFunction / putItemFunction
# それぞれの Events の最後に 2 行追加する
  getAllItemsFunction:
    Type: AWS::Serverless::Function
    Properties:
# ~ 略 ~
      Events:
        Api:
          Type: Api
          Properties:
            Path: /
            Method: GET
            RestApiId:    # 追加
              Ref: SampleApi    # 追加

# ~ 略 ~

  SampleTable:
    Type: AWS::Serverless::SimpleTable
    Properties:
      PrimaryKey:
        Name: id
        Type: String
      ProvisionedThroughput:
        ReadCapacityUnits: 2
        WriteCapacityUnits: 2

# ↓ ----- 追加 ----- ↓
  # --------------------
  # API Gateway
  # --------------------
  SampleApi:
    Type: AWS::Serverless::Api
    Properties:
      Name: "tutorial-app-api"
      StageName: dev
      OpenApiVersion: 3.0.2
      EndpointConfiguration: REGIONAL
# ↑ ----- 追加 ----- ↑

Outputs:
  WebEndpoint:
    Description: "API Gateway endpoint URL for dev stage"    # 変更
    Value: !Sub "https://${SampleApi}.execute-api.${AWS::Region}.amazonaws.com/dev"    # 変更


template.yaml の変更が終わったら、ビルド/デプロイをします。

sam build
sam deploy --config-env dev

さきほど sam deploy --guided でデプロイした際の「SAM configuration environment」で設定した環境名を引数に入れることで、その時作成した設定ファイルでデプロイされます。
※環境名を default で作成した場合は「sam deploy」のみで OK

3-6. 修正後のバックエンド API 動作確認

デプロイが完了したら、API Gateway の画面で動作確認をしておきましょう。
マネジメントコンソールで作業します。

◆API Gateway -> API
「tutorial-app-api」という名前の API を開きます。(最初の API とは名前が変わっていますので注意)

POST -> テスト をクリックして、リクエスト本文に以下を入力し、「テスト」ボタンをクリックしてデータを登録しましょう。
※リクエスト本文

{
    "id": "id2",
    "name": "name2"
}


次に、 GET -> テスト -> テスト をクリックして、結果が返ってくることを確認しましょう。


DynamoDB にもデータが登録されているかどうか確認しましょう。
◆DynamoDB -> テーブル -> tutorial-app-back-stack-SampleTable-<ランダム文字列>
テーブルアイテムの検索 をクリックして、データが登録されていることを確認します。


修正後の API でもデータが登録/参照できることが確認できました。


ここまでをリポジトリに Push しておきましょう。

git add template.yaml
git commit -m "API を共通化"
git push origin main

バックエンドの API 実装が完了しました。
次回はフロントエンドを実装してきます。

4. 参考

AWS CloudFormation(テンプレートを使ったリソースのモデル化と管理)| AWS
AWS サーバーレスアプリケーションモデル - アマゾン ウェブ サービス