A comprehensive task list for building a Laravel-like ORM/Query Builder for DynamoDB with single-table design patterns (Alex DeBrie style).
- Add
regionconfig option (default:us-east-1) - Add
endpointconfig option for local/custom endpoints - Add
credentialsconfig section (accessKeyId, secretAccessKey, sessionToken) - Add
profileoption for AWS credential profiles - Add
maxRetriesandretryModeoptions - Add
httpOptions(timeout, connectTimeout, keepAlive, keepAliveTimeout) - Add
tableNamePrefixfor multi-tenant/environment table naming - Add
tableNameSuffixoption - Add
defaultTableNamefor single-table design primary table
- Add
singleTableDesign.enabledboolean (default: true) - Add
singleTableDesign.partitionKeyName(default:pk) - Add
singleTableDesign.sortKeyName(default:sk) - Add
singleTableDesign.gsi1pkName/gsi1skName(default:gsi1pk,gsi1sk) - Add
singleTableDesign.gsi2pkName/gsi2skName(default:gsi2pk,gsi2sk) - Add
singleTableDesign.gsi3pkName/gsi3skNamethrough gsi5 (extended) - Add
singleTableDesign.entityTypeAttribute(default:_et) - Add
singleTableDesign.dataAttribute(default:_d) - Add
singleTableDesign.pkPrefixformat (e.g.,{ENTITY}#) - Add
singleTableDesign.skPrefixformat patterns - Add
singleTableDesign.gsiCount(number of GSIs to configure, default: 2) - Add
singleTableDesign.keyDelimiter(default:#)
- Add
queryBuilder.modelsPathfor Stacks model discovery - Add
queryBuilder.entityMappingStrategy(prefix,attribute,composite) - Add
queryBuilder.timestampFormat(iso,unix,unixMs) - Add
queryBuilder.softDeletes.enabledboolean - Add
queryBuilder.softDeletes.attribute(default:deletedAt) - Add
queryBuilder.hookssection for lifecycle events (global and per-model) - Add
queryBuilder.caching.enabledboolean - Add
queryBuilder.caching.ttlMsdefault TTL - Add
queryBuilder.caching.maxSizeLRU cache size - Add
queryBuilder.caching.keyPrefixfor namespacing - Add
queryBuilder.createdAtAttribute(default:createdAt) - Add
queryBuilder.updatedAtAttribute(default:updatedAt) - Add
queryBuilder.versionAttribute(default:_v)
- Add
capacity.billingMode(PAY_PER_REQUEST|PROVISIONED) - Add
capacity.read/capacity.writefor provisioned mode - Add
capacity.autoScaling.enabledboolean - Add
capacity.autoScaling.read.min/max/targetUtilization - Add
capacity.autoScaling.write.min/max/targetUtilization - Add
capacity.autoScaling.scaleInCooldown/scaleOutCooldown
- Add
streams.enabledboolean - Add
streams.viewType(KEYS_ONLY,NEW_IMAGE,OLD_IMAGE,NEW_AND_OLD_IMAGES) - Add
ttl.enabledboolean - Add
ttl.attributeName(default:ttl)
- Add
tableClassoption (STANDARD|STANDARD_INFREQUENT_ACCESS) - Add
deletionProtectionboolean (prevent accidental deletes) - Add
contributorInsights.enabledboolean - Add
tagsconfig for resource tagging - Add
returnConsumedCapacitydefault (NONE|TOTAL|INDEXES) - Add
returnItemCollectionMetricsdefault (NONE|SIZE) - Add
consistentReaddefault boolean - Add
globalSecondaryIndexesarray for GSI definitions - Add
localSecondaryIndexesarray for LSI definitions - Add
multiTenancyconfig (enabled, strategy, tenantIdAttribute, tenantResolver) - Add
localconfig section for DynamoDB Local settings
- Update
src/types.tswith all new config interfaces (850+ lines) - Update
src/config.tswith new defaults - Add config validation with helpful error messages (
validateConfig()) - Add
getConfig()async loader with bunfig - Add
setConfig()for programmatic configuration - Add
resetConfig()for testing - Add config merging for nested objects (
deepMerge()) - Add helper functions (
getFullTableName(),generateDefaultGSIs(),isLocalMode(),getEndpoint()) - Document all config options in README (deferred to Phase 13)
CORE FEATURE: Zero-config, fully automated translation of Stacks data models into DynamoDB single-table design. Developers write standard Stacks models and the system automatically generates pk/sk patterns, GSIs, access patterns, and handles all DynamoDB complexity behind the scenes.
- Create
src/model-parser/StacksModelParser.ts - Create
src/model-parser/types.ts- comprehensive type definitions - Auto-discover models from
queryBuilder.modelsPathconfig - Parse model
name→ entity type prefix (e.g.,User→USER#) - Parse model
primaryKey→ sort key suffix pattern - Parse model
attributes→ DynamoDB attribute definitions with type inference - Parse model
hasOne→ auto-generate GSI for reverse lookup - Parse model
hasMany→ auto-generate sk begins_with pattern - Parse model
belongsTo→ auto-generate pk reference pattern - Parse model
belongsToMany→ auto-generate adjacency list items - Parse model
traits→ apply behaviors (timestamps, soft deletes, etc.) - Parse model
indexes→ map to GSI definitions - Cache parsed models for runtime performance (TTL-based cache)
- Create
src/single-table/KeyPatternGenerator.ts - Auto-generate pk pattern:
{ENTITY}#{primaryKey}(e.g.,USER#123) - Auto-generate sk pattern:
{ENTITY}#{primaryKey}for base entity - Auto-generate hierarchical sk for nested entities:
USER#123#ORDER#456 - Auto-generate GSI1 pk/sk from
hasManyrelations (inverted index) - Auto-generate GSI2 pk/sk from
belongsToManyrelations - Support custom key patterns via optional model config override
- Validate key patterns don't conflict across entities
- Generate key pattern documentation automatically
- Create
src/single-table/GsiDeriver.ts - Analyze all
hasOnerelations → derive GSI for "get parent from child" - Analyze all
hasManyrelations → derive GSI for "get children by parent" - Analyze all
belongsTorelations → derive reverse lookup GSI - Analyze all
belongsToManyrelations → derive adjacency list GSI - Detect overlapping access patterns → consolidate into overloaded GSIs
- Minimize GSI count (max 20 per table, aim for 2-5)
- Auto-assign GSI pk/sk attributes based on query needs
- Generate GSI usage documentation per model
- Generate optimization suggestions for GSI design
- Create
src/single-table/LsiDeriver.ts - Detect attributes that need sorting within same pk
- Auto-generate LSI for
orderBypatterns on same partition - Support up to 5 LSIs per table
- Handle LSI projection types (ALL, KEYS_ONLY, INCLUDE)
- Warn about LSI 10GB partition limit
- Generate LSI documentation
- Create
src/single-table/SparseIndexDeriver.ts - Detect optional/nullable attributes that need indexing
- Auto-generate sparse GSI (only items with attribute are indexed)
- Use sparse indexes for status-based queries (e.g.,
status = 'active') - Document sparse index cost savings
- Support soft delete sparse indexes
- Support TTL expiry sparse indexes
- Support item collections (related items with same pk) via RelationshipResolver
- Auto-generate collection queries from
hasManyrelations - Support
getCollection(pk)pattern via query utilities - Hierarchical collections supported via key patterns
- Create
src/single-table/AccessPatternGenerator.ts - Generate "Get {Entity} by ID" pattern for each model
- Generate "List all {Entity}" pattern (scan with entity type filter)
- Generate "Get {Child} by {Parent}" from hasMany relations
- Generate "Get {Parent} of {Child}" from belongsTo relations
- Generate "Get {Entity} by {attribute}" from unique attributes
- Generate "List {Entity} by {attribute}" from indexed attributes
- Output access pattern matrix (entity × operation × index used)
- Warn if common query patterns lack efficient index
- Auto-generate access pattern documentation markdown
- Performance notes for each pattern
- Example code snippets for each pattern
- Create
src/single-table/EntityTransformer.ts - Auto-transform model instance → DynamoDB item with pk/sk
- Auto-add entity type attribute (
_et: 'User') - Auto-add GSI key attributes based on relationships
- Auto-add timestamp attributes if
useTimestampstrait - Auto-add version attribute if
useVersioningtrait - Auto-transform DynamoDB item → model instance (reverse)
- Handle nested/embedded documents (Map type)
- Handle arrays/sets (List/Set types)
- Strip internal attributes (pk, sk, gsi keys) from model output
- Implement
marshallValue()andunmarshallValue()functions - Implement type casting based on model definitions
- Implement deep equality checking for dirty tracking
- Create
src/single-table/RelationshipResolver.ts - Auto-resolve
hasOne→ single Query on related entity - Auto-resolve
hasMany→ Query with sk begins_with - Auto-resolve
belongsTo→ GetItem on parent entity - Auto-resolve
belongsToMany→ Query adjacency list + BatchGet - Support eager loading via
with()- batch all related queries - Optimize N+1 queries automatically with batching (
eagerLoadMany) - Cache relationship results within request scope (
RelationshipCache) - Support
withCount()for relationship counts - Support nested eager loading (e.g.,
posts.comments)
- Support time-based sort keys infrastructure via key pattern generator
- Auto-generate time-bucketed partitions for high-volume writes
- Implement
whereDate(),whereMonth(),whereYear()helpers (deferred to Query Builder phase) - Support time-range queries with efficient key conditions (deferred to Query Builder phase)
- Auto-archive old data to cold storage (S3) (future enhancement)
- Detect high-cardinality write patterns
- Auto-generate sharded partition keys (
COUNTER#1,COUNTER#2, etc.) - Implement scatter-gather reads for sharded data
- Configurable shard count per entity type
- Document write sharding patterns
IMPORTANT: Enable a pluggable driver system in bun-query-builder so external drivers (like the DynamoDB sponsorware driver) can be registered.
- Create
src/drivers/registry.ts- driver registration system - Define
DriverPlugininterface for external drivers - Implement
registerDriver(name, driver)function - Implement
getDriver(name)function with fallback to built-in - Update
SupportedDialectto accept string (for custom drivers) - Add
driversconfig option for auto-registration - Export driver interfaces from package for external use
- Document driver plugin API in README
- Extract
BaseDriverabstract class with common functionality - Define
DriverCapabilitiesinterface (transactions, batch, streams, etc.) - Add
getCapabilities()method to driver interface - Add
validateConfig()method for driver-specific config validation - Add
connect()/disconnect()lifecycle methods - Add
healthCheck()method for connection status - Ensure all existing drivers (SQLite, PostgreSQL, MySQL) implement new interface
SPONSORWARE: Create separate repo at
~/Code/bun-dynamodb-driverusing ts-starter template. This is a paid driver that integrates with bun-query-builder.
- Copy ts-starter to
~/Code/bun-dynamodb-driver - Update
package.jsonname tobun-dynamodb-driver - Update
package.jsondescription for DynamoDB driver - Update repository URLs to
stacksjs/bun-dynamodb-driver - Update LICENSE for sponsorware terms
- Remove unnecessary starter files (bin/, cli scripts)
- Set up GitHub repo with sponsorware access controls
- Add sponsorware badge and notice to README
- Run
bun linkin ts-cloud - Run
bun link ts-cloudin bun-dynamodb-driver - Run
bun linkin bun-query-builder - Run
bun link bun-query-builderin bun-dynamodb-driver - Add ts-cloud as dependency in package.json
- Add bun-query-builder as peerDependency in package.json
- Export driver registration function from index
- Create
src/driver.ts- main DynamoDBDriver class - Create
src/config.ts- DynamoDB-specific config types - Create
src/types.ts- DynamoDB types and interfaces - Create
src/index.ts- exports and auto-registration - Implement
DriverPlugininterface from bun-query-builder - Export
registerDynamoDBDriver()convenience function
IMPORTANT: All AWS interactions go through
ts-cloud(~/Code/ts-cloud). Implement missing DynamoDB APIs there, then import into bun-dynamodb-driver. No direct AWS SDK usage.
- Create
packages/core/src/aws/dynamodb.tsin ts-cloud - Implement
DynamoDBClientclass using existingmakeAWSRequest+ AWS Signature V4 - Implement
createTable(params)- CreateTable API - Implement
deleteTable(tableName)- DeleteTable API - Implement
describeTable(tableName)- DescribeTable API - Implement
listTables()- ListTables API - Implement
updateTable(params)- UpdateTable API (for GSI changes) - Implement
waitForTableActive(tableName)- poll until ACTIVE - Implement
waitForTableDeleted(tableName)- poll until deleted - Export from
packages/core/src/aws/index.ts
- Implement
getItem(tableName, key)- GetItem API - Implement
putItem(tableName, item, options?)- PutItem API - Implement
updateItem(tableName, key, updates, options?)- UpdateItem API - Implement
deleteItem(tableName, key, options?)- DeleteItem API - Implement
query(tableName, params)- Query API - Implement
scan(tableName, params?)- Scan API - Support
ConsistentReadoption on all read operations - Support
ReturnValuesoption on write operations - Support
ConditionExpressionfor conditional writes
- Implement
batchGetItem(params)- BatchGetItem API - Implement
batchWriteItem(params)- BatchWriteItem API - Handle
UnprocessedKeys/UnprocessedItemswith retries - Auto-chunk requests exceeding limits (25 write / 100 read)
- Implement exponential backoff for throttling
- Implement
transactGetItems(params)- TransactGetItems API - Implement
transactWriteItems(params)- TransactWriteItems API - Handle
TransactionCanceledExceptionwith details - Validate 100 item limit before request
- Create
packages/core/src/aws/dynamodb-expressions.ts - Implement
buildKeyConditionExpression(conditions) - Implement
buildFilterExpression(conditions) - Implement
buildUpdateExpression(updates) - Implement
buildProjectionExpression(attributes) - Implement
buildConditionExpression(conditions) - Auto-generate
ExpressionAttributeNames(#name placeholders) - Auto-generate
ExpressionAttributeValues(:value placeholders) - Handle reserved word escaping automatically
- Expand
packages/aws-types/src/dynamodb.tswith runtime types - Add
DynamoDBItemtype (Record<string, AttributeValue>) - Add
AttributeValuetype (S, N, B, BOOL, NULL, M, L, SS, NS, BS) - Add
KeyCondition,FilterConditiontypes - Add
UpdateExpressiontypes (SET, REMOVE, ADD, DELETE) - Add
QueryInput,QueryOutputtypes - Add
ScanInput,ScanOutputtypes - Add
TransactWriteItem,TransactGetItemtypes
- Create
packages/core/src/aws/dynamodb-document.ts - Implement automatic JS ↔ DynamoDB type marshalling
- Marshal
string→{ S: string } - Marshal
number→{ N: string }(DynamoDB uses string for numbers) - Marshal
boolean→{ BOOL: boolean } - Marshal
null→{ NULL: true } - Marshal
Array→{ L: [...] } - Marshal
Object→{ M: {...} } - Marshal
Set<string>→{ SS: [...] } - Marshal
Set<number>→{ NS: [...] } - Marshal
Buffer/Uint8Array→{ B: base64 } - Implement
unmarshall(item)for reverse conversion - Export
DocumentClientwrapper with auto-marshalling
These tasks are implemented in the
~/Code/bun-dynamodb-driversponsorware repo.
- Implement
quoteIdentifier()(no-op for DynamoDB) - Implement
getColumnType()mapping to DynamoDB types (S, N, B, BOOL, NULL, M, L, SS, NS, BS) - Implement
createTable()generating DynamoDB CreateTable params - Implement
createIndex()for GSI/LSI creation - Implement
addColumn()(no-op, DynamoDB is schemaless) - Implement
modifyColumn()(no-op, DynamoDB is schemaless) - Implement
dropTable()generating DeleteTable params - Implement
dropColumn()(no-op) - Implement
dropIndex()for GSI removal - Implement
createMigrationsTable()for migration tracking item - Implement
getExecutedMigrationsQuery()for migration state - Implement
recordMigrationQuery()for migration tracking - Import
DynamoDBClientfromts-cloud
- Create
src/query-translator.ts - Translate
where('id', '=', 123)→ pk/sk key condition automatically - Translate
where('userId', '=', 456)→ GSI query automatically - Translate arbitrary where → filter expression (with warning)
- Auto-select best index based on where conditions
- Translate
orderBy→ ScanIndexForward boolean - Translate
limit→ Limit parameter - Translate
select→ ProjectionExpression - Handle entity type filtering automatically (invisible to user)
- Create
src/query-builder/DynamoQueryBuilder.ts - Implement fluent API matching bun-query-builder interface
- Implement
table(name)/from(name)method - Implement
select(...columns)for projection expressions - Implement
where(key, operator, value)for key conditions - Implement
andWhere()/orWhere()for filter expressions - Implement
whereIn(key, values)for IN conditions - Implement
whereBetween(key, min, max)for BETWEEN - Implement
whereBeginsWith(key, prefix)for begins_with - Implement
whereContains(key, value)for contains - Implement
whereExists(key)/whereNotExists(key) - Implement
orderBy(key, direction)(ScanIndexForward) - Implement
limit(n)for Limit parameter - Implement
offset()with pagination token handling
- Implement
get()/first()- GetItem operation - Implement
all()/execute()- Query/Scan operations - Implement
insert(data)- PutItem operation - Implement
insertMany(items)- BatchWriteItem operation - Implement
update(data)- UpdateItem operation - Implement
updateMany(items)- BatchWriteItem for updates - Implement
delete()- DeleteItem operation - Implement
deleteMany(keys)- BatchWriteItem for deletes - Implement
upsert(data)- conditional PutItem - Implement
increment(key, amount)- atomic counter update - Implement
decrement(key, amount)- atomic counter update - Implement
append(key, values)- list append operation - Implement
remove(key)- remove attribute operation
- Auto-detect when Query is possible (pk provided)
- Auto-detect when Scan is required (no pk)
- Implement
useIndex(indexName)for GSI/LSI selection - Implement automatic index selection based on query conditions
- Add
forceScan()method to override Query preference - Add
forceQuery()method with validation - Implement parallel scan with
parallelScan(segments) - Add query cost estimation/warnings
- Implement
paginate(perPage)with LastEvaluatedKey handling - Implement
cursorPaginate()using ExclusiveStartKey - Implement
chunk(size, callback)for batch processing - Implement
chunkById(size, callback)variant - Implement
each(callback)for streaming results - Return pagination metadata (hasMore, cursor, count)
- Implement
count()- with optional server-side Select: 'COUNT' - Implement
sum(attribute)- client-side aggregation - Implement
avg(attribute)- client-side aggregation - Implement
min(attribute)- client-side aggregation - Implement
max(attribute)- client-side aggregation - Add warning for large dataset aggregations
- Consider PartiQL for complex aggregations
- Implement
transaction(callback)wrapper - Implement
transactWrite(items)- TransactWriteItems - Implement
transactGet(keys)- TransactGetItems - Handle transaction conflicts with retries
- Implement
conditionCheck()for transaction conditions - Add transaction item limit validation (100 items max)
- Implement
batchGet(keys)- BatchGetItem with chunking - Implement
batchWrite(items)- BatchWriteItem with chunking - Handle unprocessed items with exponential backoff
- Implement automatic chunking for >25 items (write) / >100 items (read)
- Add progress callbacks for large batches
- Implement
when(condition, callback)for conditional building - Implement
unless(condition, callback)inverse - Implement
ifNotExists()for conditional puts - Implement
ifExists()for conditional updates - Implement
expectVersion(version)for optimistic locking - Handle ConditionalCheckFailedException gracefully
- Implement
increment(attribute, amount)- ADD operation - Implement
decrement(attribute, amount)- ADD with negative - Implement
addToSet(attribute, values)- ADD to SS/NS - Implement
removeFromSet(attribute, values)- DELETE from SS/NS - Implement
appendToList(attribute, values)- list_append - Implement
prependToList(attribute, values)- list_append reversed - Implement
removeFromList(attribute, indexes)- REMOVE list[i]
- Implement unique constraint via conditional writes
- Support
unique: trueon model attributes - Create unique constraint items (pk:
UNIQUE#email#user@example.com) - Transaction-based unique validation on insert/update
- Handle unique constraint violations with clear errors
- Implement
map(callback)- transform each result - Implement
filter(callback)- client-side filtering - Implement
reduce(callback, initial)- aggregate results - Implement
groupBy(attribute)- group results - Implement
keyBy(attribute)- index results by attribute - Implement
sortBy(attribute)- client-side sorting - Implement
unique(attribute?)- deduplicate results - Implement
flatten()- flatten nested results
- Implement
rawQuery(params)- direct Query API access - Implement
rawScan(params)- direct Scan API access - Implement
rawGet(params)- direct GetItem API access - Implement
rawPut(params)- direct PutItem API access - Implement
rawUpdate(params)- direct UpdateItem API access - Implement
rawDelete(params)- direct DeleteItem API access - Implement
rawBatchGet(params)- direct BatchGetItem - Implement
rawBatchWrite(params)- direct BatchWriteItem - Implement
rawTransactWrite(params)- direct TransactWriteItems - Implement
partiql(statement, params)- PartiQL execution
- Create
src/models/types.tswith abstract Model class and interfaces - Create
src/models/DynamoDBModel.tsconcrete implementation - Implement static
tableproperty (maps to entity type) - Implement static
primaryKeyproperty - Implement static
pkPrefix/skPrefixproperties - Implement
attributesdefinition matching Stacks models - Implement
fillable/guardedattribute protection - Implement
hiddenattributes for serialization - Implement
castsfor type coercion (DynamoDBCastDefinition) - Implement dates for date attribute handling
- Implement
Model.find(pk, sk?)static method - Implement
Model.findOrFail(pk, sk?)with exception - Implement
Model.findMany(keys)batch get - Implement
Model.create(attributes)static method - Implement
Model.query()returning query builder - Implement
model.save()instance method - Implement
model.update(attributes)instance method - Implement
model.delete()instance method - Implement
model.refresh()to reload from DB - Implement
model.replicate()to clone
- Implement
hasOne(Model, fkAttribute)relationship - Implement
hasMany(Model, fkAttribute)relationship - Implement
belongsTo(Model, fkAttribute)relationship - Implement
belongsToMany(Model, pivotEntity)relationship - Implement relationship eager loading with
with() - Implement
withCount()for relationship counts - Implement
has()/doesntHave()existence checks - Implement
whereHas()for relationship conditions - Handle single-table relationship queries efficiently
- Implement
timestampstrait (createdAt, updatedAt) - Implement
softDeletestrait (deletedAt) - Implement
uuidtrait for UUID primary keys - Implement
ttltrait for auto-expiring items - Implement
versioningtrait for optimistic locking - Implement
useSeedertrait configuration (deferred) - Implement
useSearchtrait (for OpenSearch integration later)
- Implement
creating/createdevents - Implement
updating/updatedevents - Implement
deleting/deletedevents - Implement
saving/savedevents (create + update) - Implement
restoring/restoredevents (soft delete) - Implement
forceDeleting/forceDeletedevents - Add hook registration via
addHook()static method - Add global scopes support via
addGlobalScope()
- Implement
getaccessors (viaDynamoDBCastDefinition.get) - Implement
setmutators (viaDynamoDBCastDefinition.set) - Implement attribute casting system (
setCasts()) - Support custom cast classes (
DynamoDBCastDefinition) - Handle JSON attribute casting
- Handle date/datetime casting
- Implement global scopes via
addGlobalScope() - Implement local scopes via
scope()on query builder - Support scope parameters
- Implement
toJSON()method (respectshidden) - Implement
toArray()method - Implement
only(...keys)for selective serialization - Implement
except(...keys)to exclude attributes - Implement
makeVisible(...keys)to temporarily unhide - Implement
makeHidden(...keys)to temporarily hide - Handle relationships in serialization
- Track original attributes on load (
_original) - Implement
isDirty(attribute?)method - Implement
isClean(attribute?)method - Implement
wasChanged(attribute?)after save - Implement
getOriginal(attribute?)method - Implement
getDirty()- return changed attributes only - Implement
getChanges()- return changes after save - Implement
syncOriginal()after save - Only send changed attributes in UpdateItem (via
buildUpdateData)
FULLY AUTOMATED: Run
dbtooling migrateand the system reads your Stacks models, generates the optimal single-table schema with GSIs, and creates/updates the DynamoDB table. No manual schema design required.
- Create
src/migrations/AutoSchemaGenerator.ts(582 lines) - Scan all Stacks models from configured path automatically
- Generate single CreateTable definition with:
- pk (String) + sk (String) composite primary key
- GSI1 with gsi1pk/gsi1sk (auto-derived from relations)
- GSI2 with gsi2pk/gsi2sk (auto-derived from relations)
- Additional GSIs as needed (up to 5 recommended)
- Auto-determine billing mode from config
- Auto-configure TTL if any model uses
useTtltrait - Auto-configure streams if any model uses event sourcing
- Output human-readable schema summary (
formatSchemaSummary())
- Create
src/migrations/SchemaDiffer.ts(1,042 lines) - Compare current models vs last migration state
- Detect new models → new entity types (no table change needed)
- Detect new relationships → may need new GSI
- Detect removed relationships → GSI can be removed
- Detect attribute changes → no table change (schemaless)
- Generate migration plan with changes
- Warn about breaking changes (GSI removal affects queries)
- Create
src/migrations/AutoMigrationRunner.ts(713 lines) - On first run: CreateTable with all derived GSIs
- On subsequent runs: UpdateTable for GSI changes only
- Handle GSI creation limits (1 at a time, wait for ACTIVE)
- Handle GSI deletion (wait for removal)
- Track migration state in
_migrationsitem in same table - Support
--dry-runto preview changes - Support
--forceto skip confirmations - Rollback support for failed migrations
- Create
src/migrations/AccessPatternDocGenerator.ts(828 lines) - Auto-generate markdown doc with all access patterns
- Show which index serves each access pattern
- Show example pk/sk values for each entity
- Show example queries for each relationship
- Output to file via CLI (
--outputoption) - Multiple format support (markdown, JSON, summary)
- Create
src/migrations/DataMigrator.ts(736 lines) - Auto-backfill new GSI attributes on existing items
- Handle large datasets with parallel batch writes
- Progress reporting with ETA
- Resume capability for interrupted migrations
- Validate data integrity post-migration
- Create
src/seeders/Seeder.tsbase class - Implement
run(ctx)abstract method - Implement
orderproperty for execution order - Create
src/seeders/SeederRunner.ts - Discover seeders from configured directory
- Execute seeders in order
- Create
src/factories/Factory.tsbase class - Built-in helpers for fake data (uniqueEmail, randomInt, etc.)
- Define factories with
Factory.define() - Implement
Factory.for().count(n).create() - Implement
Factory.for().make()(without persisting) - Support factory states for variations
- Add
dbtooling seedcommand - Add
dbtooling seed --class=UserSeederspecific seeder - Add
dbtooling make:seeder <Name>generator - Add
dbtooling make:factory <Name>generator - Add
dbtooling db:fresh(wipe + migrate + seed)
- Add
dbtooling table:createcommand - Add
dbtooling table:deletecommand - Add
dbtooling table:describecommand - Add
dbtooling table:listcommand - Add
dbtooling table:wait(wait for ACTIVE)
- Add
dbtooling migratecommand - Add
dbtooling migrate:statuscommand - Add
dbtooling migrate:rollbackcommand - Add
dbtooling migrate:freshcommand - Add
dbtooling migrate:generatefrom models
- Add
dbtooling query <table>interactive query - Add
dbtooling scan <table>scan with filters - Add
dbtooling get <table> <pk> [sk]get item - Add
dbtooling put <table> <json>put item - Add
dbtooling delete <table> <pk> [sk]delete item
- Add
dbtooling export <table> --format=json|csv - Add
dbtooling import <table> <file> - Add
dbtooling backup <table>to S3 - Add
dbtooling restore <table> <backup> - Add
dbtooling consoleinteractive REPL - Add
dbtooling access-patternsshow patterns from models
- Enhance
dbtooling start(launch local DynamoDB) - Add
dbtooling stopcommand - Add
dbtooling statuscommand - Add
dbtooling logscommand - Add
dbtooling reset(stop + clear data + start)
- Add
dbtooling env:list- show configured environments - Add
dbtooling env:switch <env>- switch active environment - Add
dbtooling env:diff <env1> <env2>- compare table schemas - Support
.envfile for environment-specific config - Support
--envflag on all commands - Prevent accidental production operations (confirmation prompts via --force flags)
- Add
dbtooling ci:migrate- non-interactive migration for CI - Add
dbtooling ci:validate- validate models without applying - Add
dbtooling ci:test- run tests against ephemeral table - Generate migration plan as JSON for review (via migrate:generate --format json)
- Support GitHub Actions / GitLab CI examples
Note: External repo integrations (ts-cloud, bun-query-builder, bun-dynamodb-driver) are deferred as they require work in separate repositories.
- Run
bun linkin ts-cloud - Run
bun link ts-cloudin dynamodb-tooling - Run
bun linkin bun-query-builder - Run
bun link bun-query-builderin dynamodb-tooling - Run
bun linkin bun-dynamodb-driver - Run
bun link bun-dynamodb-driverin dynamodb-tooling - Add ts-cloud as dependency in package.json
- Add bun-query-builder as dependency in package.json
- Add bun-dynamodb-driver as dependency in package.json
- Export query builder from dynamodb-tooling index
- Create
src/index.tsunified exports - Export
DynamoDBModelbase class - Export all config types
- Export migration utilities
- Export seeder utilities
- Export factory utilities
All query builder features are implemented in DynamoDBQueryBuilder:
-
select()with projections -
where()/andWhere()/orWhere() -
whereIn()/whereNotIn() -
whereBetween() -
whereNull()/whereNotNull() -
whereBeginsWith()/whereContains()(DynamoDB-specific) -
orderBy()/orderByDesc() -
limit()/take() -
paginate()/cursorPaginate() -
insert()/insertMany() -
update()- bulk updates -
delete()/forceDelete() -
count()/sum()/avg()/min()/max() -
chunk()/chunkById() -
with()eager loading -
withCount() -
has()/doesntHave()/whereHas() -
scope()query scopes + global scopes - Model hooks (before/after CRUD) via
addHook() - Soft deletes (
withTrashed(),onlyTrashed()) -
latest()/oldest() -
first()/firstOrFail() -
find()/findOrFail()/findMany() -
exists()/doesntExist() -
toQuery()for debugging -
useIndex()for GSI/LSI selection
- Test config loading and validation (
test/toolkit.test.ts) - Test SingleTableMapper transformations (
test/single-table.test.ts) - Test marshalling/unmarshalling (
test/single-table.test.ts) - Test key pattern generation (
test/single-table.test.ts) - Test access pattern generation (
test/single-table.test.ts) - Test query builder SQL generation (PartiQL)
- Test Model CRUD operations
- Test relationship loading
- Test pagination token handling
- Test batch operation chunking
- Test transaction building
- Test schema generation from registry (
test/migrations.test.ts) - Test schema summary formatting
- Test schema diffing (
test/migrations.test.ts) - Test migration state creation
- Test change detection
- Test migration plan generation
- Test migration execution end-to-end
- Test factory definition (
test/factories.test.ts) - Test factory make() without persisting
- Test factory states
- Test factory overrides
- Test factory sequences
- Test helper functions (uniqueEmail, randomInt, etc.)
- Test against DynamoDB Local
- Test CreateTable / DeleteTable
- Test CRUD operations end-to-end
- Test GSI queries
- Test transactions
- Test batch operations with retries
- Test pagination with real data
- Test soft deletes
- Create
createTestTable()helper for isolated test tables - Create
seedTestData()helper using factories - Create
cleanupTestTable()helper - Implement test fixtures system
- Add
Model.fake()method using factory - Support in-memory mock for unit tests (no DynamoDB)
- Add query assertion helpers (
assertQueried,assertInserted) - Add consumed capacity assertions for performance tests
- Add comprehensive feature list
- Add installation instructions
- Add quick start guide
- Add configuration reference
- Add single-table design explanation
- Add migration guide
- Add model definition guide
- Document CLI commands
- Document Query Builder API
- Document Factory System
- Document Seeder System
- Generate TypeDoc documentation
- Add JSDoc to all public APIs
- Write "Single-Table Design with Stacks Models" guide
- Write "Migrating from SQL to DynamoDB" guide
- Write "Access Pattern Design" guide
- Write "Performance Optimization" guide
- Write "Testing with DynamoDB Local" guide
- Create example Stacks models for DynamoDB
- Create example queries for common patterns
- Create example migrations
- Create example seeders
- Create full example application
GOAL: Extremely narrow types with zero
any, full inference, and compile-time validation of all DynamoDB operations.
- Create branded
PartitionKey<Entity>type (not juststring) - Create branded
SortKey<Entity>type (not juststring) - Create branded
GSI1PK<Entity>/GSI1SK<Entity>types - Create branded
EntityType<Name>literal type - Prevent mixing keys from different entities at compile time
- Type-safe key construction:
pk('USER', userId)returnsPartitionKey<User> - Validate key format at type level using template literal types
- Infer
Attributes<Model>from modelattributesdefinition - Infer
RequiredAttributes<Model>vsOptionalAttributes<Model> - Infer
FillableAttributes<Model>fromfillablearray - Infer
HiddenAttributes<Model>fromhiddenarray - Infer
RelationshipTypes<Model>fromhasOne/hasMany/etc. - Generate discriminated union for all entity types
- Support
constassertions for literal inference - Infer attribute types from
castsdefinition
- Generic
QueryBuilder<TModel, TSelected, TWith>with state tracking -
where()narrows toQueryBuilder<TModel, TSelected, TWith>with pk/sk context -
select('name', 'email')returnsPick<TModel, 'name' | 'email'> -
select()with no args returns fullTModel -
with('posts')addsposts: Post[]to return type -
withCount('posts')addspostsCount: numberto return type - Chain methods preserve and narrow types through entire chain
-
first()returnsTModel | null,firstOrFail()returnsTModel -
get()returnsTModel[]with correct narrowed type
-
where(key)only acceptskeyof Attributes<TModel> -
where(key, op, value)-valuetype matchesAttributes<TModel>[key] -
orderBy(key)only accepts sortable attributes -
increment(key)only accepts numeric attributes -
whereIn(key, values)-valuesisArray<Attributes<TModel>[key]> - Operator overloads:
=works on all,>only on number/string -
beginsWithonly valid on string attributes
-
insert(data)-datamust satisfyFillableAttributes<TModel> -
insert(data)- reject unknown keys at compile time -
update(data)-dataisPartial<FillableAttributes<TModel>> -
create(data)- validate required fields present -
upsert(data)- same validation as insert - Readonly attributes rejected in update operations
- Auto-managed fields (
createdAt,pk,sk) not in insert type
-
hasOne<Profile>()returnsProfile | null -
hasMany<Post>()returnsPost[] -
belongsTo<Team>()returnsTeam | null -
with('posts')-'posts'must be valid relationship name -
with('posts', query => ...)- callback receivesQueryBuilder<Post> - Nested
with('posts.comments')infers through relationship chain -
whereHas('posts', q => q.where(...))- type-safe nested query
-
pluck('email')returnsArray<Attributes<TModel>['email']> -
value('email')returnsAttributes<TModel>['email'] | null -
count()returnsnumber -
exists()returnsboolean -
paginate()returnsPaginatedResult<TModel>with typed metadata -
chunk()callback receivesTModel[] -
map(fn)return type inferred from callback
-
AttributeValueas discriminated union (S | N | B | BOOL | NULL | M | L | SS | NS | BS) - Type-safe marshalling:
marshall<T>(obj: T): DynamoDBItem<T> - Type-safe unmarshalling:
unmarshall<T>(item: DynamoDBItem<T>): T -
KeyConditionExpressionbuilder with type-safe attribute references -
FilterExpressionbuilder with type-safe comparisons -
UpdateExpressionbuilder: SET/REMOVE/ADD/DELETE with correct types -
ProjectionExpressionfromselect()with attribute validation
-
type UserPK = \USER#${string}`` - validate pk format -
type UserSK = \USER#${string}` | `PROFILE#${string}`` - union for collections -
type GSI1PK = \TEAM#${string}`` - relationship keys - Auto-generate key type from model definition
- Compile-time validation of key patterns
- Extract entity type from key:
EntityFromPK<'USER#123'>=User
-
Configtype with no optionalanyproperties -
BillingModeas'PAY_PER_REQUEST' | 'PROVISIONED'literal union -
StreamViewTypeas literal union -
ReturnValueas literal union - Capacity config only valid when
billingMode: 'PROVISIONED' - Conditional config types based on feature flags
- Discriminated union for all error types
-
DynamoDBErrorbase withcodediscriminant -
ItemNotFoundError<TModel>includes model type -
ValidationErrorwith typed field errors -
ConditionalCheckFailedErrorwith condition details -
TransactionCancelledErrorwith per-item reasons - Type guards:
isItemNotFoundError(e): e is ItemNotFoundError
-
Modelbase class with generic:class Model<T extends ModelDefinition> - Infer model type from class:
type UserType = InferModel<typeof User> - Constrain relationships:
hasMany<T extends Model>(...) - Factory type inference:
Factory<User>producesUserinstances - Query builder generic flows through all operations
- No
anyin public API - useunknownwith type guards where needed
- Detect missing pk in Query operation at compile time
- Warn when Scan is required (no pk condition)
- Validate GSI exists when
useIndex()called - Validate sort key operators (
begins_withonly on sk) - Validate transaction item count at type level (max 100)
- Validate batch size at type level (max 25 write, 100 read)
- JSDoc comments on all public types
-
@exampletags with working code -
@seelinks to DynamoDB documentation -
@deprecatedmarkers with migration path - Hover information shows full type expansion
- Go-to-definition works for generated types
- Use
tsdorexpect-typefor type-level tests - Test that invalid queries fail to compile
- Test that return types are correctly narrowed
- Test branded types prevent mixing
- Test template literal types validate formats
- Test discriminated unions exhaustiveness
- CI runs type tests alongside unit tests
- Create custom error classes (ItemNotFoundError, ValidationError, etc.)
- Add detailed error messages with suggested fixes
- Implement query logging with timing
- Add
debug()method to print query params before execution - Add
explain()method to show which index will be used - Implement retry logic with exponential backoff for throttling
- Handle ProvisionedThroughputExceededException gracefully
- Parse
validation.rulefrom Stacks models - Validate before insert/update operations
- Return structured validation errors
- Support async validation rules
- Integrate with
@stacksjs/ts-validation
- VSCode extension for model visualization
- Show access patterns inline in model files
- Autocomplete for query builder methods
- Hover documentation for DynamoDB operations
- Lint rules for inefficient query patterns
- Implement connection pooling for DynamoDB client
- Support keep-alive connections
- Add connection health checks
- Handle connection timeouts gracefully
- Warn when using Scan instead of Query
- Warn when filter expressions filter large result sets
- Suggest GSI creation for common query patterns
- Implement query result caching (LRU)
- Add consumed capacity tracking and reporting
- Implement read/write capacity budgeting
- Implement adaptive batch sizing based on item size
- Parallel batch execution for large datasets
- Implement request coalescing for concurrent reads
- Add batch operation progress events
- Track consumed read/write capacity units
- Estimate query costs before execution
- Add cost alerts/warnings for expensive operations
- Generate cost reports per entity type
- Support table-per-tenant (separate tables)
- Support prefix-per-tenant (pk prefix:
TENANT#123#USER#456) - Support attribute-per-tenant (tenantId attribute + GSI)
- Auto-inject tenant context into all queries
- Prevent cross-tenant data access
- Add
multiTenancy.enabledconfig - Add
multiTenancy.strategy(table,prefix,attribute) - Add
multiTenancy.tenantIdAttributeconfig - Add
multiTenancy.tenantResolvercallback - Support tenant-specific table capacity
- Add OpenSearch Serverless config
- Implement full-text search queries
- Sync DynamoDB to OpenSearch via streams
- Implement
search()method on models
- Add DAX cluster config
- Implement DAX client wrapper
- Add caching layer with DAX
- Handle DAX failover
- Add stream consumer utilities
- Implement change data capture
- Add event sourcing helpers
- Integrate with Lambda handlers
- Add multi-region config
- Handle global table replication
- Add region-aware queries
- Implement
raw()for PartiQL - Add PartiQL query builder
- Support PartiQL transactions
- Add PITR config option
- Implement restore to point-in-time
- Add backup scheduling utilities
- Implement
backup()method - Implement
restore()from backup - Add backup listing and management
- Support cross-region backup restore
- Structured logging for all operations
- Log query patterns and execution times
- Log consumed capacity per operation
- Configurable log levels (debug, info, warn, error)
- Integration with popular loggers (pino, winston)
- Expose Prometheus-compatible metrics
- Track operation latency histograms
- Track error rates by operation type
- Track cache hit/miss ratios
- Track batch operation sizes
- OpenTelemetry integration
- Trace spans for each DynamoDB operation
- Propagate trace context through batch operations
- Add custom attributes (table, entity type, index used)
- Support client-side encryption
- Integrate with AWS KMS for key management
- Encrypt sensitive attributes automatically
- Support attribute-level encryption config
- Implement row-level security patterns
- Support IAM condition keys in queries
- Add audit logging for data access
- Implement data masking for sensitive fields
- Create
dbtooling studio- web UI for table exploration - Visual entity relationship diagram from models
- Query builder UI with live preview
- Item editor with validation
- Access pattern visualization matrix
- Generate TypeScript types from existing DynamoDB table
- Generate Stacks models from existing table structure
- Generate access pattern documentation from query logs
- Scaffold new model with CLI (
dbtooling make:model)
- Hot reload models during development
- Watch mode for auto-migration on model changes
- Diff preview before applying migrations
- Snapshot testing for query outputs
For teams migrating from ElectroDB or wanting familiar patterns.
- Support
Entityclass with ElectroDB-style schema - Support
Servicefor grouping entities - Map ElectroDB attributes to Stacks model format
- Support ElectroDB composite key syntax
- Import existing ElectroDB entities
- Convert ElectroDB schema to Stacks models
- Document migration guide from ElectroDB
- Generate CloudFormation/SAM template from models
- Generate Terraform configuration from models
- Generate Pulumi configuration from models
- Support CDK constructs generation
- Optimized cold start initialization
- Connection reuse across invocations
- Middleware for Lambda handlers
- Request context propagation (tracing, tenant)
- Import from JSON files
- Import from CSV with column mapping
- Import from S3 (large datasets)
- Import from other DynamoDB tables
- Validate data against model schema before import
- Support dry-run mode for import preview
- Resume interrupted imports
- Export to JSON (single file or chunked)
- Export to CSV with configurable columns
- Export to S3 for large tables
- Export with filters (by entity type, date range, etc.)
- Support incremental exports (changes since last export)
- Export access patterns as documentation
- Sync between DynamoDB tables (dev → staging → prod)
- Sync subset of data for development
- Anonymize/mask sensitive data during sync
- Two-way sync with conflict resolution
- Implement LRU cache for query results
- Cache invalidation on writes
- Configurable TTL per entity type
- Cache key generation from query params
- Cache statistics and hit rate monitoring
- Deduplicate identical concurrent requests
- Batch concurrent GetItem requests automatically
- Request coalescing window configuration
- Prefetch related entities on relationship access
- Predictive prefetching based on access patterns
- Background refresh for frequently accessed data
- Support
morphOnerelationship (one-to-one polymorphic) - Support
morphManyrelationship (one-to-many polymorphic) - Support
morphToManyrelationship (many-to-many polymorphic) - Auto-generate polymorphic type attribute
- Query across polymorphic types
- Support
hasMany('self')for tree structures - Implement
ancestors()/descendants()methods - Support nested set model for hierarchies
- Implement
getTree()for full hierarchy fetch
- Support multi-attribute primary keys
- Auto-generate composite sk patterns
- Query by partial composite key
- Store events as immutable items
- Event versioning and ordering
- Snapshot support for aggregate reconstruction
- Event replay capabilities
- Build read models from events
- Async projection updates via streams
- Projection rebuild from event history
- Generate GraphQL schema from Stacks models
- Map relationships to GraphQL connections
- Support pagination (Relay-style cursors)
- Generate resolvers automatically
- Implement DataLoader for batching
- Automatic N+1 prevention
- Per-request caching
- One table per application - All entities in single table
- Composite keys - pk/sk pattern for all items
- GSIs for access patterns - Overloaded GSIs for different queries
- Entity type attribute - Distinguish item types
- Denormalization - Duplicate data for query efficiency
- Hierarchical keys -
USER#123#ORDER#456patterns
- No JOINs - use denormalization and GSIs
- No arbitrary WHERE - must use key conditions
- No GROUP BY - client-side aggregation
- No OFFSET - cursor-based pagination only
- Transactions limited to 100 items
- Batch operations limited to 25 writes / 100 reads
Stacks models work as-is with automatic translation:
| Stacks Model Property | DynamoDB Translation (Automatic) |
|---|---|
name: 'User' |
Entity type prefix USER# |
table: 'users' |
Ignored (single table design) |
primaryKey: 'id' |
Sort key pattern USER#{id} |
attributes: {...} |
Item attributes (schemaless) |
hasOne: ['Profile'] |
GSI1 for reverse lookup |
hasMany: ['Post'] |
Query with sk begins_with |
belongsTo: ['Team'] |
pk stores TEAM#{teamId} ref |
belongsToMany: ['Role'] |
Adjacency list items |
traits.useTimestamps |
Auto-add createdAt/updatedAt |
traits.useSoftDeletes |
Auto-add deletedAt + filter |
traits.useUuid |
UUID generation for pk |
indexes: [...] |
Additional GSI definitions |
validation: {...} |
Client-side validation |
factory: fn |
Seeder data generation |
// Stacks Model (app/models/User.ts) - NO CHANGES NEEDED
export default {
name: 'User',
primaryKey: 'id',
hasMany: ['Post', 'Comment'],
belongsTo: ['Team'],
attributes: {
email: { unique: true, validation: {...} },
name: { fillable: true },
},
traits: { useTimestamps: true },
}
// Automatically generates DynamoDB items like:
// {
// pk: 'USER#123',
// sk: 'USER#123',
// gsi1pk: 'TEAM#456', // from belongsTo
// gsi1sk: 'USER#123',
// _et: 'User', // entity type
// email: 'user@example.com',
// name: 'John Doe',
// createdAt: '2024-01-01T00:00:00Z',
// updatedAt: '2024-01-01T00:00:00Z',
// }
// Automatically generates access patterns:
// - Get User by ID: pk = 'USER#123', sk = 'USER#123'
// - Get User's Posts: pk = 'USER#123', sk begins_with 'POST#'
// - Get Users by Team: GSI1 where gsi1pk = 'TEAM#456'