Dynamodb
Build with Amazon DynamoDB as a serverless NoSQL database. Use this skill when
You are a database specialist who integrates DynamoDB into projects. DynamoDB is a fully managed, serverless NoSQL database from AWS that provides single-digit millisecond performance at any scale with automatic scaling and zero maintenance. ## Key Points - Design for access patterns first — list every query before creating the table - Use single-table design — one table with PK/SK patterns for all entities - Use GSIs to support additional query patterns - Use `begins_with` on sort keys for hierarchical queries - Use conditional writes to prevent race conditions - Use TTL for auto-expiring data (sessions, logs, temporary records) - Use DynamoDB Streams for event-driven architectures - Designing tables like relational databases — one table per entity - Using Scan instead of Query — scans read the entire table - Not planning GSIs upfront — retrofitting access patterns is expensive - Using FilterExpression instead of proper key design — filters run after reads - Storing large items (>400KB) — use S3 for large objects ## Quick Example ```bash npm install @aws-sdk/client-dynamodb @aws-sdk/lib-dynamodb ```
skilldb get database-services-skills/DynamodbFull skill: 244 linesAmazon DynamoDB Integration
You are a database specialist who integrates DynamoDB into projects. DynamoDB is a fully managed, serverless NoSQL database from AWS that provides single-digit millisecond performance at any scale with automatic scaling and zero maintenance.
Core Philosophy
Single-table design
DynamoDB works best when you put all your data in one table with carefully designed partition and sort keys. This isn't like relational design — you model your data around your access patterns, not your entities.
Access patterns first
Before creating a table, list every query your application needs. Design your keys and indexes to serve those queries. You cannot query DynamoDB flexibly — if you didn't plan for a query, you can't do it without a full table scan.
No joins, no aggregations
DynamoDB doesn't support joins or server-side aggregations. If you need them, denormalize your data or compute aggregates in your application. This is the trade-off for unlimited scale.
Setup
Install
npm install @aws-sdk/client-dynamodb @aws-sdk/lib-dynamodb
Initialize
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';
const client = new DynamoDBClient({ region: process.env.AWS_REGION });
const docClient = DynamoDBDocumentClient.from(client, {
marshallOptions: { removeUndefinedValues: true },
});
Key Techniques
Single-table design
// All entities in one table with PK/SK patterns
// PK | SK | Attributes
// USER#alice | PROFILE | name, email, plan
// USER#alice | POST#2024-01-15 | title, content, status
// USER#alice | APIKEY#key123 | key, active, usageCount
// POST#uuid | METADATA | title, authorId, status
// POST#uuid | COMMENT#timestamp | text, authorId
const TABLE = process.env.TABLE_NAME!;
CRUD operations
import { GetCommand, PutCommand, UpdateCommand, DeleteCommand, QueryCommand } from '@aws-sdk/lib-dynamodb';
// Put (create or overwrite)
await docClient.send(new PutCommand({
TableName: TABLE,
Item: {
PK: `USER#${userId}`,
SK: 'PROFILE',
name: 'Alice',
email: 'alice@example.com',
plan: 'free',
createdAt: new Date().toISOString(),
GSI1PK: 'USERS',
GSI1SK: `USER#${userId}`,
},
}));
// Get (exact key lookup)
const { Item } = await docClient.send(new GetCommand({
TableName: TABLE,
Key: { PK: `USER#${userId}`, SK: 'PROFILE' },
}));
// Query (partition key + sort key condition)
const { Items } = await docClient.send(new QueryCommand({
TableName: TABLE,
KeyConditionExpression: 'PK = :pk AND begins_with(SK, :sk)',
ExpressionAttributeValues: {
':pk': `USER#${userId}`,
':sk': 'POST#',
},
ScanIndexForward: false, // Descending order
Limit: 20,
}));
// Query with filter
const { Items: activePosts } = await docClient.send(new QueryCommand({
TableName: TABLE,
KeyConditionExpression: 'PK = :pk AND begins_with(SK, :sk)',
FilterExpression: '#status = :status',
ExpressionAttributeNames: { '#status': 'status' },
ExpressionAttributeValues: {
':pk': `USER#${userId}`,
':sk': 'POST#',
':status': 'published',
},
}));
// Update
await docClient.send(new UpdateCommand({
TableName: TABLE,
Key: { PK: `USER#${userId}`, SK: 'PROFILE' },
UpdateExpression: 'SET #name = :name, updatedAt = :now',
ExpressionAttributeNames: { '#name': 'name' },
ExpressionAttributeValues: {
':name': 'Alice Updated',
':now': new Date().toISOString(),
},
}));
// Conditional update
await docClient.send(new UpdateCommand({
TableName: TABLE,
Key: { PK: `USER#${userId}`, SK: 'PROFILE' },
UpdateExpression: 'SET plan = :newPlan',
ConditionExpression: 'plan = :currentPlan',
ExpressionAttributeValues: {
':newPlan': 'pro',
':currentPlan': 'free',
},
}));
// Delete
await docClient.send(new DeleteCommand({
TableName: TABLE,
Key: { PK: `USER#${userId}`, SK: 'PROFILE' },
}));
// Atomic counter
await docClient.send(new UpdateCommand({
TableName: TABLE,
Key: { PK: `POST#${postId}`, SK: 'METADATA' },
UpdateExpression: 'ADD viewCount :inc',
ExpressionAttributeValues: { ':inc': 1 },
}));
Global Secondary Index (GSI) queries
// Query GSI — e.g., find all users
const { Items } = await docClient.send(new QueryCommand({
TableName: TABLE,
IndexName: 'GSI1',
KeyConditionExpression: 'GSI1PK = :pk',
ExpressionAttributeValues: { ':pk': 'USERS' },
}));
// Query by email (GSI2 with email as key)
const { Items: byEmail } = await docClient.send(new QueryCommand({
TableName: TABLE,
IndexName: 'GSI2',
KeyConditionExpression: 'GSI2PK = :email',
ExpressionAttributeValues: { ':email': `EMAIL#${email}` },
}));
Transactions
import { TransactWriteCommand } from '@aws-sdk/lib-dynamodb';
await docClient.send(new TransactWriteCommand({
TransactItems: [
{
Update: {
TableName: TABLE,
Key: { PK: `POST#${postId}`, SK: 'METADATA' },
UpdateExpression: 'SET #status = :status',
ExpressionAttributeNames: { '#status': 'status' },
ExpressionAttributeValues: { ':status': 'published' },
},
},
{
Put: {
TableName: TABLE,
Item: {
PK: `USER#${authorId}`,
SK: `NOTIFICATION#${Date.now()}`,
type: 'post_published',
postId,
},
},
},
],
}));
Pagination
async function* paginate(params: QueryCommandInput) {
let lastKey: Record<string, any> | undefined;
do {
const { Items, LastEvaluatedKey } = await docClient.send(
new QueryCommand({ ...params, ExclusiveStartKey: lastKey })
);
yield Items ?? [];
lastKey = LastEvaluatedKey;
} while (lastKey);
}
for await (const page of paginate({ TableName: TABLE, KeyConditionExpression: 'PK = :pk', ExpressionAttributeValues: { ':pk': 'USERS' }, IndexName: 'GSI1' })) {
for (const item of page) {
console.log(item);
}
}
Best Practices
- Design for access patterns first — list every query before creating the table
- Use single-table design — one table with PK/SK patterns for all entities
- Use GSIs to support additional query patterns
- Use
begins_withon sort keys for hierarchical queries - Use conditional writes to prevent race conditions
- Use TTL for auto-expiring data (sessions, logs, temporary records)
- Use DynamoDB Streams for event-driven architectures
Anti-Patterns
- Designing tables like relational databases — one table per entity
- Using Scan instead of Query — scans read the entire table
- Not planning GSIs upfront — retrofitting access patterns is expensive
- Using FilterExpression instead of proper key design — filters run after reads
- Storing large items (>400KB) — use S3 for large objects
- Hot partition keys — distribute writes across partitions
- Not handling pagination — DynamoDB returns max 1MB per query
Install this skill directly: skilldb add database-services-skills
Related Skills
Cassandra
Build with Apache Cassandra for high-availability distributed data. Use this skill
Clickhouse
Build with ClickHouse for real-time analytics and OLAP workloads. Use this skill
Cockroachdb
Build with CockroachDB as a distributed SQL database. Use this skill when the
Convex
Build with Convex as a reactive backend. Use this skill when the project needs
Drizzle
Use Drizzle ORM for type-safe SQL in TypeScript. Use this skill when the project
Fauna
Build with Fauna as a distributed document-relational database. Use this skill