How to create and access DynamoDB tables for local development in a Serverless Framework project - all without using any plugins!
Example source code from article Using DynamoDB Locally in a Serverless Framework project.
How to create and access DynamoDB tables for local development in a Serverless Framework project - all without using any plugins!
Example source code from article Using DynamoDB Locally in a Serverless Framework project.
| const fs = require('fs') | |
| const DynamoDB = require('aws-sdk/clients/dynamodb') | |
| const yaml = require('js-yaml') | |
| const cloudformationSchema = require('@serverless/utils/cloudformation-schema') | |
| const SERVERLESS_CONFIG = __dirname + '/serverless.yml' | |
| const ddb = new DynamoDB({ | |
| accessKeyId: 'fake-key', | |
| endpoint: 'http://localhost:8001', | |
| region: 'local', | |
| secretAccessKey: 'fake-secret', | |
| }) | |
| async function getDynamoDBTableResources() { | |
| const tables = Object.entries( | |
| yaml.loadAll(fs.readFileSync(SERVERLESS_CONFIG), { | |
| schema: cloudformationSchema, | |
| })[0].resources.Resources, | |
| ).filter( | |
| ([, resource]) => | |
| resource.Type === 'AWS::DynamoDB::Table', | |
| ) | |
| return tables | |
| } | |
| ;(async function main() { | |
| console.info('Setting up local DynamoDB tables') | |
| const tables = await getDynamoDBTableResources() | |
| const existingTables = (await ddb.listTables().promise()) | |
| .TableNames | |
| for await ([logicalId, definition] of tables) { | |
| const { | |
| Properties: { | |
| BillingMode, | |
| TableName, | |
| AttributeDefinitions, | |
| KeySchema, | |
| GlobalSecondaryIndexes, | |
| LocalSecondaryIndexes, | |
| }, | |
| } = definition | |
| if ( | |
| existingTables.find((table) => table === TableName) | |
| ) { | |
| console.info( | |
| `${logicalId}: DynamoDB Local - Table already exists: ${TableName}. Skipping..`, | |
| ) | |
| continue | |
| } | |
| const result = await ddb | |
| .createTable({ | |
| AttributeDefinitions, | |
| BillingMode, | |
| KeySchema, | |
| LocalSecondaryIndexes, | |
| GlobalSecondaryIndexes, | |
| TableName, | |
| }) | |
| .promise() | |
| console.info( | |
| `${logicalId}: DynamoDB Local - Created table: ${TableName}`, | |
| ) | |
| } | |
| })() |
| version: '2.1' | |
| services: | |
| dynamodb: | |
| image: amazon/dynamodb-local:latest | |
| command: -jar DynamoDBLocal.jar -sharedDb -inMemory -port 8001 | |
| ports: | |
| - '8001:8001' | |
| hostname: dynamodb.localhost | |
| working_dir: /home/dynamodblocal |
| const DynamoDB = require('aws-sdk/clients/dynamodb') | |
| // If the code is running locally, we point the | |
| // DynamoDB client at our local instance of | |
| // DynamoDB Local, otherwise we point it at the | |
| // real AWS DynamoDB. | |
| const ddbClient = new DynamoDB.DocumentClient({ | |
| service: | |
| typeof process.env.AWS_ACCESS_KEY_ID === 'undefined' | |
| ? new DynamoDB({ | |
| accessKeyId: 'fake-key', | |
| endpoint: 'http://localhost:1338', | |
| region: 'local', | |
| secretAccessKey: 'fake-secret', | |
| }) | |
| : new DynamoDB(), | |
| }) | |
| module.exports.handler = async function handler(event) { | |
| const { pk, id } = event | |
| const result = await ddbClient | |
| .get({ | |
| TableName: process.env.DYNAMODB_TABLE, | |
| Key: { pk, id }, | |
| }) | |
| .promise() | |
| return result.Item | |
| } |
| const DynamoDB = require('aws-sdk/clients/dynamodb') | |
| const { nanoid } = require('nanoid') | |
| const { handler } = require('./example') | |
| process.env.DYNAMODB_TABLE = 'example' | |
| const ddb = new DynamoDB.DocumentClient({ | |
| service: new DynamoDB({ | |
| accessKeyId: 'fake-key', | |
| endpoint: 'http://localhost:8001', | |
| region: 'local', | |
| secretAccessKey: 'fake-secret', | |
| }), | |
| }) | |
| describe('example handler', () => { | |
| it('should return an item from DynamoDB specified by key', async () => { | |
| const testItem = { | |
| pk: 'test-pk-' + nanoid(), | |
| sk: 'test-sk-' + nanoid(), | |
| } | |
| await ddb | |
| .put({ | |
| TableName: process.env.DYNAMODB_TABLE, | |
| Item: testItem, | |
| }) | |
| .promise() | |
| const result = await handler(testItem) | |
| expect(result).toMatchObject(testItem) | |
| }) | |
| }) |
| { | |
| "name": "example", | |
| "scripts": { | |
| "setup:dynamodb": "node create-tables-locally.js", | |
| "up": "docker-compose up -d", | |
| "postup": "npm run setup:dynamodb", | |
| "down": "docker-compose down", | |
| "pretest": "npm run up", | |
| "test": "jest --passWithNoTests", | |
| "posttest": "npm run down", | |
| "pretest:watch": "npm run up", | |
| "test:watch": "jest --watch" | |
| }, | |
| "dependencies": { | |
| "aws-sdk": "2.906.0" | |
| }, | |
| "devDependencies": { | |
| "jest": "27.0.6", | |
| "js-yaml": "4.1.0", | |
| "nanoid": "3.1.23", | |
| "serverless": "2.55.0" | |
| } | |
| } |
| service: example | |
| provider: | |
| name: aws | |
| runtime: nodejs14.x | |
| functions: | |
| example: | |
| description: An example lambda function | |
| handler: example.handler | |
| memorySize: 256 | |
| role: LambdaRole | |
| environment: | |
| DYNAMODB_TABLE: !Ref DynamoDBExampleTable | |
| resources: | |
| Resources: | |
| DynamoDBExampleTable: | |
| Type: AWS::DynamoDB::Table | |
| Properties: | |
| TableName: example | |
| SSESpecification: | |
| SSEEnabled: true | |
| BillingMode: PAY_PER_REQUEST | |
| AttributeDefinitions: | |
| - AttributeName: pk | |
| AttributeType: S | |
| - AttributeName: sk | |
| AttributeType: S | |
| - AttributeName: sk2 | |
| AttributeType: S | |
| KeySchema: | |
| - AttributeName: pk | |
| KeyType: HASH | |
| - AttributeName: sk | |
| KeyType: RANGE | |
| GlobalSecondaryIndexes: | |
| - IndexName: pk-sk2 | |
| KeySchema: | |
| - AttributeName: pk | |
| KeyType: HASH | |
| - AttributeName: sk2 | |
| KeyType: RANGE | |
| Projection: | |
| ProjectionType: ALL | |
| LambdaRole: | |
| Type: AWS::IAM::Role | |
| Properties: | |
| AssumeRolePolicyDocument: | |
| Statement: | |
| - Effect: Allow | |
| Principal: | |
| Service: lambda.amazonaws.com | |
| Action: sts:AssumeRole | |
| ManagedPolicyArns: | |
| - arn:aws:iam::aws:policy/AWSXrayWriteOnlyAccess | |
| - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole | |
| Policies: | |
| - PolicyName: dynamodb | |
| PolicyDocument: | |
| Statement: | |
| - Effect: Allow | |
| Action: | |
| - 'dynamodb:BatchGetItem' | |
| - 'dynamodb:BatchWriteItem' | |
| - 'dynamodb:PutItem' | |
| - 'dynamodb:DeleteItem' | |
| - 'dynamodb:GetItem' | |
| - 'dynamodb:Scan' | |
| - 'dynamodb:Query' | |
| - 'dynamodb:UpdateItem' | |
| - 'dynamodb:PartiQL*' | |
| Resource: | |
| - !GetAtt DynamoDBExampleTable.Arn | |
| - !Sub | |
| - ${Table}/* | |
| - Table: !GetAtt DynamoDBExampleTable.Arn |