DynamoDB Local
Amazon DynamoDB
Node.js
Database Development
AWS SDK

How I can work with Amazon's Dynamodb Local in Node?

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

DynamoDB Local is a downloadable version of Amazon DynamoDB that runs on your machine, allowing you to develop and test applications without connecting to AWS or incurring costs. In Node.js, you connect to DynamoDB Local by configuring the AWS SDK with a custom endpoint pointing to http://localhost:8000. The rest of the API is identical to production DynamoDB — same methods, same parameters, same response formats. This makes it easy to develop locally and deploy to AWS with only an endpoint configuration change.

Setting Up DynamoDB Local

bash
1# Option 1: Download the JAR
2# Download from https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.html
3java -Djava.library.path=./DynamoDBLocal_lib -jar DynamoDBLocal.jar -sharedDb -port 8000
4
5# Option 2: Docker (recommended)
6docker run -d --name dynamodb-local \
7  -p 8000:8000 \
8  amazon/dynamodb-local:latest \
9  -jar DynamoDBLocal.jar -sharedDb
10
11# Verify it's running
12curl http://localhost:8000
13# Should return: {"__type":"com.amazonaws.dynamodb.v20120810#MissingAuthenticationToken"...}

The -sharedDb flag uses a single database file for all regions and credentials, simplifying local development. Without it, each credentials/region combination gets a separate database.

Connecting with AWS SDK v3

javascript
1// AWS SDK v3 (recommended for new projects)
2import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
3import { DynamoDBDocumentClient, PutCommand, GetCommand, QueryCommand }
4  from "@aws-sdk/lib-dynamodb";
5
6const client = new DynamoDBClient({
7  region: "us-east-1",           // Required but ignored locally
8  endpoint: "http://localhost:8000",
9  credentials: {
10    accessKeyId: "fakeMyKeyId",     // Any non-empty string works
11    secretAccessKey: "fakeSecretAccessKey",
12  },
13});
14
15const docClient = DynamoDBDocumentClient.from(client);

For DynamoDB Local, credentials can be any non-empty string — they are not validated. The region is required by the SDK but ignored locally when using -sharedDb.

Creating a Table

javascript
1import { CreateTableCommand } from "@aws-sdk/client-dynamodb";
2
3const createTable = async () => {
4  const command = new CreateTableCommand({
5    TableName: "Users",
6    KeySchema: [
7      { AttributeName: "userId", KeyType: "HASH" },   // Partition key
8      { AttributeName: "email", KeyType: "RANGE" },    // Sort key
9    ],
10    AttributeDefinitions: [
11      { AttributeName: "userId", AttributeType: "S" },
12      { AttributeName: "email", AttributeType: "S" },
13    ],
14    BillingMode: "PAY_PER_REQUEST",  // No capacity planning needed locally
15  });
16
17  const result = await client.send(command);
18  console.log("Table created:", result.TableDescription.TableName);
19};
20
21await createTable();

CRUD Operations

javascript
1// PUT — insert or replace an item
2await docClient.send(new PutCommand({
3  TableName: "Users",
4  Item: {
5    userId: "user-001",
6    email: "[email protected]",
7    name: "Alice",
8    age: 30,
9    createdAt: new Date().toISOString(),
10  },
11}));
12
13// GET — retrieve by primary key
14const { Item } = await docClient.send(new GetCommand({
15  TableName: "Users",
16  Key: { userId: "user-001", email: "[email protected]" },
17}));
18console.log(Item);  // { userId: 'user-001', email: '[email protected]', ... }
19
20// QUERY — find items by partition key
21const { Items } = await docClient.send(new QueryCommand({
22  TableName: "Users",
23  KeyConditionExpression: "userId = :uid",
24  ExpressionAttributeValues: { ":uid": "user-001" },
25}));
26console.log(Items);
27
28// UPDATE
29import { UpdateCommand } from "@aws-sdk/lib-dynamodb";
30
31await docClient.send(new UpdateCommand({
32  TableName: "Users",
33  Key: { userId: "user-001", email: "[email protected]" },
34  UpdateExpression: "SET age = :age, #n = :name",
35  ExpressionAttributeNames: { "#n": "name" },    // 'name' is a reserved word
36  ExpressionAttributeValues: { ":age": 31, ":name": "Alice B." },
37}));
38
39// DELETE
40import { DeleteCommand } from "@aws-sdk/lib-dynamodb";
41
42await docClient.send(new DeleteCommand({
43  TableName: "Users",
44  Key: { userId: "user-001", email: "[email protected]" },
45}));

