https://serverless-stack.com/chapters/add-a-create-note-api.html

Building a Serverless REST API

Add a Create Note API

//create.js
import uuid from "uuid";
import AWS from "aws-sdk";

const dynamoDb = new AWS.DynamoDB.DocumentClient();

export function main(event, context, callback) {
  // Request body is passed in as a JSON encoded string in 'event.body'
  const data = JSON.parse(event.body);

  const params = {
    TableName: "notes",
    // 'Item' contains the attributes of the item to be created
    // - 'userId': user identities are federated through the
    //             Cognito Identity Pool, we will use the identity id
    //             as the user id of the authenticated user
    // - 'noteId': a unique uuid
    // - 'content': parsed from request body
    // - 'attachment': parsed from request body
    // - 'createdAt': current Unix timestamp
    Item: {
      userId: event.requestContext.identity.cognitoIdentityId,
      noteId: uuid.v1(),
      content: data.content,
      attachment: data.attachment,
      createdAt: Date.now()
    }
  };

  dynamoDb.put(params, (error, data) => {
    // Set response headers to enable CORS (Cross-Origin Resource Sharing)
    const headers = {
      "Access-Control-Allow-Origin": "*",
      "Access-Control-Allow-Credentials": true
    };

    // Return status code 500 on error
    if (error) {
      const response = {
        statusCode: 500,
        headers: headers,
        body: JSON.stringify({ status: false })
      };
      callback(null, response);
      return;
    }

    // Return status code 200 and the newly created item
    const response = {
      statusCode: 200,
      headers: headers,
      body: JSON.stringify(params.Item)
    };
    callback(null, response);
  });
}

DynamoDB 테이블에 Put 액션을 실행하도록하고,
상태코드와 CORS (Cross-Origin Resource Sharing) 를 사용 하도록 HTTP 상태 코드 와 응답 헤더가 있는 새로 생성 된 노트 객체를 반환합니다 .

HTTP 서버 응답 코드 (Response Code) 정리
=>https://ooz.co.kr/260

//serverless.yml 추가되는코드
# 'iamRoleStatements' defines the permission policy for the Lambda function.
  # In this case Lambda functions are granted with permissions to access DynamoDB.
  iamRoleStatements:
    - Effect: Allow
      Action:
        - dynamodb:DescribeTable
        - dynamodb:Query
        - dynamodb:Scan
        - dynamodb:GetItem
        - dynamodb:PutItem
        - dynamodb:UpdateItem
        - dynamodb:DeleteItem
      Resource: "arn:aws:dynamodb:us-east-1:*:*"

functions:
  # Defines an HTTP API endpoint that calls the main function in create.js
  # - path: url path is /notes
  # - method: POST request
  # - cors: enabled CORS (Cross-Origin Resource Sharing) for browser cross
  #     domain api call
  # - authorizer: authenticate using the AWS IAM role
  create:
    handler: create.main
    events:
      - http:
          path: notes
          method: post
          cors: true
          authorizer: aws_iam

테스트

함수에 전달할 입력 데이터가있는 json 파일

//mocks/create-event.json
{
  "body": "{\"content\":\"hello world\",\"attachment\":\"hello.jpg\"}",
  "requestContext": {
    "identity": {
      "cognitoIdentityId": "USER-SUB-1234"
    }
  }
}

실행문

$ serverless invoke local --function create --path mocks/create-event.json

결과

Leeui-MacBook-Air:notes-app-api lee$ serverless invoke local --function create --path mocks/create-event.json
Serverless: Bundling with Webpack...
{
    "statusCode": 200,
    "headers": {
        "Access-Control-Allow-Origin": "*",
        "Access-Control-Allow-Credentials": true
    },
    "body": "{\"userId\":\"USER-SUB-1234\",\"noteId\":\"5aa3c890-af6d-11e9-81ca-0b1806a5f9cd\",\"content\":\"hello world\",\"attachment\":\"hello.jpg\",\"createdAt\":1564121981849}"
}

serverless invoke =>https://serverless.com/framework/docs/providers/aws/cli-reference/invoke/

https://github.com/jerrynim/jerrynim-serverless-stack/commit/85042a06a2ca75e2f4da0495278091adc3ed504a

Get Note API 추가

handler 추가

//get.js
import * as dynamoDbLib from "./libs/dynamodb-lib";
import { success, failure } from "./libs/response-lib";

