Home

Awesome

<h1 align="center" style="text-align:center">🔒 DDB-Table</h1> <h4 align="center">Strongly typed library for querying and modeling DynamoDB documents in TypeScript.</h4> <p align="center"> <a href="https://www.npmjs.org/package/ddb-table"> <img src="http://img.shields.io/npm/v/ddb-table.svg" alt="View On NPM"> </a> <a href="https://github.com/neuledge/ddb-table/actions/workflows/build.yml"> <img src="https://github.com/neuledge/ddb-table/actions/workflows/build.yml/badge.svg" alt="Build Status"> </a> <a href="https://depfu.com/github/neuledge/ddb-table?project_id=13055"> <img src="https://badges.depfu.com/badges/c06bc1e007e8b7f804d8563a56bb2ced/overview.svg" alt="Dependency Status"> </a> <a href="https://codecov.io/gh/neuledge/ddb-table"> <img src="https://codecov.io/gh/neuledge/ddb-table/branch/master/graph/badge.svg?token=JQG7E9QJ3B" alt="Coverage Status" /> </a> <a href="LICENSE"> <img src="https://img.shields.io/npm/l/ddb-table.svg" alt="License"> </a> </p> <br>

DDB-Table was built to provide strongly-typed data structures over DynamoDB tables. Using AWS DocumentClient & TypeScript you can easily fetch and store any JSON document and validate it’s structure statically. Query secondary indexes and run complicated update expressions without any error on runtime.

await table
  .update('demo@example.com')
  .set('FullName', 'John Doe')
  // 🚨 TypeScript Error: 'fullName' is not assignable to 'Email' | 'FullName'
  .condition((cond) => cond.eq('fullName', 'Johnny Doe'))
  .exec();

Main Features

Sponsored by ❤️

If you like this project, please consider sponsoring us to help us continue to maintain and improve this project.

<br>

Install

npm i ddb-table
<br>

Usage

import Table from 'ddb-table';
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
import { DynamoDBDocument } from '@aws-sdk/lib-dynamodb';


interface MessageSchema {
  threadId: string;
  timestamp: number;
  senderId: string;
  message: string;
  status: 'sent' | 'received';
  tags?: Set<string>;
  attachments: {
    name: string;
    URL: string;
  }[];
}

const client = new DynamoDBClient({
  // settings...
});

// create the basic table definition
const messages = new Table<MessageSchema, 'threadId', 'timestamp'>({
  tableName: 'Messages',
  primaryKey: 'threadId',
  sortKey: 'timestamp',
  documentClient: DynamoDBDocument.from(client);
});

const updateRes = await messages
  .update('john@gmail.com', 1588191225322)
  .set('message', 'Hello World!')
  .add('tags', new Set(['unread', 'important']))
  .set('attachments', (exp) =>
    exp.listAppend([{ name: 'Test', URL: 'demo.com' }]),
  )
  .return('ALL_NEW')
  .exec();

console.log(updateRes.Attributes);

Working with indexes as well:

// create a secondary index definition
type SenderTimestampIndex = Pick<
  MessageSchema,
  'threadId' | 'timestamp' | 'senderId'
>;

const outboxIndex = messages.index<
  SenderTimestampIndex,
  'senderId',
  'timestamp'
>('senderId-timestamp-index', 'senderId', 'timestamp');

const it = outboxIndex
  .query()
  .keyCondition((cond) => cond.eq('senderId', 'john@gmail.com'))
  .keyCondition((cond) =>
    cond.between('timestamp', Date.now() - 3600e3, Date.now()),
  )
  .project({ threadId: 1, message: 1 })
  .reverseIndex()
  .entries();

for await (const item of it) {
  console.log(item);
}

Error Handling

import { DynamoDBExceptionName } from 'ddb-table';

try {
  await table.put(...).exec();
} catch (err) {
  if ((err as DynamoDBServiceException)?.name === DynamoDBExceptionName.ConditionalCheckFailedException) {
    // handle exception
  }
}
<br>

License

MIT license © 2022 Neuledge