@bhoos/dynamodb
DynamoDB wrapper
Installation
$ yarn add @bhoos/dynamodb
Usage
Environment Variables:
AWS_REGION
DYNAMODB_ENDPOINT
[optional]
Example (Single partition key)
import { createCollection } from '@bhoos/dynamodb'
const User = createCollection('User', ({ key }) => [
key('username'),
], ({ self }) => {
get: async (username) => {
const u = await self.findOne(username);
const { password, ...res } = u;
return u;
},
create: async (username, password, name) => self.insert({
username,
name,
password: hash(password),
}),
validate: async (username, password) => {
const u = await self.findOne(user);
if (!u || hash(password) !== u.password) {
throw new Error('Invalid username/password');
}
const { password, ...res } = u;
return res;
},
changePassword: async (username, password) => {
await self.update({ password: hash(password) }, username);
},
deleteUser: async (username) => {
await self.delete(username);
},
});
const email = await User.create('john@doe.com', 'SuperSecret', 'John Doe');
const user = await User.get('john@doe.com');
await User.validate('john@doe.com', 'Wrong');
await User.validate('john@doe.com', 'SuperSecret');
await User.deleteUser('john@doe.com');
await User.insert({ username: 'jane@doe.com', password: 'not-hashed', name: 'Jane Doe' });
await User.findOne('jane@doe.com');
await User.update({ password: 'not-hashed-new' }, 'jane@doe.com');
await User.delete('jane@doe.com');
Example (Multiple keys)
const Movie = createCollection('Movie', ({ key }) => ([
key('year'),
key('title'),
}), ({ self, doc }) => ({
get: (year, title) => self.findOne(year, title),
findAnnual: (year) => doc.query(.......),
});
Example (Local Secondary Index)
const Thread = createCollection('Thread', ({ key, field }) => ([
key('forum'),
key('subject').sort(),
field('timestamp').local('LastPostIndex'),
]), ({ doc }) => ({
getRecentPosts: (forum) => {
const params = {
TableName: Thread.name,
IndexName: 'LastPostIndex',
KeyConditionExpression: '#forum=:forum',
ExpressionAttributeNames: { '#forum': 'forum' },
ExpressionAttributeValues: { ':forum': forum },
ScanIndexForward: false,
};
return doc.query(params).promise().then(data => data.Items);
},
}));
Example (Global Secondary Index)
const GameScore = createCollection('GameScore', ({ key, field }) => ([
key('UserId'),
key('GameTitle').sort().global('GameTitleIndex'),
field('TopScore').global('GameTitleIndex', true)
]), ({ doc }) => ({
getTopScorer: (title) => {
const params = {
TableName: GameScore.name,
IndexName: 'GameTitleIndex',
KeyConditionExpression: 'GameTitle = :title',
ExpressionAttributeValues: { ':title': title },
ScanIndexForward: false,
};
return doc.query(params).promise().then(data => data.Items);
},
}));
Example (With TTL)
const Logs = createCollection('Log', ({ key, field }) => ([
key('id').auto(),
field('name'),
field('expiry').ttl(),
]));
await Logs.updateTTL(true);
await Logs.insert({ name: 'Tomorrow', expiry: parseInt(Date.now() / 1000) + 86400 });
await Logs.insert({ name: 'next week', expiry: parseInt(Date.now() / 1000) + 7 * 86400 });