npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

use-dynamodb

v1.0.32

Published

A TypeScript library that provides a simplified interface for interacting with Amazon DynamoDB, using the AWS SDK v3.

Downloads

1,752

Readme

Use DynamoDB

A TypeScript library that provides a simplified interface for interacting with Amazon DynamoDB, using the AWS SDK v3.

TypeScript Vitest MIT License

🚀 Features

  • ✅ Type-safe CRUD operations (Create, Read, Update, Delete)
  • 🔍 Support for local and global secondary indexes (LSI & GSI)
  • 📦 Batch operations with automatic chunking
  • 🔎 Query and Scan operations with filtering
  • 🔄 Optimistic locking with versioning
  • 📄 Automatic pagination
  • 🕒 Built-in timestamp management (**createdAt, **updatedAt, __ts)
  • 🔒 Conditional updates and transactions
  • 🎯 Change tracking with callbacks
  • 🔄 Custom retry strategy with exponential backoff

📚 Documentation

📦 Installation

yarn add use-dynamodb

🛠️ Usage

Initialization

import Dynamodb from 'use-dynamodb';

type Item = {
	pk: string;
	sk: string;
	gsiPk: string;
	gsiSk: string;
	lsiSk: string;
	foo: string;
};

const db = new Dynamodb<Item>({
	accessKeyId: 'YOUR_ACCESS_KEY',
	secretAccessKey: 'YOUR_SECRET_KEY',
	region: 'us-east-1',
	table: 'YOUR_TABLE_NAME',
	schema: { partition: 'pk', sort: 'sk' },
	indexes: [
		{
			name: 'ls-index',
			partition: 'pk',
			sort: 'lsiSk',
			sortType: 'S'
		},
		{
			name: 'gs-index',
			partition: 'gsiPk',
			partitionType: 'S',
			sort: 'gsiSk',
			sortType: 'S'
		}
	],
	onChange: async events => {
		// Optional callback for tracking changes
		console.log('Changes:', events);
	}
});

Table Operations

Create Table

await db.createTable();

Basic Operations

Put Item

// Simple put with automatic condition to prevent overwrites
const item = await db.put({
	pk: 'user#123',
	sk: 'profile',
	foo: 'bar'
});

// Put with overwrite allowed
const overwrittenItem = await db.put(
	{
		pk: 'user#123',
		sk: 'profile',
		foo: 'baz'
	},
	{
		overwrite: true
	}
);

// Put with conditions
const conditionalItem = await db.put(
	{
		pk: 'user#123',
		sk: 'profile',
		foo: 'bar'
	},
	{
		attributeNames: { '#foo': 'foo' },
		attributeValues: { ':foo': 'bar' },
		conditionExpression: '#foo <> :foo'
	}
);

Get Item

// Get by partition and sort key
const item = await db.get({
	item: { pk: 'user#123', sk: 'profile' }
});

// Get with specific attributes
const partialItem = await db.get({
	item: { pk: 'user#123', sk: 'profile' },
	select: ['foo']
});

// Get using query expression
const queriedItem = await db.get({
	attributeNames: { '#pk': 'pk' },
	attributeValues: { ':pk': 'user#123' },
	queryExpression: '#pk = :pk'
});

// Get last by partition and sort key
const item = await db.getLast({
	item: { pk: 'user#123', sk: 'profile' }
});

Update Item

// Update using function
const updatedItem = await db.update({
	filter: {
		item: { pk: 'user#123', sk: 'profile' }
	},
	updateFunction: item => ({
		...item,
		foo: 'updated'
	})
});

// Update using expression
const expressionUpdatedItem = await db.update({
	filter: {
		item: { pk: 'user#123', sk: 'profile' }
	},
	attributeNames: { '#foo': 'foo' },
	attributeValues: { ':foo': 'updated' },
	updateExpression: 'SET #foo = :foo'
});

// Upsert
const upsertedItem = await db.update({
	filter: {
		item: { pk: 'user#123', sk: 'profile' }
	},
	updateFunction: item => ({
		...item,
		foo: 'new'
	}),
	upsert: true
});

// Update with partition/sort key change
const movedItem = await db.update({
	allowUpdatePartitionAndSort: true,
	filter: {
		item: { pk: 'user#123', sk: 'profile' }
	},
	updateFunction: item => ({
		...item,
		pk: 'user#124'
	})
});

Delete Item

// Delete by key
const deletedItem = await db.delete({
	filter: {
		item: { pk: 'user#123', sk: 'profile' }
	}
});

