-
Notifications
You must be signed in to change notification settings - Fork 0
Plain Express routes
Nothing in the toolkit requires the framework adapters — every Adapter method is an ordinary async function you can call from your own routes. This page is the minimal proof: an Express app with three hand-written routes that call dynamodb-toolkit directly, no canned adapter, no route pack, no wire grammar. Use this shape when you want full control over URLs, envelopes, and status codes, or when your API is a handful of endpoints that don't fit the standard pack.
Same construction as Getting started:
import express from 'express';
import {DynamoDBClient} from '@aws-sdk/client-dynamodb';
import {DynamoDBDocumentClient} from '@aws-sdk/lib-dynamodb';
import {Adapter} from 'dynamodb-toolkit';
const client = DynamoDBDocumentClient.from(new DynamoDBClient({region: 'us-east-1'}), {
marshallOptions: {removeUndefinedValues: true}
});
const planets = new Adapter({
client,
table: 'planets',
keyFields: ['name']
});
const app = express();
app.use(express.json());// 1. List with paging: GET /planets?offset=0&limit=10
app.get('/planets', async (req, res) => {
const offset = Math.max(Number(req.query.offset) || 0, 0);
const limit = Math.min(Number(req.query.limit) || 10, 100);
const page = await planets.getListByParams({}, {offset, limit});
res.json(page); // {data, offset, limit, total}
});
// 2. Single item: GET /planets/earth
app.get('/planets/:name', async (req, res) => {
const item = await planets.getByKey({name: req.params.name});
if (item === undefined) return res.status(404).end();
res.json(item);
});
// 3. Create: POST /planets
app.post('/planets', async (req, res) => {
await planets.post(req.body); // rejects if the key already exists
res.status(201).end();
});
app.listen(3000);That is the whole integration. The toolkit consumes and produces plain objects — express.json() supplies the body, res.json() ships the result, and the shape of your URLs and responses is entirely yours.
Adapter methods throw on failure. Express 5 forwards rejected promises to the error middleware automatically (on Express 4, wrap each handler), so one error middleware covers all three routes:
app.use((err, req, res, next) => {
if (res.headersSent) return next(err);
// post() on an existing key surfaces the SDK's conditional-write rejection
if (err.name === 'ConditionalCheckFailedException') {
return res.status(409).json({code: 'AlreadyExists', message: 'item already exists'});
}
res.status(err.status >= 400 && err.status < 600 ? err.status : 500).json({
code: err.code || err.name || 'InternalError',
message: err.message
});
});Toolkit errors carry name (matching the class name) and often status; SDK errors keep their SDK names. Map the ones you care about, default the rest.
Every other Adapter method drops in the same way — patch for partial updates, delete, getByKeys for bulk reads, the mass operations (Adapter: CRUD methods, Adapter: Mass methods). The toolkit's other layers stay available à la carte: the expression builders compose raw SDK params, and the REST core parsers/builders are importable individually if you want the standard filter grammar or envelope shape inside your own routes.
When the endpoint count grows and you find yourself re-implementing the standard pack — list envelopes with paging links, -by-names bulk routes, clone/move, the filter grammar — that is what the framework adapters ship in one line; the Express adapter coexists happily with hand-written routes on the same app (mount it on one path prefix, keep custom routes on others).
Start here
- Getting started
- Concepts
- Key and field design
- Compatibility
- Migration: v2 to v3
- SDK v2 to v3 cheat sheet
Guides
- Hierarchical data walkthrough
- Key expression patterns
- Multi-type tables
- Pagination
- Mass operation semantics
- URL schema design
Adapter
- Adapter
- Constructor options
- CRUD methods
- Mass methods
- Batch builders
- Hooks
- Raw marker
- Indirect indices
- Transaction auto-upgrade
Expression builders
Batch / transactions / mass / paths
REST surface
Framework adapters
Recipes
- Recipes index
- List records of a tier
- Per-tier sparse GSI markers
- Tier within a partition
- Reservation with auto-release
- Keys-only GSI, runtime projection
- Cascade subtree operations
- Querying subtrees with buildKey
- Filter URL grammar
- Text search
- Provisioning workflow
- Resumable mass operations
History