Once you've written your model, it's now time to use it!
Important reminder: All models work with native JS object, instead of documents as you'd expect from a more traditional ORM. Hence you'll find the documentation refers to items, not documents.
const dynazord = require('dynazord');
const users = dynazord.createModel({
tableName: 'dynazord-example-users',
keySchema: 'email',
properties: {
email: {
type: String,
required: true,
validate: {
notNull: true,
},
},
name: {
type: String,
required: true,
},
avatar: {
type: String,
},
role: {
type: String,
enum: [ 'ADMIN', 'MODERATOR', 'EDITOR', 'USER' ],
default: 'USER',
},
},
options: {
createdAtTimestamp: true,
updatedAtTimestamp: true,
},
});
const sessions = dynazord.createModel({
tableName: 'dynazord-example-sessions',
// "Primary" index of email + accessToken
keySchema: { hash: 'email', range: 'accessToken' },
secondaryIndexes: {
// "Secondary" index called "sessionsByTime" of email + createdAt
sessionsByTime: { hash: 'email', range: 'createdAt' },
},
properties: {
email: {
type: String,
required: true,
},
accessToken: {
type: String,
required: true,
default: () => uuid(),
},
ipAddress: {
type: String,
required: true,
},
userAgent: {
type: String,
required: true,
},
createdAt: {
type: Date,
// This sets the underlying `createdAt` property to a number format underneath
format: Number,
},
lastActiveAt: {
type: Date,
},
},
options: {
createdAtTimestamp: true,
updatedAtTimestamp: true,
},
});
- Creates a new item, throwing an error if the hash/range combination already exists.
item
must contain the hash/range properties.
const user = await users.create({
email: '[email protected]',
name: 'James D',
avatar: 'https://github.com/jdrydn.png',
});
console.log(user);
// { email: '[email protected]',
// name: 'James D',
// avatar: 'https://github.com/jdrydn.png',
// role: 'USER',
// createdAt: [Date YYYY-MM-DDTHH:mm:ss.Z],
// updatedAt: [Date YYYY-MM-DDTHH:mm:ss.Z] }
const session = await users.create({
email: '[email protected]',
ipAddress: '127.0.0.1',
userAgent: 'Safari (Macintosh; Intel Mac OS X 10_15) AppleWebKit/605.1.15 (KHTML, like Gecko) Safari/605.1.15',
});
console.log(session);
// { email: '[email protected]',
// accessToken: '7897d78d-8616-4d63-a0ba-43fb896e6842',
// ipAddress: '127.0.0.1',
// userAgent: 'Safari (Macintosh; Intel Mac OS X 10_15) AppleWebKit/605.1.15 (KHTML, like Gecko) Safari/605.1.15',
// createdAt: [Date YYYY-MM-DDTHH:mm:ss.Z],
// updatedAt: [Date YYYY-MM-DDTHH:mm:ss.Z] }
opts
is an optional object, passed to hooks & sets the following:
Option | Description |
---|---|
hooks |
Boolean to execute hooks, defaults to true |
- Fetches an item by hash/range combination, throwing an error if the hash/range combination does not exists.
const user = await users.get({ email: '[email protected]' });
console.log(user);
// { email: '[email protected]',
// name: 'James D',
// avatar: 'https://github.com/jdrydn.png',
// role: 'USER',
// createdAt: [Date YYYY-MM-DDTHH:mm:ss.Z],
// updatedAt: [Date YYYY-MM-DDTHH:mm:ss.Z] }
opts
is an optional object, sets the following:
Option | Description |
---|---|
attributesToGet |
An array of properties to build a ProjectedExpression underneath. |
consistentRead |
A boolean to determine consistency. |
- Update an item, throwing an error if the hash/range combination does not exists.
const user = await users.update({ role: 'EDITOR' }, { email: '[email protected]' });
console.log(user);
// { email: '[email protected]',
// name: 'James D',
// avatar: 'https://github.com/jdrydn.png',
// role: 'EDITOR',
// createdAt: [Date YYYY-MM-DDTHH:mm:ss.Z],
// updatedAt: [Date YYYY-MM-DDTHH:mm:ss.Z] }
opts
is an optional object, passed to hooks & sets the following:
Option | Description |
---|---|
hooks |
Boolean to execute hooks, defaults to true |
const user = await users.delete({ email: '[email protected]' });
console.log(user);
// true
opts
is an optional object, passed to hooks & sets the following:
Option | Description |
---|---|
hooks |
Boolean to execute hooks, defaults to true |
- Inserts/updates an item, overwriting properties that you provide.
- Just like create,
item
must contain the hash/range properties.
const user = await users.upsert({
email: '[email protected]',
name: 'James D',
avatar: 'https://github.com/jdrydn.png',
role: 'EDITOR',
});
console.log(user);
// { email: '[email protected]',
// name: 'James D',
// avatar: 'https://github.com/jdrydn.png',
// role: 'EDITOR',
// createdAt: [Date YYYY-MM-DDTHH:mm:ss.Z],
// updatedAt: [Date YYYY-MM-DDTHH:mm:ss.Z] }
opts
is an optional object, passed to hooks & sets the following:
Option | Description |
---|---|
hooks |
Boolean to execute hooks, defaults to true |
- Creates up to 100 items, throwing an error if a primary key already exists.
- Each item must include the primary key properties.
- Applied in a transaction, so if one fails they all fail.
- This method uses
DynamoDB.transactWriteItems
underneath, so you are also bound bytransactWriteItems
limitations too.
const users = await users.bulkCreate([
{ email: '[email protected]', name: 'James 1' },
{ email: '[email protected]', name: 'James 2' },
]);
console.log(users);
// [ { email: '[email protected]',
// name: 'James 1',
// role: 'USER',
// createdAt: [Date YYYY-MM-DDTHH:mm:ss.Z],
// updatedAt: [Date YYYY-MM-DDTHH:mm:ss.Z] },
// { email: '[email protected]',
// name: 'James 2',
// role: 'USER',
// createdAt: [Date YYYY-MM-DDTHH:mm:ss.Z],
// updatedAt: [Date YYYY-MM-DDTHH:mm:ss.Z] } ]
opts
is an optional object, passed to hooks & sets the following:
Option | Description |
---|---|
bulkHooks |
Boolean to execute bulk-hooks, defaults to true |
hooks |
Boolean to execute hooks, defaults to false |
- Fetch up to 100 items at a time, specified by their primary key, returning
null
if they do not exist. - Applied in a transaction, so if one fails they all fail.
- This method uses
DynamoDB.transactGetItems
underneath, so you are also bound bytransactGetItems
limitations too.
const users = await users.bulkGet([
{ email: '[email protected]' },
{ email: '[email protected]' },
]);
console.log(users);
// [ { email: '[email protected]',
// name: 'James 1',
// role: 'USER',
// createdAt: [Date YYYY-MM-DDTHH:mm:ss.Z],
// updatedAt: [Date YYYY-MM-DDTHH:mm:ss.Z] },
// { email: '[email protected]',
// name: 'James 2',
// role: 'USER',
// createdAt: [Date YYYY-MM-DDTHH:mm:ss.Z],
// updatedAt: [Date YYYY-MM-DDTHH:mm:ss.Z] } ]
- Apply the same update to up to 100 items at a time, specified by their primary key.
- Applied in a transaction, so if one fails they all fail.
- This method uses
DynamoDB.transactWriteItems
underneath, so you are also bound bytransactWriteItems
limitations too.
const users = await users.bulkUpdate({
avatar: 'https://http.cat/409',
}, [
{ email: '[email protected]' },
{ email: '[email protected]' },
]);
console.log(users);
// [ { email: '[email protected]',
// name: 'James 1',
// role: 'USER',
// avatar: 'https://http.cat/409',
// createdAt: [Date YYYY-MM-DDTHH:mm:ss.Z],
// updatedAt: [Date YYYY-MM-DDTHH:mm:ss.Z] },
// { email: '[email protected]',
// name: 'James 2',
// role: 'USER',
// avatar: 'https://http.cat/409',
// createdAt: [Date YYYY-MM-DDTHH:mm:ss.Z],
// updatedAt: [Date YYYY-MM-DDTHH:mm:ss.Z] } ]
opts
is an optional object, passed to hooks & sets the following:
Option | Description |
---|---|
bulkHooks |
Boolean to execute bulk-hooks, defaults to true |
hooks |
Boolean to execute hooks, defaults to false |
- Delete up to 100 items at a time, specified by their primary key.
- Applied in a transaction, so if one fails they all fail.
- This method uses
DynamoDB.transactWriteItems
underneath, so you are also bound bytransactWriteItems
limitations too.
const users = await users.bulkDelete([
{ email: '[email protected]' },
{ email: '[email protected]' },
]);
console.log(users);
// true
opts
is an optional object, passed to hooks & sets the following:
Option | Description |
---|---|
bulkHooks |
Boolean to execute bulk-hooks, defaults to true |
hooks |
Boolean to execute hooks, defaults to false |
- Create-or-update up to 100 items at a time, regardless of if their primary key already exists.
- Each item must include the primary key properties.
- Applied in a transaction, so if one fails they all fail.
- This method uses
DynamoDB.transactWriteItems
underneath, so you are also bound bytransactWriteItems
limitations too.
const users = await users.bulkUpsert([
{ email: '[email protected]', name: 'James 1', avatar: 'https://http.cat/307' },
{ email: '[email protected]', name: 'James 2' },
]);
console.log(users);
// [ { email: '[email protected]',
// name: 'James 1',
// role: 'USER',
// avatar: 'https://http.cat/307',
// createdAt: [Date YYYY-MM-DDTHH:mm:ss.Z],
// updatedAt: [Date YYYY-MM-DDTHH:mm:ss.Z] },
// { email: '[email protected]',
// name: 'James 2',
// role: 'USER',
// createdAt: [Date YYYY-MM-DDTHH:mm:ss.Z],
// updatedAt: [Date YYYY-MM-DDTHH:mm:ss.Z] } ]
opts
is an optional object, passed to hooks & sets the following:
Option | Description |
---|---|
bulkHooks |
Boolean to execute bulk-hooks, defaults to true |
hooks |
Boolean to execute hooks, defaults to false |
There are two concepts to understand when looking up values in DynamoDB: query
& scan
. The essential difference is a query
searches for items using indexes) whereas a scan
reads every item in a table to find results.
- Searches across the DynamoDB table using the primary index (by default) or secondary indexes (if
indexName
is specified). - You must provide the
hash
key with a single value, optionally you can include therange
key with a comparison to refine the results. - You can optionally provide
opts.filter
to add filter expressions to a query.
const { gt } = dynazord.operators;
const results = await posts.query({
blogID: 'theverge.com',
publishedAt: { [gt]: new Date('2020-01-01') },
}, {
// Optionally set the index name
indexName: 'blogPublishedAt',
// Optionally filter unwanted entries
filter: { status: 'ACTIVE' },
// Optionally reverse the sort order to get latest posts first
scanIndexForward: false,
// And optionally fetch 10 results maximum
limit: 10,
});
opts
is an optional object, passed to hooks & sets the following:
Option | Description |
---|---|
attributesToGet |
An array of properties to build a ProjectedExpression underneath. |
consistentRead |
A boolean to determine consistency. |
filter |
A filter expression to restrict results. |
exclusiveStartKey |
An optional object to specify a start-key for pagination. |
indexName |
An optional string to specify the index you'd like to query with. |
limit |
An optional number to specify the number of results you want to be returned. |
scanIndexForward |
A boolean to specify the order for index traversal, if true (default) sorts in ascending & false sorts in descending. |
{: .no_toc }
Key conditions are a subset of filter expressions, allowing you to immediately query for a subset of items using the primary index (by default) or secondary indexes (if indexName
is specified):
// Specify just the hash key (partition key)
{ blogID: 'theverge.com' }
// Specify the hash key (partition key) & the range key (sort key)
{ blogID: 'theverge.com', publishedAt: { [gt]: new Date('2020-01-01') } }
Supported operators (exported as dynazord.operators
) for key conditions are:
Symbol | Operator |
---|---|
eq |
Equals |
lt |
Less-Than |
lte |
Less-Than-Or-Equals |
gt |
Greater-Than |
gte |
Greater-Than-Or-Equals |
const { gt } = dynazord.operators;
const results = await posts.scan({
blogID: 'theverge.com',
publishedAt: { [gt]: new Date('2020-01-01') },
status: 'ACTIVE',
}, {
// Optionally set the index name
indexName: 'blogPublishedAt',
// And optionally fetch 10 results maximum
limit: 10,
});
opts
is an optional object, passed to hooks & sets the following:
Option | Description |
---|---|
attributesToGet |
An array of properties to build a ProjectedExpression underneath. |
consistentRead |
A boolean to determine consistency. |
exclusiveStartKey |
An optional object to specify a start-key for pagination. |
indexName |
An optional string to specify the index you'd like to scan with. |
limit |
An optional number to specify the number of results you want to be returned. |
scanIndexForward |
A boolean to specify the order for index traversal, if true (default) sorts in ascending & false sorts in descending. |
Filter expressions are objects that define filter statements.
const { or, gt, lt } = dynazord.operators;
{ blogID: 'theverge.com' }
// blogID = "theverge.com"
{ blogID: 'theverge.com', publishedAt: { [gt]: new Date('2020-01-01') } }
// blogID = "theverge.com" AND publishedAt > 1577836800000
{ blogID: 'theverge.com', publishedAt: { [gt]: new Date('2019-01-01'), [lt]: new Date('2020-01-01') } }
// blogID = "theverge.com" AND publishedAt > 1546300800000 AND publishedAt < 1577836800000
{ [or]: [ { blogID: 'carthrottle.com' }, { blogID: 'wtf1.com' } ], publishedAt: { [lt]: new Date('2020-01-01') } }
// (blogID = "carthrottle.com" OR blogID = "wtf1.com") AND publishedAt < 1577836800000
Supported operators (exported as dynazord.operators
) for filter conditions are:
Symbol | Operator |
---|---|
and |
And |
or |
Or |
not |
Not |
eq |
Equals |
lt |
Less-Than |
lte |
Less-Than-Or-Equals |
gt |
Greater-Than |
gte |
Greater-Than-Or-Equals |
in |
In |
and
, or
& not
support nested expressions. lt
/lte
& gt
/gte
can be used together, but the rest are exclusive.
You can iterate through pages by taking results.lastEvaluatedKey
& passing it as exclusiveStartKey
to future query
/scan
calls:
const page1 = await posts.query({ blogID: 'theverge.com' }, {
// Optionally set the index name
indexName: 'blogPublishedAt',
// Optionally filter unwanted entries
filter: { status: 'ACTIVE' },
// Optionally reverse the sort order to get latest posts first
scanIndexForward: false,
// And optionally fetch 10 results at a time
limit: 10,
});
console.log(page1);
// [ { id: '73da08d1-3c23-4819-b37c-cde4c7fcca65',
// blogID: 'theverge.com'
// publishedAt: [Date YYYY-MM-DDTHH:mm:ss.Z],
// ...: ... },
// { ...: ... },
// { ...: ... },
// count: 10,
// scannedCount: 18,
// lastEvaluatedKey: Buffer<...> ]
const page2 = await posts.query({ blogID: 'theverge.com' }, {
// Optionally set the index name
indexName: 'blogPublishedAt',
// Optionally filter unwanted entries
filter: { status: 'ACTIVE' },
// Optionally reverse the sort order to get latest posts first
scanIndexForward: false,
// And optionally fetch 10 results at a time
limit: 10,
// Passing through lastEvaluatedKey
exclusiveStartKey: page1.lastEvaluatedKey,
});
console.log(page1);
// [ { id: '73da08d1-3c23-4819-b37c-cde4c7fcca65',
// blogID: 'theverge.com'
// publishedAt: [Date YYYY-MM-DDTHH:mm:ss.Z],
// ...: ... },
// { ...: ... },
// { ...: ... },
// count: 8,
// scannedCount: 18,
// lastEvaluatedKey: undefined ]
You can also use lastEvaluatedKey
to work out if there are more items to fetch.
DynamoDB supports transactions - in fact plenty of the bulk methods use transactions underneath - so of course models support transactions too!
const [ user, session ] = await dynazord.transaction([
users.transaction.upsert({,
email: '[email protected]',
name: 'James',
}),
sessions.transaction.create({
email: '[email protected]',
ipAddress: '127.0.0.1',
userAgent: 'Safari (Macintosh; Intel Mac OS X 10_15) AppleWebKit/605.1.15 (KHTML, like Gecko) Safari/605.1.15',
}),
]);
console.log(user);
// { email: '[email protected]',
// name: 'James D',
// role: 'USER',
// createdAt: [Date YYYY-MM-DDTHH:mm:ss.Z],
// updatedAt: [Date YYYY-MM-DDTHH:mm:ss.Z] }
console.log(session);
// { email: '[email protected]',
// accessToken: '7897d78d-8616-4d63-a0ba-43fb896e6842',
// ipAddress: '127.0.0.1',
// userAgent: 'Safari (Macintosh; Intel Mac OS X 10_15) AppleWebKit/605.1.15 (KHTML, like Gecko) Safari/605.1.15',
// createdAt: [Date YYYY-MM-DDTHH:mm:ss.Z],
// updatedAt: [Date YYYY-MM-DDTHH:mm:ss.Z] }
- Executes up to 100 transaction statements with
dynazord.transaction
at a time, across all your models. - This doesn't support models that are created with explicit
dynamodb
clients passed to it. - You cannot mix reads & writes - you can read up to 100 items or write up to 100 items, not both.
Create an item within a transaction.
const [ user ] = await dynazord.transaction([
users.transaction.create({
email: '[email protected]',
name: 'James',
}),
]);
console.log(user);
// { email: '[email protected]',
// name: 'James D',
// role: 'USER',
// createdAt: [Date YYYY-MM-DDTHH:mm:ss.Z],
// updatedAt: [Date YYYY-MM-DDTHH:mm:ss.Z] }
opts
is an optional object, passed to hooks & sets the following:
Option | Description |
---|---|
bulkHooks |
Boolean to execute bulk-hooks, defaults to true |
hooks |
Boolean to execute hooks, defaults to false |
Get an item within a transaction.
const [ user ] = await dynazord.transaction([
users.transaction.get({
email: '[email protected]',
}),
]);
console.log(user);
// { email: '[email protected]',
// name: 'James D',
// role: 'USER',
// createdAt: [Date YYYY-MM-DDTHH:mm:ss.Z],
// updatedAt: [Date YYYY-MM-DDTHH:mm:ss.Z] }
opts
is an optional object, passed to hooks & sets the following:
Option | Description
bulkHooks
| Boolean to execute bulk-hooks, defaults to true
hooks
| Boolean to execute hooks, defaults to false
Update an item within a transaction.
const [ user ] = await dynazord.transaction([
users.transaction.update({
avatar: 'https://http.cat/410',
}, {
email: '[email protected]',
}),
]);
console.log(user);
// { email: '[email protected]',
// name: 'James D',
// avatar: 'https://http.cat/410',
// role: 'USER',
// createdAt: [Date YYYY-MM-DDTHH:mm:ss.Z],
// updatedAt: [Date YYYY-MM-DDTHH:mm:ss.Z] }
opts
is an optional object, passed to hooks & sets the following:
Option | Description |
---|---|
bulkHooks |
Boolean to execute bulk-hooks, defaults to true |
hooks |
Boolean to execute hooks, defaults to false |
Delete an item within a transaction.
const [ user ] = await dynazord.transaction([
users.transaction.update({
avatar: 'https://http.cat/410',
}, {
email: '[email protected]',
}),
]);
console.log(user);
// true
opts
is an optional object, passed to hooks & sets the following:
Option | Description |
---|---|
bulkHooks |
Boolean to execute bulk-hooks, defaults to true |
hooks |
Boolean to execute hooks, defaults to false |
Create-or-update an item within a transaction.
const [ user ] = await dynazord.transaction([
users.transaction.upsert({
email: '[email protected]',
name: 'James',
}),
]);
console.log(user);
// { email: '[email protected]',
// name: 'James D',
// role: 'USER',
// createdAt: [Date YYYY-MM-DDTHH:mm:ss.Z],
// updatedAt: [Date YYYY-MM-DDTHH:mm:ss.Z] }
opts
is an optional object, passed to hooks & sets the following:
Option | Description |
---|---|
bulkHooks |
Boolean to execute bulk-hooks, defaults to true |
hooks |
Boolean to execute hooks, defaults to false |
- Working with Queries in DynamoDB
- Working with Scans in DynamoDB
- When to use (and when not to use) DynamoDB Filter Expressions
- Improving data access with secondary indexes
Finally, check out some examples.