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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@eresearchqut/ddb-repository

v1.16.3

Published

A TypeScript library providing a generic repository pattern implementation for AWS DynamoDB, simplifying CRUD operations and common database interactions.

Readme

DynamoDB Repository

npm version Coverage Status

A TypeScript library providing a generic repository pattern implementation for AWS DynamoDB, simplifying CRUD operations and common database interactions.

Features

  • 🚀 Generic repository pattern for type-safe DynamoDB operations
  • 🔍 Rich query support with filter expressions and projections
  • 🎯 Batch get with automatic retry for unprocessed keys
  • 📄 Paginated queries with limit and sort order control
  • 🗂️ JSON Pointer Repository for storing structured JSON documents
  • 🧪 Fully tested with Jest and Testcontainers
  • 💪 Written in TypeScript with full type safety
  • ⚡ Built on top of AWS SDK v3

Installation

npm install @eresearchqut/ddb-repository

Prerequisites

  • Node.js LTS
  • AWS credentials configured (for production use)
  • DynamoDB table with the appropriate key schema

Usage

Basic Example

import { DynamoDbRepository, FilterOperator } from '@eresearchqut/ddb-repository';
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';

interface UserKey {
  id: string;
}

interface User {
  id: string;
  name: string;
  email?: string;
  age?: number;
}

const client = new DynamoDBClient({ region: 'us-east-1' });

const userRepository = new DynamoDbRepository<UserKey, User>({
  client,
  tableName: 'users',
  hashKey: 'id',
});

// Write an item
await userRepository.putItem({ id: '123' }, { id: '123', name: 'Alice', email: '[email protected]' });

// Read an item
const user = await userRepository.getItem({ id: '123' });

// Update specific fields (SET name, REMOVE email)
await userRepository.updateItem({ id: '123' }, { name: 'Alicia' }, ['email']);

// Delete an item
await userRepository.deleteItem({ id: '123' });

Querying Items

getItems performs a DynamoDB Query and returns all matching items. It paginates automatically and stops early once limit items are accumulated.

// All items for a partition key
const items = await userRepository.getItems({ id: 'user-123' });

// With filter expressions
const activeAdults = await userRepository.getItems({
  id: 'user-123',
  filterExpressions: [
    { attribute: 'age', operator: FilterOperator.GREATER_THAN_OR_EQUALS, value: 18 },
    { attribute: 'status', operator: FilterOperator.EQUALS, value: 'active' },
  ],
});

// With projection (only return selected attributes)
const names = await userRepository.getItems({
  id: 'user-123',
  projectedAttributes: ['id', 'name'],
});

// With limit and sort order
const recent = await userRepository.getItems({
  id: 'user-123',
  limit: 10,
  sortOrder: 'DESC',
});

// Query a Global Secondary Index
const byStatus = await userRepository.getItems({
  status: 'active',
  index: 'status-index',
});

Filter Operators

| Operator | DynamoDB Expression | Notes | |---|---|---| | EQUALS | #attr = :val | | | NOT_EQUALS | #attr <> :val | | | GREATER_THAN | #attr > :val | | | GREATER_THAN_OR_EQUALS | #attr >= :val | | | LESS_THAN | #attr < :val | | | LESS_THAN_OR_EQUALS | #attr <= :val | | | IN | #attr IN (:val0, :val1, ...) | Pass an Array as value | | BETWEEN | #attr BETWEEN :val0 AND :val1 | Pass a 2-tuple as value | | BEGINS_WITH | begins_with(#attr, :val) | String prefix match | | CONTAINS | contains(#attr, :val) | Substring or set membership |

All operators support negate: true to wrap the expression in NOT (...).

Batch Get

const keys = [{ id: '1' }, { id: '2' }, { id: '3' }];
const items = await userRepository.batchGetItems(keys);

Automatically deduplicates keys, pages requests in chunks of 100, and retries any UnprocessedKeys with exponential back-off.

Composite Key Tables

interface OrderKey {
  customerId: string;
  orderId: string;
}

