DynamoDB is a fully implemented service in LocalStack Explorer. It supports table management, item CRUD with batch operations, Global Secondary Index (GSI) management, a visual query builder, PartiQL editor, and DynamoDB Streams viewer.
- List, create, and delete tables
- View table details (key schema, throughput, item count, size, stream config, ARN)
- Scan and query items with pagination
- Create, edit, and delete individual items via JSON editor
- Batch write (put + delete) and batch get with automatic chunking
- Visual query builder with key conditions, filters, index selection, and sort direction
- PartiQL editor with statement execution, examples, and history
- Global Secondary Index management (create, delete, list)
- Local Secondary Index viewing
- DynamoDB Streams viewer with shard listing and record inspection
- JSON file import for batch operations and JSON export for results
- Search/filter tables by name
All endpoints are prefixed with /api/dynamodb.
| Method | Path | Description | Request | Response |
|---|---|---|---|---|
| GET | / |
List tables | -- | { tables: [...] } |
| POST | / |
Create table | CreateTableBody |
{ message: string } |
| GET | /:tableName |
Describe table | -- | TableDetailResponse |
| DELETE | /:tableName |
Delete table | -- | { success: boolean } |
| Method | Path | Description | Request | Response |
|---|---|---|---|---|
| POST | /:tableName/indexes |
Create GSI | CreateGSIBody |
{ message: string } |
| DELETE | /:tableName/indexes/:indexName |
Delete GSI | -- | { success: boolean } |
| Method | Path | Description | Request | Response |
|---|---|---|---|---|
| POST | /:tableName/items/scan |
Scan items | ScanBody (filters, limit, startKey) |
ItemsResponse |
| POST | /:tableName/items/query |
Query items | QueryBody (keyCondition, filters, etc) |
ItemsResponse |
| POST | /:tableName/items/get |
Get item | { key: {...} } |
ItemsResponse |
| POST | /:tableName/items |
Put item | { item: {...} } |
{ message: string } |
| DELETE | /:tableName/items |
Delete item | { key: {...} } |
{ success: boolean } |
| POST | /:tableName/items/batch-write |
Batch write | { putItems?, deleteKeys? } |
BatchWriteResponse |
| POST | /:tableName/items/batch-get |
Batch get | { keys: [...], projectionExpression? } |
BatchGetResponse |
| Method | Path | Description | Request | Response |
|---|---|---|---|---|
| POST | /partiql |
Execute statement | { statement, parameters? } |
{ items: [...] } |
| Method | Path | Description | Query Params | Response |
|---|---|---|---|---|
| GET | /:tableName/streams |
Describe stream | -- | StreamDescription |
| GET | /:tableName/streams/records |
Get stream records | ?shardId=&limit= |
StreamRecordsResponse |
List tables:
curl http://localhost:3001/api/dynamodb{
"tables": [
{
"tableName": "users",
"tableStatus": "ACTIVE",
"itemCount": 42,
"tableSizeBytes": 8192
}
]
}Create table:
curl -X POST http://localhost:3001/api/dynamodb \
-H "Content-Type: application/json" \
-d '{
"tableName": "users",
"keySchema": [
{ "attributeName": "userId", "keyType": "HASH" },
{ "attributeName": "createdAt", "keyType": "RANGE" }
],
"attributeDefinitions": [
{ "attributeName": "userId", "attributeType": "S" },
{ "attributeName": "createdAt", "attributeType": "N" }
],
"provisionedThroughput": {
"readCapacityUnits": 5,
"writeCapacityUnits": 5
}
}'Describe table:
curl http://localhost:3001/api/dynamodb/users{
"tableName": "users",
"tableStatus": "ACTIVE",
"tableArn": "arn:aws:dynamodb:us-east-1:000000000000:table/users",
"creationDateTime": "2026-03-28T10:00:00.000Z",
"keySchema": [
{ "attributeName": "userId", "keyType": "HASH" },
{ "attributeName": "createdAt", "keyType": "RANGE" }
],
"attributeDefinitions": [
{ "attributeName": "userId", "attributeType": "S" },
{ "attributeName": "createdAt", "attributeType": "N" }
],
"provisionedThroughput": {
"readCapacityUnits": 5,
"writeCapacityUnits": 5
},
"globalSecondaryIndexes": [],
"itemCount": 42,
"tableSizeBytes": 8192
}Put item:
curl -X POST http://localhost:3001/api/dynamodb/users/items \
-H "Content-Type: application/json" \
-d '{"item": {"userId": "u-001", "createdAt": 1711612800, "name": "Alice"}}'Scan items:
curl -X POST http://localhost:3001/api/dynamodb/users/items/scan \
-H "Content-Type: application/json" \
-d '{"limit": 25}'{
"items": [
{ "userId": "u-001", "createdAt": 1711612800, "name": "Alice" }
],
"count": 1,
"scannedCount": 1,
"lastEvaluatedKey": null
}Query items:
curl -X POST http://localhost:3001/api/dynamodb/users/items/query \
-H "Content-Type: application/json" \
-d '{
"keyConditionExpression": "#pk = :pk AND #sk > :sk",
"expressionAttributeNames": { "#pk": "userId", "#sk": "createdAt" },
"expressionAttributeValues": { ":pk": "u-001", ":sk": 0 },
"scanIndexForward": false,
"limit": 10
}'Batch write:
curl -X POST http://localhost:3001/api/dynamodb/users/items/batch-write \
-H "Content-Type: application/json" \
-d '{
"putItems": [
{ "userId": "u-002", "createdAt": 1711612900, "name": "Bob" },
{ "userId": "u-003", "createdAt": 1711613000, "name": "Carol" }
],
"deleteKeys": [
{ "userId": "u-001", "createdAt": 1711612800 }
]
}'{
"processedCount": 3,
"unprocessedCount": 0
}Execute PartiQL:
curl -X POST http://localhost:3001/api/dynamodb/partiql \
-H "Content-Type: application/json" \
-d '{"statement": "SELECT * FROM \"users\" WHERE userId = '\''u-001'\''"}'Create GSI:
curl -X POST http://localhost:3001/api/dynamodb/users/indexes \
-H "Content-Type: application/json" \
-d '{
"indexName": "name-index",
"keySchema": [{ "attributeName": "name", "keyType": "HASH" }],
"projection": { "projectionType": "ALL" },
"provisionedThroughput": { "readCapacityUnits": 5, "writeCapacityUnits": 5 }
}'The DynamoDB service maps AWS SDK errors to appropriate HTTP status codes:
| Scenario | Status | Error Code |
|---|---|---|
| Table not found | 404 | TABLE_NOT_FOUND |
| Table already in use | 409 | TABLE_IN_USE |
| Limit exceeded | 429 | LIMIT_EXCEEDED |
| Stream not found | 404 | STREAM_NOT_FOUND |
| Validation error | 400 | VALIDATION_ERROR |
All errors return a consistent JSON shape:
{
"error": "TABLE_NOT_FOUND",
"message": "Table 'missing-table' not found",
"statusCode": 404
}The DynamoDB plugin consists of four files in packages/backend/src/plugins/dynamodb/:
| File | Purpose |
|---|---|
index.ts |
Plugin registration -- creates DynamoDB, Document, and Streams clients, registers routes |
service.ts |
DynamoDBService class -- business logic wrapping AWS SDK calls |
routes.ts |
Fastify route definitions with TypeBox validation schemas |
schemas.ts |
TypeBox schemas for all request inputs and response outputs |
The plugin uses three AWS SDK clients:
| Client | Package | Purpose |
|---|---|---|
DynamoDBClient |
@aws-sdk/client-dynamodb |
Table operations, GSI management, PartiQL |
DynamoDBDocumentClient |
@aws-sdk/lib-dynamodb |
Item operations (auto marshalling/unmarshalling) |
DynamoDBStreamsClient |
@aws-sdk/client-dynamodb-streams |
Stream description and record retrieval |
The Document Client wraps the base client with marshallOptions: { removeUndefinedValues: true }, automatically converting between JavaScript native types and DynamoDB attribute value format.
Table operations:
| Method | AWS SDK Command | Description |
|---|---|---|
listTables() |
ListTables + DescribeTable |
Lists all tables with status, item count, and size |
describeTable(name) |
DescribeTableCommand |
Full table description including indexes and stream |
createTable(params) |
CreateTableCommand |
Creates table with key schema, throughput, indexes |
deleteTable(name) |
DeleteTableCommand |
Deletes a table |
Item operations (via Document Client):
| Method | AWS SDK Command | Description |
|---|---|---|
scanItems(table, options?) |
ScanCommand |
Scans with optional filter, index, pagination |
queryItems(table, options) |
QueryCommand |
Queries with key condition and optional filter |
getItem(table, key) |
GetCommand |
Gets a single item by primary key |
putItem(table, item) |
PutCommand |
Creates or replaces an item |
deleteItem(table, key) |
DeleteCommand |
Deletes an item by primary key |
batchWriteItems(table, puts?, deletes?) |
BatchWriteCommand |
Batch put/delete, auto-splits into 25-item chunks |
batchGetItems(table, keys, projection?) |
BatchGetCommand |
Batch get, auto-splits into 100-key chunks |
Index operations:
| Method | AWS SDK Command | Description |
|---|---|---|
createGSI(table, params) |
UpdateTableCommand |
Adds a GSI to existing table |
deleteGSI(table, indexName) |
UpdateTableCommand |
Removes a GSI from table |
Stream operations:
| Method | AWS SDK Command | Description |
|---|---|---|
describeStream(table) |
DescribeTable + DescribeStreamCommand |
Stream metadata and shard list |
getStreamRecords(table, shard?, limit?) |
GetShardIteratorCommand + GetRecordsCommand |
Reads change records from a shard |
PartiQL:
| Method | AWS SDK Command | Description |
|---|---|---|
executePartiQL(statement, params?) |
ExecuteStatementCommand |
Executes SQL-like PartiQL statement |
The DynamoDB frontend is in packages/frontend/src/components/dynamodb/ and packages/frontend/src/routes/dynamodb/.
| Component | Description |
|---|---|
TableList |
Table of DynamoDB tables with search, create dialog, and delete with confirmation |
CreateTableDialog |
Modal for creating a table with partition key, optional sort key, and throughput |
TableDetail |
Overview card with table attributes, key schema, and ARN |
ItemBrowser |
Scan-based item viewer with pagination, create/edit/delete via JSON editor |
ItemEditorDialog |
JSON editor dialog for creating and editing items |
QueryBuilder |
Visual form for building queries with key conditions, filters, and index selection |
PartiQLEditor |
SQL-like statement editor with execute, examples, and history |
StreamViewer |
Stream information, shard list, and event record viewer |
IndexManager |
GSI/LSI listing with create and delete GSI dialogs |
CreateGSIDialog |
Modal for creating a GSI with key schema, projection type, and throughput |
BatchOperations |
Tabbed view for batch write (with JSON import) and batch get (with JSON export) |
| Route | Component | Description |
|---|---|---|
/dynamodb |
TableList |
List and manage tables |
/dynamodb/:tableName |
Detail page | Table detail with tabbed view (7 tabs) |
| Tab | Component | Description |
|---|---|---|
| Overview | TableDetail |
Table status, key schema, throughput, metrics, ARN |
| Items | ItemBrowser |
Browse, create, edit, and delete items |
| Query | QueryBuilder |
Visual query builder with index and filter support |
| PartiQL | PartiQLEditor |
Execute PartiQL (SQL-like) statements |
| Streams | StreamViewer |
View stream status, shards, and change records |
| Indexes | IndexManager |
Manage GSIs and view LSIs |
| Batch | BatchOperations |
Batch write/get operations with file import/export |
The visual query builder provides a form-based interface for constructing DynamoDB queries:
- Index selector: Choose between table primary key or any GSI
- Key conditions: Auto-populated partition and sort key fields based on selected index; sort key supports operators (
=,<,>,<=,>=,BETWEEN,begins_with) - Filters: Dynamic filter rows with attribute name, operator, and value; multiple operators supported (
=,<>,<,>,contains,begins_with,attribute_exists,attribute_not_exists) - Options: Limit, ascending/descending sort direction
- Auto mode: Runs a Query when partition key is provided, falls back to Scan when it is not
The component auto-generates KeyConditionExpression, FilterExpression, ExpressionAttributeNames, and ExpressionAttributeValues from the form inputs.
The PartiQL editor supports SQL-like queries against DynamoDB tables:
- Textarea with monospace font for writing statements
- Cmd+Enter (Ctrl+Enter) keyboard shortcut to execute
- Template buttons for common operations: SELECT, INSERT, UPDATE, DELETE
- Statement history (last 10, in-memory) with success/failure status and item count
- Results displayed in a dynamic table
All hooks are in packages/frontend/src/api/dynamodb.ts:
| Hook / Function | Type | Query Key / Notes |
|---|---|---|
useListTables() |
Query | ["dynamodb", "tables"] |
useDescribeTable(name) |
Query | ["dynamodb", "table", tableName] |
useCreateTable() |
Mutation | Invalidates ["dynamodb", "tables"] |
useDeleteTable() |
Mutation | Invalidates ["dynamodb", "tables"] |
useCreateGSI(table) |
Mutation | Invalidates ["dynamodb", "table", tableName] |
useDeleteGSI(table) |
Mutation | Invalidates ["dynamodb", "table", tableName] |
useScanItems(table) |
Mutation | Dynamic scan parameters |
useQueryItems(table) |
Mutation | Dynamic query parameters |
useGetItem(table) |
Mutation | Dynamic key parameter |
usePutItem(table) |
Mutation | Invalidates ["dynamodb", "items", tableName] |
useDeleteItem(table) |
Mutation | Invalidates ["dynamodb", "items", tableName] |
useBatchWriteItems(table) |
Mutation | Invalidates ["dynamodb", "items", tableName] |
useBatchGetItems(table) |
Mutation | Returns fetched items |
useExecutePartiQL() |
Mutation | Dynamic statement parameter |
useDescribeStream(table) |
Query | ["dynamodb", "streams", tableName] |
useGetStreamRecords(table) |
Mutation | Dynamic shard/limit parameters |
Scan, query, get, PartiQL, and stream records use mutation hooks because their parameters change dynamically with each user action. Mutations that modify data automatically invalidate the relevant query cache.
The batch operations handle DynamoDB's built-in limits automatically:
- Batch write: Splits requests into chunks of 25 items (DynamoDB's
BatchWriteItemlimit) - Batch get: Splits requests into chunks of 100 keys (DynamoDB's
BatchGetItemlimit)
The frontend supports JSON file import for batch write operations and JSON export for batch get results.
Batch write format:
{
"put": [
{ "userId": "u-001", "name": "Alice" },
{ "userId": "u-002", "name": "Bob" }
],
"delete": [
{ "userId": "u-003", "createdAt": 1711612800 }
]
}When streams are enabled on a table, the StreamViewer component shows:
- Stream metadata: ARN, status, view type (KEYS_ONLY, NEW_IMAGE, OLD_IMAGE, NEW_AND_OLD_IMAGES), label
- Shard list: Each shard with ID, parent shard, and a button to fetch records
- Stream records: Color-coded event badges (INSERT, MODIFY, REMOVE) with expandable JSON views for keys, new image, and old image
Note: Streams must be enabled on the table at creation time or via an UpdateTable operation. The viewer indicates when streams are not enabled.