Skip to content
This repository was archived by the owner on Jul 17, 2026. It is now read-only.

Getting started

Eugene Lazutkin edited this page Apr 19, 2026 · 1 revision

Getting started

This page walks through a complete working example: an Express app that serves a DynamoDB table over the standard REST route pack.

Prerequisites

  • A DynamoDB table (or DynamoDB Local running at http://localhost:8000 for experimentation).
  • Node 20+ (or Bun / Deno with Express).
  • An Express app — this adapter plugs into any existing Express middleware chain.

Install

npm install dynamodb-toolkit-express dynamodb-toolkit express @aws-sdk/client-dynamodb @aws-sdk/lib-dynamodb

Optional, but recommended for most apps:

# express.json() ships with express itself — no extra install needed.
  • Mount at a sub-pathapp.use('/planets', middleware) strips the /planets prefix from req.path before the adapter sees it. Without a prefix mount, the adapter matches against the full req.path, which only works for single-collection apps.
  • express.json() pre-parses JSON bodies so every middleware sees req.body. The adapter uses it when present and falls back to streaming the raw request otherwise.

First working example

// 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'));

Try it

# 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/pluto

See the Routes page for the full route pack.

Next steps

Without a mount prefix

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.

Without express.json()

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.

Clone this wiki locally