7
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Go, AWS】低予算APIサーバー構築を最初から最後まで【Golang, Lambda, DynamoDB, API Gateway, Cognito】

Posted at
  • Go -> Python差し替え編
  • クライアント(Vue.js)編

執筆中です。

価格・安全性など重要事項は随時ご自分で調べ、自己責任で作業をしてください。
筆者はいかなる損害について責任を負いかねます。

はじめに

低予算(規模が十分小さければ無料)でREST APIサーバーを作りましょう。

DBあるよ!

概要

まず、低予算で推奨されるのが サーバーレス です。
厳密性を無視して簡単に言うと、 常時待機するのではなく、必要な時だけ動作するサーバー というイメージです。低予算でいける理由がこれでわかりますね。

思ったよりやること、というよりかは扱わないといけない機能が多いです。

AWS_Rest.drawio (1).png

  • Cognito
    ユーザー管理を全部代行してくれる神機能の王、神機能キングです。
    ログイン画面 のフロントエンドすら用意してくれます。めっちゃ安いというかだいたい無料です。

    もしユーザー認証が必要なければ、Cognitoを使わず、一個下に書いてあるAPI Gatewayで全員共通のAPIキーを作り、それで認証することもできます。

  • API Gateway
    その名の通り、API通信の「門」です。
    アクセス負荷, アクセス制限, バリデーション など防衛的なお話はだいたいここの機能で行います。

  • AWS Lambda
    サーバーレスAWSの肝要です
    Lambdaという名の通り、ラムダ関数を用意するというコンセプトです。
    GoなりPythonなりC#なり各々の言語で、

    • 引数: リクエスト
    • 返り値: レスポンス

    の関数を作るというわけです。

  • Dynamo DB
    AWSが用意しているDBの中で唯一無料枠が提供されている種類です。

    非リレーショナルで、データはJSONで扱われ、SQLは使えません。

実装

Lambda関数をGolangで書く

中級以上のプログラマー向けにコーディングの概形や雰囲気を把握してもらうための記述をしていまして、各記述の詳細は各自お調べください。

まずは、通常のGolang書きと同じように、モジュール作るべくgo mod initなどをしてください。

POSTリクエストを受け取り、レスポンスを返す

下記のパッケージが必要になります。

go get "github.com/aws/aws-lambda-go/events"
go get "github.com/aws/aws-lambda-go/lambda"

まず、大枠から。

main.go
package main

import (
	"github.com/aws/aws-lambda-go/events"
	"github.com/aws/aws-lambda-go/lambda"
)

//リクエストJSON
type Request struct {
	vari   string `json:"vari"`
}

//レスポンスJSON
type Response struct {
	vari   string `json:"vari"`
}

func requestHandler(ctx context.Context, req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
	// リクエストを受け取る
	request := Request{}
	err := json.Unmarshal([]byte(req.Body), &request)
	if err != nil {
		//エラーログ
		fmt.Println("Error parsing request: ", err, "; ", req.Body)	

      //HTTPステータス400のレスポンスを返す
		return events.APIGatewayProxyResponse{StatusCode: 400}, fmt.Errorf("error parsing request: %v", err)
	}

     //...いろいろな、処理...

	// レスポンス作成
	response := Response{Token: token}
	responseJson, err := json.Marshal(response)
	if err != nil {
		// エラーログ
		fmt.Println("Error marshalling response: ", err)

          //HTTPステータス500のレスポンスを返す
		return events.APIGatewayProxyResponse{StatusCode: 500}, fmt.Errorf("error marshalling response: %v", err)
	}
	return events.APIGatewayProxyResponse{StatusCode: 200, Body: string(responseJson)}, nil
}

func main() {
	lambda.Start(requestHandler)
}

こうやってリクエストを受け取り、レスポンスを返します。(不親切な説明)

CognitoユーザーID取得

もしCognitoのユーザーIDを取得したい場合は、

CognitoIdGetter.go
func getUserId(req events.APIGatewayProxyRequest) (string, error) {
	cognitoAttributes, ok := req.RequestContext.Authorizer["claims"].(map[string]interface{})
	if !ok {
		return "", fmt.Errorf("couldn't get cognito attributes")
	}
	
	userId, ok := cognitoAttributes["sub"].(string)

	if !ok {
		return "", fmt.Errorf("couldn't get userId")
	} else {
		return userId, nil
	}
}

ちょっと大変ですがこんな感じ。
(要するにAuthorizer -> claims -> subからCognito IDを取得できます)

Dynamo DBとのデータ出し入れ

github.com/guregu/dynamoを使うととても便利です。

こちら参照(パッケージ作成者本人による解説記事): 気楽にDynamoDBを使おう

Zipにする

AWS Lambdaに投稿するには、zipにしなければなりません。

筆者はWindowsのPower Shellを使っておりますが、こちらの呪文をコピペして

cd (main.goがあるディレクトリ)
$env:GOOS = "linux"
$env:GOARCH = "amd64"
$env:CGO_ENABLED = "0"
go build -o bootstrap main.go

当該ディレクトリに生成されたbootstrapファイル 一つだけ.zipに圧縮しています。
このzipファイルをAWS Lambdaにアップロードします


いよいよマウスでカチカチするノーコードタイムです。この設定作業が楽しい人がいるか分かりませんが、プログラミングだけが好きな筆者にとっては苦痛ですね。

もちろんですが、AWSのユーザー登録をして、AWSコンソールにアクセスしてくださいね。

Cognito設定

ユーザープール を作ります。IDプールは使えなかったです(泣)
image.png