// Delete with condition
const conditionalDelete = await db.delete({
	attributeNames: { '#foo': 'foo' },
	attributeValues: { ':foo': 'bar' },
	conditionExpression: '#foo = :foo',
	filter: {
		item: { pk: 'user#123', sk: 'profile' }
	}
});

Query Operations

// Query by partition key
const { items, count, lastEvaluatedKey } = await db.query({
	item: { pk: 'user#123' }
});

// Query with prefix matching
const prefixResults = await db.query({
	item: { pk: 'user#123', sk: 'profile#' },
	prefix: true
});

// Query with filter
const filteredResults = await db.query({
	attributeNames: { '#foo': 'foo' },
	attributeValues: { ':foo': 'bar' },
	filterExpression: '#foo = :foo',
	item: { pk: 'user#123' }
});

// Query with pagination
const paginatedResults = await db.query({
	item: { pk: 'user#123' },
	limit: 10,
	startKey: lastEvaluatedKey
});

// Query using index
const indexResults = await db.query({
	item: { gsiPk: 'status#active' },
	index: 'gs-index'
});

// Query with chunks processing
const chunkedResults = await db.query({
	item: { pk: 'user#123' },
	chunkLimit: 10,
	onChunk: async ({ items, count }) => {
		// Process items in chunks
		console.log(`Processing ${count} items`);
	}
});

Scan Operations

// Basic scan
const { items, count, lastEvaluatedKey } = await db.scan();

// Filtered scan
const filteredScan = await db.scan({
	attributeNames: { '#foo': 'foo' },
	attributeValues: { ':foo': 'bar' },
	filterExpression: '#foo = :foo'
});

// Scan with selection
const partialScan = await db.scan({
	select: ['foo', 'bar']
});

// Paginated scan
const paginatedScan = await db.scan({
	limit: 10,
	startKey: lastEvaluatedKey
});

Batch Operations

// Batch write
const items = await db.batchWrite([
	{ pk: 'user#1', sk: 'profile', foo: 'bar' },
	{ pk: 'user#2', sk: 'profile', foo: 'baz' }
]);

// Batch get
const retrievedItems = await db.batchGet([
	{ pk: 'user#1', sk: 'profile' },
	{ pk: 'user#2', sk: 'profile' }
]);

// Batch delete
const deletedItems = await db.batchDelete([
	{ pk: 'user#1', sk: 'profile' },
	{ pk: 'user#2', sk: 'profile' }
]);

// Clear table
await db.clear(); // Clear entire table
await db.clear('user#123'); // Clear by partition key

Filter Operations

// Filter is a higher-level abstraction that combines query and scan
const results = await db.filter({
	item: { pk: 'user#123' }, // Uses query
	// OR
	queryExpression: '#pk = :pk', // Uses query
	// OR
	filterExpression: '#status = :status' // Uses scan
});

Types

Key Types

type TableSchema = {
	partition: string;
	sort?: string;
};

type TableGSI = {
	name: string;
	partition: string;
	partitionType: 'S' | 'N';
	sort?: string;
	sortType?: 'S' | 'N';
};

type TableLSI = {
	name: string;
	partition: string;
	sort?: string;
	sortType: 'S' | 'N';
};

Item Types

type Dict = Record<string, any>;

type PersistedItem<T extends Dict = Dict> = T & {
	__createdAt: string;
	__ts: number;
	__updatedAt: string;
};

Change Tracking

type ChangeType = 'PUT' | 'UPDATE' | 'DELETE';

type ChangeEvent<T extends Dict = Dict> = {
	item: PersistedItem<T>;
	partition: string;
	sort?: string | null;
	table: string;
	type: ChangeType;
};

type OnChange<T extends Dict = Dict> = (events: ChangeEvent<T>[]) => Promise<void>;

🧪 Testing

# Set environment variables
export AWS_ACCESS_KEY='YOUR_ACCESS_KEY'
export AWS_SECRET_KEY='YOUR_SECRET_KEY'

# Run tests
yarn test

📝 Notes

  • The library automatically handles optimistic locking using the __ts field
  • All write operations (put, update, delete) trigger change events if an onChange handler is provided
  • Batch operations automatically handle chunking according to DynamoDB limits
  • The library provides built-in retry strategy with exponential backoff
  • All timestamps are managed automatically (**createdAt, **updatedAt, __ts)
  • Queries automatically handle pagination for large result sets

📝 License

MIT © Felipe Rohde

⭐ Show your support

Give a ⭐️ if this project helped you!

👨‍💻 Author

Felipe Rohde

🙏 Acknowledgements