Typescript ORM of DynamoDB, written from scratch to fully support DynamoDB. Powering Vingle
process.env.ENABLE_XRAY = "true"
Also, dynamo-types let you overcome several limits that DynamoDB or the aws-sdk has.
And most importantly, all of those queries regardless of whether it's from index or primary key, strongly typed. I mean what's the point of using typescript if not anyway?
@Decorator.Table({ name: "prod-Card" })
class Card extends Table {
@Decorator.Attribute()
public id: number;
@Decorator.Attribute()
public title: string;
@Decorator.Attribute({ timeToLive: true })
public expiresAt: number;
@Decorator.FullPrimaryKey('id', 'title')
static readonly primaryKey: Query.FullPrimaryKey<Card, number, string>;
@Decorator.Writer()
static readonly writer: Query.Writer<Card>;
}
// Create Table At DynamoDB
await Card.createTable();
// Drop Table At DynamoDB
await Card.dropTable();
// Creating Record
const card = new Card();
card.id = 100;
card.title = "Title";
//
await Card.writer.put(card);
// OR just
await card.save();
// Batch Put
await Card.writer.batchPut([
new Card(),
new Card()
]);
// Get Record
await Card.primaryKey.get(100, "Title");
// BatchGet
// This array is strongly typed such as Array<[number, string]> so don't worry.
await Card.primaryKey.batchGet([
[100, "Title"],
[200, "Title2"]
])
// Query
// Range key opreations are stringly typed. ([">=", T] | ["=", T] ...)
await Card.primaryKey.query({
hash: 100,
range: [">=", "Title"]
})
// Delete record
await card.delete()
// Delete record only when it meets condition.
// with this, you can avoid race condition such as somebody updating the record while you're trying to delete it
await card.delete({
condition: { title: Equal("Title") }
});
// when mismatch occurs, it raises "ConditionalCheckFailedException" error.
// Likewise, update record only when it meets condition
card.title = "New Title"
await card.save({ condition: { title: "Title" } });
// when mismatch occurs, it raises "ConditionalCheckFailedException" error.
import {
Config,
Decorator,
Query,
Table,
} from "dynamo-types";
@Decorator.Table({ name: `table_name` })
export class CardStat extends Table {
@Decorator.HashPrimaryKey("card_id")
public static readonly primaryKey: Query.HashPrimaryKey<CardStat, number>;
@Decorator.Writer()
public static readonly writer: Query.Writer<CardStat>;
@Decorator.Attribute({ name: "card_id" })
public cardId: number;
@Decorator.Attribute({ name: "impressions_count" })
public impressionsCount: number = 0;
@Decorator.Attribute({ name: "shares" })
public shares: number = 0;
}
DynamoTypes utilize reflect-metadata to read metadata (usually type of variables) from Typescript code. to do so, you must enable those options.
{
"compilerOptions": {
// other options..
//
"experimentalDecorators": true, // required
"emitDecoratorMetadata": true // required
}
}
DynamoDB supports 2 different kinds of connections. Plain connections to DynamoDB through HTTP, or through DAX. dynamo-types supports this by letting you create a separate connection for each table.
@Decorator.Table({ name: "prod-Card1", connection: new DAXConnection({ endpoints: ["dax-domain:8892"] }) })
class Card extends Table {
@AttributeDecorator()
public id: number;
@AttributeDecorator()
public title: string;
@AttributeDecorator({ name: "complicated_field"})
public complicatedField: string;
@FullPrimaryKeyDecorator('id', 'title')
static readonly primaryKey: Query.FullPrimaryKey<Card, number, string>;
@WriterDecorator()
static readonly writer: Query.Writer<Card>;
}
Then any query that is sent to the Card table will be sent through DAXConnection.
If you don't specify any connection, it automatically uses default connection, which is DynamoDBConnection.