32
loading...
This website collects cookies to deliver better user experience
npm i dynamodb-onetable
require
to import the libraries.import {Table} from 'dynamodb-onetable'
DynamoDB
class and create a DocumentClient
instance.import DynamoDB from 'aws-sdk/clients/dynamodb'
const client = new DynamoDB.DocumentClient(params)
DynamoDBClient
class and the OneTable Dynamo
helper. Then create a DynamoDBClient
instance and Dynamo wrapper instance.import {DynamoDBClient} from '@aws-sdk/client-dynamodb'
import Dynamo from 'dynamodb-onetable/Dynamo'
const client = new Dynamo({client: new DynamoDBClient(params)})
Table
instance and define your models via a schema. The schema defines your single-table entities, attributes and indexes.import MySchema from './Schema'
const table = new Table({
client: client,
name: 'MyTable',
schema: MySchema,
})
models
(entities), keys, indexes and attributes. Schemas look like this:export default {
indexes: {
primary: { hash: 'pk', sort: 'sk' },
gs1: { hash: 'gs1pk', sort: 'gs1sk', project: ['gs1pk', 'gs1sk', 'data'] },
},
models: {
Account: {
pk: { type: String, value: 'account#${id}' },
sk: { type: String, value: 'account#' },
id: { type: String, uuid: true, validate: Match.ulid },
name: { type: String, required: true, unique: true, validate: Match.name },
balance: { type: Number, default: 0 },
// Search by account name or by type
gs1pk: { type: String, value: 'account#' },
gs1sk: { type: String, value: 'account#${name}${id}' },
},
User: {
pk: { type: String, value: 'account#${accountId}' },
sk: { type: String, value: 'user#${email}' },
accountId: { type: String, required: true },
id: { type: String, uuid: true, validate: Match.ulid },
name: { type: String, required: true, validate: Match.name },
email: { type: String, required: true, validate: Match.email, crypt: true },
address: { type: Object, default: {}, schema: {
street: { type: String, /* map: 'data.street' */ },
city: { type: String, /* map: 'data.city' */ },
zip: { type: String, /* map: 'data.zip' */ },
} },
status: { type: String, required: true, default: 'active', enum: ['active', 'inactive'] },
balance: { type: Number, default: 0 },
// Search by user name or by type
gs1pk: { type: String, value: 'user#' },
gs1sk: { type: String, value: 'user#${name}#${id}' },
},
Product: {
pk: { type: String, value: 'product#${id}' },
sk: { type: String, value: 'product#' },
id: { type: String, uuid: true, validate: Match.ulid },
name: { type: String, required: true },
price: { type: Number, required: true },
// Search by product name or by type
gs1pk: { type: String, value: 'product#' },
gs1sk: { type: String, value: 'product#${name}#${id}' },
},
Invoice: {
pk: { type: String, value: 'account#${accountId}' },
sk: { type: String, value: 'invoice#${id}' },
accountId: { type: String, required: true },
date: { type: Date, default: () => new Date() },
id: { type: String, uuid: true },
product: { type: String },
count: { type: Number },
total: { type: Number },
// Search by invoice date or by type
gs1pk: { type: String, value: 'invoice#' },
gs1sk: { type: String, value: 'invoice#${date}#${id}' },
}
}
const Account = table.getModel('Account')
type AccountType = Entity<typeof Schema.models.Account>
const Account = table.getModel<AccountType>('Account')
await table.createTable()
let account = await Account.create({name: 'Acme Airplanes'})
{
pk: 'account:8e7bbe6a-4afc-4117-9218-67081afc935b',
sk: 'account:98034',
name: 'Acme Airplanes',
id: '8e7bbe6a-4afc-4117-9218-67081afc935b',
balance: 0,
created: '2021-07-05T04:58:55.136Z',
updated: '2021-07-05T04:58:55.136Z',
}
{
name: 'Acme Airplanes',
id: '8e7bbe6a-4afc-4117-9218-67081afc935b',
balance: 0,
created: '2021-07-05T04:58:55.136Z',
updated: '2021-07-05T04:58:55.136Z',
}
{hidden: true}
in the params:let account = await Account.create({name: 'Acme Airplanes'}, {hidden: true})
table.setContext({accountId: account.id})
let user = await User.create({name: 'Road Runner', email: '[email protected]'})
{
pk: { type: String, value: 'account#${id}' },
sk: { type: String, value: 'account#' },
}
id
and then OneTable will construct the keys for you.account = await Account.get({id: '8e7bbe6a-4afc-4117-9218-67081afc935b'})
{
gs1pk: { type: String, value: 'user#' },
gs1sk: { type: String, value: 'user#${name}#${id}' },
}
user = await User.get({name: 'Road Runner'}, {index: 'gs1', follow: true, log: true})
user#Road Runner
. Because the user ID portion is not provided, OneTable searches for a user that begins with the provided string.gs1pk
, gs1sk
and data
attributes to the GSI. That means the other user attributes are not returned when querying only from the GSI. OneTable addresses this by providing the follow: true
parameter which follows the GSI read with a read from the primary index to fetch the full user record.{log: true}
is useful to see the actual DynamoDB request logged to the console.let users = await User.find()
let adminUsers = await User.find({accountId: account.id, role: 'admin'})
where
clause. This will find users with an account balance over $100. Where clauses are useful for more complex filtering expressions. A where clause supplies property references by enclosing the name in ${}
. Similarly, values are enclosed in {}
without the dollar.users = await User.find({}}, {
where: '${balance} > {100}'
})
user = await User.update({email: '[email protected]', balance: 0, role: 'admin'})
params.set
. This will update just the property you specify.user = await User.update({email: '[email protected]'}, {set: {'address.zip': '{"98034"}'}})
user = await User.update({email: '[email protected]'}, {add: {balance: 10}})
user = await User.update({email: '[email protected]'}, {set: {balance: '${balance} - {20}'}})
table.fetch
.let collection = await table.fetch(['Account', 'User', 'Invoice'], {pk: `account#${account.id}`})
let batch = {}
let i = 0, count = 0
while (i++ < 200) {
User.create({name: `user${i}`, email: `user${i}@acme.com`}, {batch})
if (++count >= 25) {
await table.batchWrite(batch)
batch = {}
count = 0
}
}
let transaction = {}
await Account.update({id: account.id, status: 'active'}, {transaction})
await User.update({id: user.id, role: 'user'}, {transaction})
await table.transact('write', transaction)
start
param on request APIs and in result sets to mark the current reading position.let start
do {
users = await User.find({}, {start, limit: 25})
// Operate on this batch of 25 users ...
start = users.start
} while (users.start)
32