Switching Between Local and Production

javascript
1// config.js — environment-aware configuration
2const isLocal = process.env.NODE_ENV === "development";
3
4const dynamoConfig = isLocal
5  ? {
6      region: "us-east-1",
7      endpoint: "http://localhost:8000",
8      credentials: {
9        accessKeyId: "local",
10        secretAccessKey: "local",
11      },
12    }
13  : {
14      region: process.env.AWS_REGION || "us-east-1",
15      // Production uses IAM roles or environment credentials automatically
16    };
17
18export const client = new DynamoDBClient(dynamoConfig);
19export const docClient = DynamoDBDocumentClient.from(client);

This pattern keeps your application code identical between local development and production. Only the DynamoDB client configuration changes.

Integration Testing with DynamoDB Local

javascript
1// test/setup.js — start DynamoDB Local for tests
2import { GenericContainer } from "testcontainers";
3
4let container;
5
6beforeAll(async () => {
7  container = await new GenericContainer("amazon/dynamodb-local")
8    .withExposedPorts(8000)
9    .withCommand(["-jar", "DynamoDBLocal.jar", "-sharedDb", "-inMemory"])
10    .start();
11
12  const endpoint = `http://${container.getHost()}:${container.getMappedPort(8000)}`;
13  process.env.DYNAMODB_ENDPOINT = endpoint;
14}, 30000);
15
16afterAll(async () => {
17  await container.stop();
18});
javascript
1// test/users.test.js
2import { createUser, getUser } from "../src/users.js";
3
4test("creates and retrieves a user", async () => {
5  await createUser({ userId: "test-1", email: "[email protected]", name: "Test" });
6  const user = await getUser("test-1", "[email protected]");
7  expect(user.name).toBe("Test");
8});

Using -inMemory flag makes DynamoDB Local store data in memory only, providing a clean state for each test run.

Common Pitfalls

  • Forgetting the endpoint configuration: Without endpoint: "http://localhost:8000", the SDK connects to the real AWS DynamoDB service, which requires valid credentials and incurs costs. Always verify the endpoint is set for local development.
  • DynamoDB Local not supporting all features: DynamoDB Local does not support DynamoDB Streams, TTL expiration, global tables, or some IAM-based access control features. Test these features against the real AWS service in a staging environment.
  • Port conflict with other services: Port 8000 is commonly used by other applications. If DynamoDB Local fails to start, check for port conflicts and use a different port: docker run -p 8001:8000 amazon/dynamodb-local with endpoint: "http://localhost:8001".
  • Data loss on container restart: Without volume mounting, DynamoDB Local in Docker loses all data when the container stops. Mount a volume for persistence: docker run -v ./dynamodb-data:/home/dynamodblocal/data -p 8000:8000 amazon/dynamodb-local.
  • Using SDK v2 syntax with SDK v3: AWS SDK v3 uses a modular client pattern (new DynamoDBClient()) and command objects (new PutCommand()), while SDK v2 uses new AWS.DynamoDB() with callback/promise methods. Mixing the two causes import errors and runtime failures.

Summary

  • Run DynamoDB Local via Docker: docker run -p 8000:8000 amazon/dynamodb-local -jar DynamoDBLocal.jar -sharedDb
  • Connect with AWS SDK v3 by setting endpoint: "http://localhost:8000" and dummy credentials
  • Use DynamoDBDocumentClient for simplified JavaScript-native data types
  • Switch between local and production using an environment variable for the endpoint
  • Use -inMemory flag for clean test runs and -sharedDb for simplified local development
  • The API is identical between local and production — only the client configuration changes

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.