Skip to main content
v2 · built on AWS SDK v3

Single-table design,
made simple.

Dynatable is a functional, fully typed TypeScript library for Amazon DynamoDB with first-class single-table design. Define your schema once — get end-to-end type inference, a fluent query builder, and safe schema migrations.

schema.ts
import { Table } from '@ftschopp/dynatable-core';

const schema = {
format: 'dynatable:1.0.0',
version: '1.0.0',
indexes: { primary: { hash: 'PK', sort: 'SK' } },
models: {
User: {
key: {
PK: { type: String, value: 'USER#${username}' },
SK: { type: String, value: 'PROFILE' },
},
attributes: {
username: { type: String, required: true },
email: { type: String },
age: { type: Number },
},
},
},
} as const;

const table = new Table({ name: 'MyApp', client, schema });

// ✨ fully typed — inferred straight from your schema
const user = await table.entities.User
.get({ username: 'alice' })
.execute();

const adults = await table.entities.User
.scan()
.filter((a, op) => op.gt(a.age, 18))
.execute();
100%Type inference
2Packages, zero fluff
AWS SDK v3Under the hood
MITOpen source

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.

Less code, more safety

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.

Raw AWS SDK
raw-sdk.ts
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;
With Dynatable
dynatable.ts
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;
Schema migrations, included

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
terminal
# 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

Ready to build on DynamoDB?

Go from zero to a fully typed, production-ready data layer in minutes.