interface Order {
  customerId: string;
  orderId: string;
  total: number;
}

const orderRepository = new DynamoDbRepository<OrderKey, Order>({
  client,
  tableName: 'orders',
  hashKey: 'customerId',
  rangeKey: 'orderId',
});

await orderRepository.putItem(
  { customerId: 'c1', orderId: 'o1' },
  { customerId: 'c1', orderId: 'o1', total: 99.99 },
);

Consumed Capacity Middleware

import { consumedCapacityMiddleware } from '@eresearchqut/ddb-repository';

client.middlewareStack.add(
  consumedCapacityMiddleware({
    onConsumedCapacity: async (detail) => {
      console.log('Consumed capacity:', detail.ConsumedCapacity);
    },
  }),
);

API Reference

DynamoDbRepository<K, T>

Constructor options

| Option | Type | Required | Description | |---|---|---|---| | client | DynamoDBClient | ✅ | AWS SDK v3 DynamoDB client | | tableName | string | ✅ | DynamoDB table name | | hashKey | string | ✅ | Partition key attribute name | | rangeKey | string | | Sort key attribute name | | returnConsumedCapacity | ReturnConsumedCapacity | | Defaults to TOTAL |

Methods

| Method | Signature | Description | |---|---|---| | getItem | (key: K) => Promise<T \| undefined> | Read a single item by key | | putItem | (key: K, record: T) => Promise<T> | Write an item (create or replace) | | updateItem | (key: K, updates: Partial<T>, remove?: string[]) => Promise<T \| undefined> | Partial update with optional attribute removal | | deleteItem | (key: K) => Promise<Partial<T> \| undefined> | Delete an item | | getItems | (query: Query) => Promise<T[] \| undefined> | Query items (auto-paginates) | | batchGetItems | (keys: K[], projectedQuery?: ProjectedQuery) => Promise<Array<T \| undefined>> | Fetch multiple items by key |

JsonPointerRepository

Stores JSON documents as individual per-pointer DynamoDB items using RFC 6901 JSON Pointer addressing.

See JSON_POINTER_REPOSITORY.md for full documentation.

Quick Example

import { JsonPointerRepository } from '@eresearchqut/ddb-repository';

const docRepo = new JsonPointerRepository({
  client,
  tableName: 'documents',  // requires hash key "id", range key "pointer"
});

await docRepo.putDocument('doc-1', {
  name: 'Alice',
  address: { city: 'Melbourne' },
  tags: ['admin', 'user'],
});

const doc = await docRepo.getDocument('doc-1');
// { name: 'Alice', address: { city: 'Melbourne' }, tags: ['admin', 'user'] }

const city = await docRepo.getAttribute('doc-1', '/address/city');
// 'Melbourne'

await docRepo.putAttribute('doc-1', '/address/postcode', '3000');
await docRepo.deleteAttribute('doc-1', '/tags/1');
await docRepo.deleteDocument('doc-1');

Development

npm install        # install dependencies
npm run lint       # lint
npm run lint:fix   # lint with auto-fix
npm test           # run integration tests (requires Docker)
npm run test:coverage  # tests with coverage report
npm run build      # compile to dist/

Configuration

AWS Credentials

Configure via environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY), AWS credentials file (~/.aws/credentials), or IAM role.

Required IAM Permissions

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "dynamodb:PutItem",
        "dynamodb:GetItem",
        "dynamodb:UpdateItem",
        "dynamodb:DeleteItem",
        "dynamodb:Query",
        "dynamodb:BatchGetItem"
      ],
      "Resource": "arn:aws:dynamodb:*:*:table/your-table-name"
    }
  ]
}

Contributing

Contributions are welcome! Please submit a Pull Request.

Commit message convention

Semantic release uses Conventional Commits:

  • feat: → minor version bump
  • fix: / perf: → patch version bump
  • docs: / chore: → no release
  • feat!: or BREAKING CHANGE: footer → major version bump

License

MIT