export async function main(event, context) {
  const params = {
    TableName: "notes",
    // 'Key' defines the partition key and sort key of the item to be retrieved
    // - 'userId': Identity Pool identity id of the authenticated user
    // - 'noteId': path parameter
    Key: {
      userId: event.requestContext.identity.cognitoIdentityId,
      noteId: event.pathParameters.id
    }
  };

  try {
    const result = await dynamoDbLib.call("get", params);
    if (result.Item) {
      // Return the retrieved item
      return success(result.Item);
    } else {
      return failure({ status: false, error: "Item not found." });
    }
  } catch (e) {
    return failure({ status: false });
  }
}

serverless.yml 추가부분

 get:
    # Defines an HTTP API endpoint that calls the main function in get.js
    # - path: url path is /notes/{id}
    # - method: GET request
    handler: get.main
    events:
      - http:
          path: notes/{id}
          method: get
          cors: true
          authorizer: aws_iam

테스트

pathParameters는 이전에 생성한 noteId 입니다.

//mocks/get-event.json
{
  "pathParameters": {
    "id": "578eb840-f70f-11e6-9d1a-1359b3b22944"
  },
  "requestContext": {
    "identity": {
      "cognitoIdentityId": "USER-SUB-1234"
    }
  }
}

실행문과 결과

$ serverless invoke local --function get --path mocks/get-event.json

Leeui-MacBook-Air:notes-app-api lee$ serverless invoke local --function get --path mocks/get-event.json
Serverless: Bundling with Webpack...
{
    "statusCode": 200,
    "headers": {
        "Access-Control-Allow-Origin": "*",
        "Access-Control-Allow-Credentials": true
    },
    "body": "{\"attachment\":\"hello.jpg\",\"content\":\"hello world\",\"createdAt\":1564121981849,\"noteId\":\"5aa3c890-af6d-11e9-81ca-0b1806a5f9cd\",\"userId\":\"USER-SUB-1234\"}"
}

https://github.com/jerrynim/jerrynim-serverless-stack/commit/9581f26dbfdf5c85f169f28f94c0dc2c806f48de

같은 방식으로 Update 와 Delete 추가

-- Update 시 없던 noteid에대해서는 새로 추가가 됨

https://github.com/jerrynim/jerrynim-serverless-stack/commit/61d6d4f0b58b6cdba07611e949b98f44120bf093

API 게이트웨이 CORS 오류 처리

https://serverless-stack.com/chapters/handle-api-gateway-cors-errors.html

API를 배포하기 전에 마지막으로 설정해야합니다. CORS 헤더를 API 게이트웨이 오류에 추가해야합니다.
우리가 API 요청을 할 때, API Gateway는 Lambda 함수 전에 호출됩니다. 즉, API 게이트웨이 수준에서 오류가 발생하면 CORS 헤더가 설정되지 않습니다.
기본적으로 Serverless Framework 는 코드를 통해 API 게이트웨이 인스턴스를 구성 할 수 있도록 CloudFormation 을 지원 합니다.

//resources/api-gateway-errors.yml
Resources:
  GatewayResponseDefault4XX:
    Type: 'AWS::ApiGateway::GatewayResponse'
    Properties:
      ResponseParameters:
         gatewayresponse.header.Access-Control-Allow-Origin: "'*'"
         gatewayresponse.header.Access-Control-Allow-Headers: "'*'"
      ResponseType: DEFAULT_4XX
      RestApiId:
        Ref: 'ApiGatewayRestApi'
  GatewayResponseDefault5XX:
    Type: 'AWS::ApiGateway::GatewayResponse'
    Properties:
      ResponseParameters:
         gatewayresponse.header.Access-Control-Allow-Origin: "'*'"
         gatewayresponse.header.Access-Control-Allow-Headers: "'*'"
      ResponseType: DEFAULT_5XX
      RestApiId:
        Ref: 'ApiGatewayRestApi'

serverless.yml

# Create our resources with separate CloudFormation templates
resources:
  # API Gateway Errors
  - ${file(resources/api-gateway-errors.yml)}

https://github.com/jerrynim/jerrynim-serverless-stack/commit/130dba1f41a263dfb2ea6c3357e55b48555c74ea

API 배포

$ serverless deploy

단일 함수 배포

$ serverless deploy function -f list

Cognito user pool vs identity pool

https://serverless-stack.com/chapters/create-a-cognito-identity-pool.html