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

@jayalfredprufrock/dynamo-box

v0.10.0

Published

Dynamo-box leverages [TypeBox](https://github.com/sinclairzx81/typebox) to provide a strongly typed repository client for interacting with DynamoDb tables. **Remain safe in the box.**

Downloads

361

Readme

dynamo-box

Dynamo-box leverages TypeBox to provide a strongly typed repository client for interacting with DynamoDb tables. Remain safe in the box.

This library also currently leverages Dynamon for marshalling and building expressions, so be sure to visit their docs for additional usage information, especially in regards to conditional/update expressions.

npm i @jayalfredprufrock/dynamo-box @sinclair/typebox @typemon/dynamon

Basic Usage

Consider the following schema that represents a user:

import { Static, Type } from '@sinclair/typebox';

export const UserSchema = Type.Object({
    id: Type.String(),
    name: Type.String(),
    email: Type.String(),
    createdAt: Type.Integer(),
    updatedAt: Type.Integer(),
});

Use makeDdbRepository() to create a fully-typed repository class:

import { makeDdbRepository } from '@jayalfredprufrock/dynamo-box';

export class UserRepository = makeDdbRepository(UserSchema)({
    client: { region: 'us-east-1', endpoint: 'localhost' },
    tableName: 'users',
    partitionKey: 'id',
    transformInput: (input: UserInput) => {
        return {
            id: randomString(16),
            createdAt: Date.now(),
            ...input,
            updatedAt: Date.now(),
        };
    },
    gsis: {
        keysByEmail: {
            partitionKey: 'email',
            projection: 'KEYS',
        },
    },
});

const userRepo = new UserRepository();

// does a PUT, but throws if id already exists
const newUser = await userRepo.create({
    name: 'Dynamo Box',
    email: '[email protected]'
});

// last param of all methods allow advanced configuration
const user = await userRepo.getOrThrow({ id: newUser.id }, { consistentRead: true });

The class can also be extended:

export class UserRepositoryExtended extends UserRepository {
    constructor() {
        super({
            tableName: usersTableName,
            client: new DynamoDBClient({ region: 'us-east-1', endpoint: 'localhost' }),
        });
    }

    async getByEmail(userEmail: string): Promise<User> {
        const email = userEmail.toLowerCase();

        const [userKeys] = await this.queryGsi('keysByEmail', { email });
        if (!userKeys) {
            throw new Error('User not found');
        }
        return this.getOrThrow({ id: userKeys.id });
    }
}

Docs

Library is nearing 1.0 release, at which point full documentation will become available. It is currently being used in production environments so I welcome brave community members to give it a spin in its current state. I don't suspect any major API changes at this point and I do my absolute best to respect SemVer.


Wishlist

  • update() not transforming input, thus not getting updatedAt
  • create() not transforming output
  • expression based update() not transforming input. Are timestamps the only use case for that?
  • should a distinction be made between "defaults" and "generated/computed" values?
  • leverage typebox decoder/encoder for transform support
  • update() should take array of expressions and not require wrapping in dynamon update()
  • atomic field (optimistic lock) support that automatically generates condition expression based on something like updatedAt or version
  • transformer support projected GSIs
  • transact write/get items support
  • modify return type based on filter projection
  • strongly typed equality filter support
  • transformInput/Output async support
  • explore integrating mongodb-style conditional expressions for safer typings and less reliance on dynamon.
  • alternatively, provide strongly-typed "condition builder"
  • ability to create tables (or definitions) based on config
  • createOrUpdate() and make put() require complete type object?
  • update() support nested path syntax for simple updates
  • utility to create projectionFilter from schema
  • use special schema field to indicate basic mapping type and generate mapping automatically
  • handle transformInput a little better so its obvious that the input is providing defaults. will probably involve specifying a transformInput schema (which can also provide runtime type safety) or switching to specifying default/readonly fields for create/update
  • make special case for TTL field, support function that automatically sets it based on data
  • remove undefined values from update expression object values
  • bulk operations that perform non-batch operations in parallell (for conditions and consistent logging interface)
  • allow typebox schemas for projection and filter conditions
  • count() method that automatically projects partition key only and returns number
  • isEmpty() method that counts (with limit of 1) and returns true/false
  • make it easier to override methods, potentially by not producing an anonymous class
  • paged versions of scanGsi and queryGsi
  • transformOutput should not be run when a projectionExpression is used
  • automatically remove keys from update expressions? potentially anything "readonly" as well
  • getGsi() and getGsiOrThrow() that fetches one record
  • get() (optionally gsi variants) that allow a simple condition expression and pretends like document doesn't exist if condition fails (useful for permission stuff)
  • should event item object be transformed?