End-to-end type safety
Types are inferred straight from your schema. Every get, query, scan and mutation is fully typed — catch mistakes at compile time, not in production.
Single-table design, built in
Model many entities in one table the DynamoDB way. Composite keys, GSIs, and complex relationships — without hand-writing key strings.
Fluent query builder
A chainable, immutable API for queries and scans. Filter, project, and paginate with full type inference — no more raw DynamoDB expressions.
Safe schema migrations
Evolve your data with versioned up/down migrations, dry-run mode, distributed locking and a full CLI — shipped as a dedicated package.
Minimal boilerplate
Define your schema once and get typed CRUD automatically. No decorators, no classes — just plain, immutable TypeScript objects.
Production ready
Built on AWS SDK v3 with transactions, batch operations, conditional writes, ULID/UUID generation and automatic timestamps out of the box.
Stop hand-writing DynamoDB
The raw AWS SDK makes you build keys, marshall attributes, and unmarshall responses by hand — with no type safety. Dynatable does it for you.
import {
DynamoDBClient,
GetItemCommand,
} from '@aws-sdk/client-dynamodb';
const res = await client.send(
new GetItemCommand({
TableName: 'MyApp',
Key: {
PK: { S: `USER#${username}` },
SK: { S: 'PROFILE' },
},
}),
);
// unmarshall by hand — no types, easy to typo keys
const age = res.Item?.age?.N
? Number(res.Item.age.N)
: undefined;
import { table } from './db';
const user = await table.entities.User
.get({ username })
.execute();
// user: User | undefined
// keys are built for you, everything is typed
const age = user?.age;
Evolve your data with confidence
DynamoDB is schemaless, but your data still has a shape. The dedicated@ftschopp/dynatable-migrations package brings versioned, reversible migrations and a full CLI — so shipping a data change feels as safe as a code change.
- Up / down migrations with semver versioning
- Dry-run mode to preview every change first
- Distributed locking prevents concurrent runs
- History lives in your table — no extra infra
# scaffold migrations in your project
$ dynatable-migrate init
# create a versioned migration (semver bump)
$ dynatable-migrate create add_user_email --type minor
✓ migrations/0.2.0_add_user_email.ts
# preview changes — nothing is written
$ dynatable-migrate up --dry-run
→ 0.2.0 add_user_email (dry run)
# apply: distributed lock + history in your table
$ dynatable-migrate up
✓ 0.2.0 add_user_email applied
# roll back if you need to
$ dynatable-migrate down --steps 1
Everything you need, nothing you don't
Ready to build on DynamoDB?
Go from zero to a fully typed, production-ready data layer in minutes.