サインインオプション。ログイン画面で入力するアレを指定できます。
image.png

次のステップに移りましてセキュリティ要件。
デフォルトのパスワード要件がけっこうガッチリしていますので、必要にあわせて調整。
二段階認証の可否も必要に合わせて調整(デフォルトは「必須」になっています)
image.png

次のステップに移りましてサインアップエクスペリエンス

  • 自己登録とは ユーザー登録機能 を指します。 オフにすると、管理者が一人ひとり手動で登録する、という仕様になります
  • 検証と確認 とは、ユーザー登録の際のメアド認証ないし電話番号認証のメールを飛ばすやつです。必要に応じて。
    image.png

次のステップに移りましてアプリケーション設定

  • ホストされたUICognitoがログイン画面を代わりに提供してくれる神機能 です。後述参照。
  • 身内だけでユーザー管理をする場合は クライアントシークレット を設定するべきだそうですが、公開APIを作成する場合は 生成しない に設定。
    image.png

ホストされたUI にチェック入れると、こんな設定画面が。
そのCognitoドメインってのが、Cognitoが提供してくれるログイン画面になります。
image.png

これでCognitoの設定完了です。


Dynamo DB

左のメニューから「テーブル」を選んで、「テーブルの作成」
image.png

テーブル作成の設定はこれだけ。
パーティションキー は、RDBでいうところの「 主キー 」です。
まあ各データのどの項目を「ID」にするか、ということです。だいたい。
image.png

これでDB設定は終わりです。

JSON管理DBですので(?)、パーティションキー以外に 列名などの事前登録はせず、Lambda側に委ねられています


IAM

現状のままでは、 LambdaがDynamo DBを編集する権限がありません

AWSのサービス一覧に「IAM」というのがありまして、そこから設定しないといけません。

ロール」へ
image.png

ロールを作成
image.png

  • 信頼されたエンティティタイプは「AWSのサービス」(Lambdaのロールを作っているので)
  • ユースケースLambda を選択。
    image.png

具体的な権限を付与します。
「dynamo」と検索すると4つ出てきますね。

  • AmazonDynamoDBFullAcccess
  • AmazonDynamoDBExecution

を選択するとデータの出し入れができます。
image.png

これでDynamoDBデータ出し入れロールが作成できました。


Lambda

「関数を作成」を選択。

  • 「一から作成」
  • Golang の場合は、ランタイム「Amazon Linux」
  • 実行ロール は、IAMで作成したDynamoアクセスロール をセット。

image.png

Zipをアップロード。
Golangのところで作成したzipファイルをドラッグアンドドロップでアップロード。
image.png

終わりです。

テストしたい場合は、「テスト」から テンプレート から 「apigateway-aws-proxy」を選択。
image.png

リクエストボディはどうしてかこんな感じでJSONをエスケープ付きの文字列にしないといけないようです。
文字列化はこのサイトが便利
image.png


API Gateway

ここの設定が複雑で苦痛です。

APIを作成

APIを作成
image.png

REST APIを作成
image.png

「リソース」を作成でURIの末端部分を指定できます。
話によると/ 自体にメソッド(機能)を持たせるとDDos攻撃を受けやすくなるらしいです。
image.png

Webサイトからのアクセスを想定する場合、 CORS にチェックつけるのをオススメします、というかチェックしないとCORS制限にひっかかります。
image.png

「メソッドを作成します」
OPTIONはそのまま にしましょう。それはCORSに使われるのでいじらない方がいいです。「プリフライトリクエスト」ってやつです[参考]
image.png

  • メソッドタイプ を欲しいものに選択
  • 統合タイプ はもちろん Lambda
  • Lambdaプロキシ統合 にチェック(するとGo書きで使ったパッケージが使えます)
  • 作ったLambda関数を指定
    image.png

メソッド作成はこれで完了

認証機能をつける

Cognitoの認証機能を付与します。

「オーソライザー」へ
image.png

「作成」
image.png

  • Cognitoを指定
  • ユーザープールは作成したものを指定
  • トークンのソース はよきように。Authorizationが推奨。 (リクエストのヘッダーのやつですよ!!)
    image.png

さあ「リソース」へ戻って「メソッドリクエストの設定」から「編集」
image.png

さっき作ったものを指定。
image.png

これで、リクエストヘッダーにCognitoから与えられたトークンを入れてないと弾かれる、という ユーザー認証 ができます。

バリデーション

「モデル」へ
image.png

「モデル」を作成
image.png

モデルの記法はこんな感じ。AWS 開発者ガイドより引用。

{
  "type" : "object",
  "required" : [ "name", "price", "type" ],
  "properties" : {
    "id" : {
      "type" : "integer"
    },
    "type" : {
      "type" : "string",
      "enum" : [ "dog", "cat", "fish" ]
    },
    "name" : {
      "type" : "string"
    },
    "price" : {
      "type" : "number",
      "minimum" : 25.0,
      "maximum" : 500.0
    }
  }
}

前項と同じくメソッドリクエストを編集から登録。
image.png

デプロイ

デプロイで初めて使えるようになります。
image.png

ステージは、(ドメイン)/(ステージ名)/(リソース名)というような感じでURIに現れます。
慣習的には、テスト段階ではbeta、本番ではv1と指定します。
image.png

デプロイ完了!!

URIを確認するには

「ステージ」へ
image.png

この部分です!!!
image.png

末筆

以上です。
あまり丁寧に記述しなかった点は多々ありますが(というか全部?)、情報不足などあれば積極的にコメントに書いていただければ、随時追記します。

この記事に「いいね」をくだされば、泣いて喜びます!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

7
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?