-
Notifications
You must be signed in to change notification settings - Fork 0
Getting started
This page walks through a complete working example: an Express app that serves a DynamoDB table over the standard REST route pack.
- A DynamoDB table (or DynamoDB Local running at
http://localhost:8000for experimentation). - Node 20+ (or Bun / Deno with Express).
- An Express app — this adapter plugs into any existing Express middleware chain.
npm install dynamodb-toolkit-express dynamodb-toolkit express @aws-sdk/client-dynamodb @aws-sdk/lib-dynamodbOptional, but recommended for most apps:
# express.json() ships with express itself — no extra install needed.-
Mount at a sub-path —
app.use('/planets', middleware)strips the/planetsprefix fromreq.pathbefore the adapter sees it. Without a prefix mount, the adapter matches against the fullreq.path, which only works for single-collection apps. -
express.json()pre-parses JSON bodies so every middleware seesreq.body. The adapter uses it when present and falls back to streaming the raw request otherwise.
// server.js
import express from 'express';
import {DynamoDBClient} from '@aws-sdk/client-dynamodb';
import {DynamoDBDocumentClient} from '@aws-sdk/lib-dynamodb';
import {Adapter} from 'dynamodb-toolkit';
import {createExpressAdapter} from 'dynamodb-toolkit-express';
const ddbClient = DynamoDBDocumentClient.from(
new DynamoDBClient({region: 'us-east-1'}),
{marshallOptions: {removeUndefinedValues: true}}
);
const planets = new Adapter({
client: ddbClient,
table: 'planets',
keyFields: ['name']
});
const app = express();
app.use(express.json());
app.use('/planets', createExpressAdapter(planets));
app.listen(3000, () => console.log('Listening on http://localhost:3000'));# Create
curl -X POST http://localhost:3000/planets/ \
-H 'content-type: application/json' \
-d '{"name":"earth","mass":5.97,"climate":"temperate"}'
# Read one
curl http://localhost:3000/planets/earth
# Partial update (PATCH merges, does not replace)
curl -X PATCH http://localhost:3000/planets/earth \
-H 'content-type: application/json' \
-d '{"population":8.2e9}'
# List with paging
curl 'http://localhost:3000/planets/?offset=0&limit=25'
# Filter by field prefix (see parent wiki for query grammar)
curl 'http://localhost:3000/planets/?fields=name,mass'
# Bulk fetch by name
curl 'http://localhost:3000/planets/-by-names?names=earth,mars,venus'
# Delete
curl -X DELETE http://localhost:3000/planets/plutoSee the Routes page for the full route pack.
- Custom error handling — see Error handling.
- Composite keys (partition + sort) — see Composite keys.
- Per-tenant list filtering — see Per-tenant filtering.
- Mounting multiple collections — see Mounting and routing.
If you only serve one collection, you can skip the prefix and use the adapter as a top-level middleware:
const app = express();
app.use(express.json());
app.use(createExpressAdapter(planets));
app.listen(3000);
// GET /earth, POST /, PATCH /earth — all hit the adapter directly.The adapter matches routes against req.path. Without a prefix mount there's no prefix to strip, so a top-level mount means URLs don't carry a collection prefix.
The adapter reads the request stream directly when req.body is undefined, enforcing a 1 MiB cap by default (configurable via the maxBodyBytes option). Apps that don't already have a body parser don't need to add one just for this adapter.
const app = express();
app.use(createExpressAdapter(planets, {maxBodyBytes: 256 * 1024})); // 256 KiB cap
app.listen(3000);See Body parsing for when each mode is preferable.