orm-dynamodb
v1.2.3
Published
Lightweight TypeScript decorators for modeling DynamoDB items as classes
Maintainers
Readme
orm-dynamodb
Lightweight TypeScript decorators for modeling DynamoDB items as classes.
Project site: https://ssz360.github.io/ORM-DynamoDB/
✨ Features
- 🎯 Type-safe decorators for DynamoDB entities with full TypeScript support
- 🔗 Entity relationships with
@LinkObjectand@LinkArraydecorators and automatic link loading - 🔄 Custom serialization with
@ToDbModeland@FromDbModeltransformers - 🛠️ Intuitive API with
insert(),update(),delete(),get(), andquery()methods - 🚀 Zero configuration - works out of the box with AWS SDK v3
- 📦 Tiny footprint - lightweight with minimal dependencies
📦 Install
npm install orm-dynamodbDependencies: @aws-sdk/client-dynamodb and @aws-sdk/lib-dynamodb
TypeScript Configuration
Enable decorators in your tsconfig.json:
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}🚀 Quick Start
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
import {
BaseEntity,
Entity,
FromDbModel,
HashKeyValue,
LinkObject,
SortKeyValue,
ToDbModel
} from 'orm-dynamodb';
BaseEntity.configure(new DynamoDBClient({ region: process.env.AWS_REGION || 'us-east-1' }));
@Entity('content', 'pk', 'sk')
class Author extends BaseEntity {
@HashKeyValue
get hashKey() {
return 'AUTHOR';
}
@SortKeyValue
get sortKey() {
return this.authorId;
}
authorId: string;
displayName: string;
constructor(authorId: string = '', displayName: string = '') {
super();
this.authorId = authorId;
this.displayName = displayName;
}
}
@Entity('content', 'pk', 'sk')
class Post extends BaseEntity {
@HashKeyValue
get hashKey() {
return 'POST';
}
@SortKeyValue
get sortKey() {
return this.slug;
}
slug: string;
title: string;
publishedAt: Date | null;
@LinkObject(Author)
author: Author | undefined;
constructor(slug: string = '', title: string = '', publishedAt: Date | null = null) {
super();
this.slug = slug;
this.title = title;
this.publishedAt = publishedAt;
}
@ToDbModel
static toDbModel(post: Post) {
return {
publishedAt: post.publishedAt ? post.publishedAt.toISOString() : null
};
}
@FromDbModel
static fromDbModel(item: { publishedAt?: string | null }) {
return {
publishedAt: item.publishedAt ? new Date(item.publishedAt) : null
};
}
}
async function run() {
const post = new Post('decorators-with-dynamodb', 'Decorators With DynamoDB', new Date());
post.author = new Author('ada-lovelace', 'Ada Lovelace');
await post.insert();
const loaded = await Post.get('decorators-with-dynamodb');
await loaded?.loadLinks();
console.log(loaded?.author?.displayName);
}📚 API
Decorators & Classes
BaseEntity- Base class for all entities with CRUD operations@Entity(tableName, hashKey, sortKey?, dbClient?)- Marks a class as a DynamoDB entity@HashKeyValue- Defines the hash key value getter@SortKeyValue- Defines the sort key value getter@LinkObject(EntityClass, options?)- Creates a reference to a single entity (options:{ inline?: boolean })@LinkArray(EntityClass, options?)- Creates a reference to an array of entities (options:{ inline?: boolean })@ToDbModel- Custom serialization when writing to DynamoDB@FromDbModel- Custom deserialization when reading from DynamoDB
Core Methods
**Iinsert()` - Insert or update the entity (cascade saves linked entities)
update(attributes)- Partial update of the entitydelete()- Remove the entity from DynamoDBloadLinks()- Load all linked entities
Static Methods:
get(sortKeyValue)- Retrieve a single entity by sort keyquery(options)- Query entities with custom optionsqueryAll(options?)- Query all entities in the partitionqueryEquals(sortKeyValue, options?)- Query entities with exact sort key matchqueryStartsWith(prefix, options?)- Query entities with sort key prefixqueryBetween(start, end, options?)- Query entities with sort key in rangequeryGreaterThan(value, options?)- Query entities with sort key greater than valuequeryLessThan(value, options?)- Query entities with sort key less than valuequery(options)- Query entities in the partitionconfigure(client)- Set the DynamoDB client globally
Helper methods accept a query options object, for example:
const recentOrders = await Order.queryGreaterThan('2024-01-01', {
limit: 20,
scanIndexForward: false,
});Features
- ✅ Explicit DynamoDB client configuration via
BaseEntity.configure(...)or@Entity(..., dbClient) - ✅ Type-safe entity relationships with
@LinkObjectand@LinkArraydecorators - ✅ Lazy loading of linked entities with
loadLinks() - ✅ Custom transformation between domain models and DynamoDB items
- ✅ Inline and non-inline link storage options
📋 Changelog
1.2.2
- Added optional cascade saving with
insert(true)so linked entities can be persisted before the parent entity.
1.1.2
- Changed query helper methods such as
queryAll,queryBetween, andqueryGreaterThanto accept an options object instead of only alimitargument. - Added support for helper options like
scanIndexForwardwhile preserving the existing sort key helper APIs. - Updated the README examples to document the new query helper signature.
🛠️ Development
Install dependencies:
pnpm installBuild the package:
pnpm buildRun the local example:
pnpm exec tsx examples.tsCheck what will be published:
npm run pack:check📝 License
Apache-2.0
⚠️ Important Notes
- Static lookups assume the hash key can be derived from a default-constructed instance
- Query helpers operate within a single partition key value
- Link loading issues individual
GetCommandcalls per linked record - This is a lightweight utility, not a schema or migration system
📖 Examples
See examples.ts for examples.
🤝 Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
To publish it:
- In GitHub, open Settings > Pages.
- Set Source to GitHub Actions.
- Push to
mainor run the Deploy GitHub Pages workflow manually.
