serverless教程六:使用serverless开发DynamoDB API


在开发无服务器应用的时候,通过纯手工方式创建各种资源的话,效率会非常低,因此需要借助一些框架来提高开发效率。下面介绍如何在serverless中开发DynamoDB API(Lambda)。

serverless教程
serverless教程

使用serverless添加Query方法

访问DynamoDB的公共方法

首先增加文件api/common/dynamo/Dynamo.js

Javascript
const AWS = require('aws-sdk');
const documentClient = new AWS.DynamoDB.DocumentClient();

const Dynamo = {
    async query(params) {
        const data = await documentClient
                        .query(params)
                        .promise()
          
        if (!data || !data.Items) {
            throw Error(`Error occured while fetching the data`);
        }                        
        return data.Items;
    }
}

module.exports = Dynamo;

增加具体的Lambda方法

增加文件api/scores/getScores.js:

Javascript
const Responses = require('../common/Responses');
const Dynamo = require('../common/dynamo/Dynamo');

exports.handler = async event => {
    // 从serverless.yml定义中获取tableName
    const tableName = process.env.tableName;

    if (!event.pathParameters || !event.pathParameters.studentID) {
        return Responses.HTTP_400({message: 'missing the studentID from the URL.'});
    }
    
    let studentID = event.pathParameters.studentID;
    const params = { 
        KeyConditionExpression: 'StudentID = :studentId', 
        ExpressionAttributeValues: { ':studentId': studentID}, 
        TableName: tableName 
    };
    
    const scores = await Dynamo.query(params).catch(err => {
        console.log("error while getting scores from DynamoDB", err);
        return null;
    })

    if(!scores) {
        return Responses.HTTP_400({message: "Failed to find scores by studentID."});
    }

    return Responses.HTTP_200({message: scores})    
}

如果同时使用PK和SK进行查询的话,可以更改params:

Javascript
const params = { 
    KeyConditionExpression: 'StudentID = :studentId AND Subject = :subject', 
    ExpressionAttributeValues: { ':studentId': studentID, ':subject': 'Math' }, 
    TableName: tableName 
};

serverless.yml中的定义

需要注意,在定义DynamoDB访问权限的时候,不仅要定义对表各种操作的权限,还需要定义对index的权限,否则会报错

yaml
- Effect: Allow
  Action:
    - dynamodb:Query
    - dynamodb:Scan
  Resource: "arn:aws:dynamodb:${self:provider.region}:*:table/${self:provider.environment.tableName}/index/*"

完整代码:

yaml
service: service-05-api-dynamodb

provider:
  name: aws
  region: eu-west-1
  runtime: nodejs14.x
  lambdaHashingVersion: 20201221
  profile: default
  environment:
    tableName: ${self:custom.tableName}
  iamRoleStatements:
    - Effect: Allow
      Action:
        - dynamodb:Query
        - dynamodb:Scan
        - dynamodb:GetItem
        - dynamodb:PutItem
        - dynamodb:UpdateItem
        - dynamodb:DeleteItem
      Resource: "arn:aws:dynamodb:${self:provider.region}:*:table/${self:provider.environment.tableName}"
    - Effect: Allow
      Action:
        - dynamodb:Query
        - dynamodb:Scan
      Resource: "arn:aws:dynamodb:${self:provider.region}:*:table/${self:provider.environment.tableName}/index/*"

custom:
  tableName: student-scores

resources:  
  Resources:
    MyDynamoDbTable:
      Type: AWS::DynamoDB::Table
      Properties:
        TableName: ${self:custom.tableName}
        AttributeDefinitions:
          - AttributeName: StudentID
            AttributeType: S
          - AttributeName: Subject
            AttributeType: S  
          - AttributeName: Date
            AttributeType: S 
        KeySchema: 
          - AttributeName: StudentID
            KeyType: HASH
          - AttributeName: Subject
            KeyType: RANGE  
        GlobalSecondaryIndexes:
          - IndexName: Dates
            KeySchema:
              - AttributeName: Date
                KeyType: HASH
              - AttributeName: Subject
                KeyType: RANGE
            Projection:
              ProjectionType: ALL    
        BillingMode: PAY_PER_REQUEST

functions:
  getScores:
    handler: api/scores/getScores.handler
    events:
      - http:
          path: get-scores/{studentID}
          method: GET
          cors: true

plugins:
  - serverless-webpack
  
package:
  individually: true

测试

发布

bash
sls deploy

测试get-scores

然后直接访问这样的网址就可以获取数据了:

https://xxxxxx.execute-api.eu-west-1.amazonaws.com/dev/get-scores/9901

使用serverless对DynamoDB写入数据

更新api/common/dynamo/Dynamo.js

首先增加对DynamoDB进行写入的方法:

Javascript
async insert (params) {
    const res = await documentClient.put(params).promise();

    if(!res) {
        throw Error(`There was an error while inserting the data`);
    }

    return params.Item;
}

增加api/scores/insertScore.js

接下来创建文件:api/scores/insertScore.js:

Javascript
const Responses = require('../common/Responses');
const Dynamo = require('../common/dynamo/Dynamo');

const tableName = process.env.tableName;

exports.handler = async event => {
    const score = JSON.parse(event.body);

    let params = { 
        TableName: tableName, 
        Item: { 
            "StudentID": score.studentId, 
            "Subject": score.subject, 
            "Score": score.score, 
            "Date": score.date 
        } 
    };

    const newScore = await Dynamo.insert(params).catch(err => {
        console.log('error in dynamo insert', err);
        return null;
    });

    console.log(newScore);

    if (!newScore) {
        return Responses.HTTP_400({ message: 'Failed to insert data' });
    }

    return Responses.HTTP_200({ newScore });
};

这里要特别注意,代码中的字段名字一定要和DynamoDB中的一致,包括大小写,否则会出错。

更新serverless.yml

yaml
functions:
  insertScore:
    handler: api/scores/insertScore.handler
    events:
      - http:
          path: new-score
          method: POST
          cors: true   

测试

重新部署:

bash
sls deploy

接下来可以使用类似Postman的工具测试。也可以使用这个在线工具进行测试:https://reqbin.com/

serverless教程
serverless教程


文章作者: 逻思
版权声明: 本博客所有文章除特別声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 逻思 !