Adding the Real Database

Learn about the integration of the database with our project.

Database integration

Now we are ready to add the database to our system. First, we will update our infrastructure.

Press + to interact
// ...
Globals:
Function:
Runtime: nodejs12.x
Handler: main.handler
Environment:
Variables:
ENVIRONMENT: !Ref Environment
DATABASE_NAME: !Ref ReservationsDb
Resources:
CreateLambda:
Type: AWS::Serverless::Function
Properties:
MemorySize: 512
Timeout: 15
CodeUri: lambdas/create/dist/create.zip
Policies:
- AWSLambdaExecute
- Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- "dynamodb:*"
Resource:
- "*"
Events:
Api:
Type: Api
Properties:
Path: /create
Method: POST
RestApiId: !Ref ServerlessRestApi
RetrieveLambda:
Type: AWS::Serverless::Function
Properties:
MemorySize: 512
Timeout: 15
CodeUri: lambdas/retrieve/dist/retrieve.zip
Policies:
- AWSLambdaExecute
- Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- "dynamodb:*"
Resource:
- "*"
Events:
Api:
Type: Api
Properties:
Path: /retrieve
Method: GET
RestApiId: !Ref ServerlessRestApi
// rest of the file unchanged
```

Explanation

  • Line 10: We now add another environment variable that injects the name of the database into the Lambdas.
  • Lines 21–27 and 43–49: We give both our Lambdas permission to do anything they want with our DynamoDB databases. For better security, we can limit this to one resource (the specific database created in this file) and we can find out what DynamoDB calls we need. For example, our Create Lambda would need a PutItem permission.

Add the aws-sdk to the dev dependencies of both our Lambdas:

Press + to interact
"devDependencies": {
"@types/jest": "^26.0.14",
"aws-sdk": "2.828.0"
}

While these are provided by AWS when running a Lambda (and don’t have to be packaged with the rest of our dependencies), they’re still needed and useful for local development.

Create gateway folder

The first thing we’ll do is create a new folder called gateway, similar to the naming of our previous project. Inside it, we create a file, similar to the previous chapter, a very simple call to an external service.

Press + to interact
import {DynamoDB} from "aws-sdk";
import {DocumentClient} from "aws-sdk/lib/dynamodb/document_client";
import PutItemInput = DocumentClient.PutItemInput;
const client = new DynamoDB.DocumentClient({region: 'eu-west-1'});
export const save = (params: PutItemInput) => {
return client.put(params).promise();
};

Note: Be careful not to create the DocumentClient directly. Otherwise, TypeScript won’t be able to handle the DocumentClient type gracefully.

Now, we can rewrite our databaseTest.

import {mocked} from 'ts-jest/utils';
import {saveToDatabase} from "../src/specifics/database";
import {CreateReservationPricedRequest} from "../../../util/domain/types";
import {save} from "../src/gateway/awsCalls";
import {DocumentClient} from "aws-sdk/lib/dynamodb/document_client";
import PutItemOutput = DocumentClient.PutItemOutput;

jest.mock("../src/gateway/awsCalls");

describe('id generation', () => {
    // stays the same
});

describe('save', () => {
    beforeEach( () => {
        mocked(save as any).mockClear();
    });

    it('should save and then return an enriched event', async () => {
        const mockSave = mocked(save as any)
            .mockImplementation((): Promise<PutItemOutput> => {
                return Promise.resolve({});
            });

        const startDate = new Date(2020, 11, 10, 12, 0, 0);
        const dayLater = new Date(2020, 11, 11, 12, 0, 0);

        const exampleRequest: CreateReservationPricedRequest = {
            hotelId: '1',
            userId: '10',
            start: startDate,
            end: dayLater,
            price: 20,
            timestamp: startDate,
        };

        await saveToDatabase(exampleRequest);

        expect(mockSave.mock.calls).toHaveLength(0);
    });
});
Test case for database in databaseTest.ts file

This is similar to the kind of testing we performed ...