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: a Koa 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 Koa 3).
  • A Koa app — this adapter plugs into any existing Koa middleware chain.

Install

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

Optional, but recommended for most apps:

npm install koa-mount koa-bodyparser
  • koa-mount lets you mount the adapter at a sub-path (e.g. /planets). Without it, the adapter matches against the full ctx.path, which only works for single-collection apps.
  • koa-bodyparser (or @koa/bodyparser) pre-parses JSON bodies so every middleware sees ctx.request.body. The adapter uses it when present and falls back to streaming the raw request otherwise.

First working example

// server.js
import Koa from 'koa';
import mount from 'koa-mount';
import bodyParser from 'koa-bodyparser';
import {DynamoDBClient} from '@aws-sdk/client-dynamodb';
import {DynamoDBDocumentClient} from '@aws-sdk/lib-dynamodb';
import {Adapter} from 'dynamodb-toolkit';
import {createKoaAdapter} from 'dynamodb-toolkit-koa';

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 = new Koa();
app.use(bodyParser());
app.use(mount('/planets', createKoaAdapter(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 koa-mount

If you only serve one collection, you can skip koa-mount and use the adapter as a top-level middleware:

const app = new Koa();
app.use(bodyParser());
app.use(createKoaAdapter(planets));
app.listen(3000);
// GET /earth, POST /, PATCH /earth — all hit the adapter directly.

The adapter matches routes against ctx.path. Without koa-mount there's no prefix to strip, so a top-level mount means URLs don't carry a collection prefix.

Without koa-bodyparser

The adapter reads ctx.req directly when ctx.request.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 = new Koa();
app.use(createKoaAdapter(planets, {maxBodyBytes: 256 * 1024})); // 256 KiB cap
app.listen(3000);

See Body parsing for when each mode is preferable.

Clone this wiki locally