All Projects → peak-ai → Jedlik

peak-ai / Jedlik

Licence: mit
DynamoDB ODM for Node

Programming Languages

javascript
184084 projects - #8 most used programming language
typescript
32286 projects

Projects that are alternatives of or similar to Jedlik

Dynamo Easy
DynamoDB client for NodeJS and browser with a fluent api to build requests. We take care of the type mapping between JS and DynamoDB, customizable trough typescript decorators.
Stars: ✭ 133 (+33%)
Mutual labels:  aws, odm, dynamodb
Serverless
This is intended to be a repo containing all of the official AWS Serverless architecture patterns built with CDK for developers to use. All patterns come in Typescript and Python with the exported CloudFormation also included.
Stars: ✭ 1,048 (+948%)
Mutual labels:  aws, dynamodb
Terraform Aws Dynamodb
Terraform module that implements AWS DynamoDB with support for AutoScaling
Stars: ✭ 49 (-51%)
Mutual labels:  aws, dynamodb
Contacts api
Serverless RESTful API with AWS Lambda, API Gateway and DynamoDB
Stars: ✭ 66 (-34%)
Mutual labels:  aws, dynamodb
Workshop Donkeytracker
Workshop to build a serverless tracking application for your mobile device with an AWS backend
Stars: ✭ 27 (-73%)
Mutual labels:  aws, dynamodb
Graphql Serverless
Sample project to guide the use of GraphQL and Serverless Architecture.
Stars: ✭ 28 (-72%)
Mutual labels:  aws, dynamodb
Content Dynamodb Deepdive
Amazon DynamoDB Deep Dive Course
Stars: ✭ 59 (-41%)
Mutual labels:  aws, dynamodb
Aws Mobile React Sample
A React Starter App that displays how web developers can integrate their front end with AWS on the backend. The App interacts with AWS Cognito, API Gateway, Lambda and DynamoDB on the backend.
Stars: ✭ 650 (+550%)
Mutual labels:  aws, dynamodb
Moviesapp
React Native Movies App: AWS Amplify, AWS AppSync, AWS Cognito, GraphQL, DynamoDB
Stars: ✭ 78 (-22%)
Mutual labels:  aws, dynamodb
Diamondb
[WIP] DiamonDB: Rebuild of time series database on AWS.
Stars: ✭ 98 (-2%)
Mutual labels:  aws, dynamodb
Historical
A serverless, event-driven AWS configuration collection service with configuration versioning.
Stars: ✭ 85 (-15%)
Mutual labels:  aws, dynamodb
Jcabi Dynamodb Maven Plugin
DynamoDB Local Maven Plugin
Stars: ✭ 26 (-74%)
Mutual labels:  aws, dynamodb
Dynamodb Admin
GUI for DynamoDB Local or dynalite
Stars: ✭ 803 (+703%)
Mutual labels:  aws, dynamodb
Tempest
Typesafe DynamoDB for Kotlin and Java.
Stars: ✭ 32 (-68%)
Mutual labels:  aws, dynamodb
Dynamodb Toolbox
A simple set of tools for working with Amazon DynamoDB and the DocumentClient
Stars: ✭ 752 (+652%)
Mutual labels:  aws, dynamodb
Aws Testing Library
Chai (https://chaijs.com) and Jest (https://jestjs.io/) assertions for testing services built with aws
Stars: ✭ 52 (-48%)
Mutual labels:  aws, dynamodb
Awesome Aws
A curated list of awesome Amazon Web Services (AWS) libraries, open source repos, guides, blogs, and other resources. Featuring the Fiery Meter of AWSome.
Stars: ✭ 9,895 (+9795%)
Mutual labels:  aws, dynamodb
Serverless Dynamodb Local
Serverless Dynamodb Local Plugin - Allows to run dynamodb locally for serverless
Stars: ✭ 530 (+430%)
Mutual labels:  aws, dynamodb
Dynamodb Gui Client
DynamoDb GUI Client
Stars: ✭ 540 (+440%)
Mutual labels:  aws, dynamodb
Lambda Refarch Webapp
The Web Application reference architecture is a general-purpose, event-driven, web application back-end that uses AWS Lambda, Amazon API Gateway for its business logic. It also uses Amazon DynamoDB as its database and Amazon Cognito for user management. All static content is hosted using AWS Amplify Console.
Stars: ✭ 1,208 (+1108%)
Mutual labels:  aws, dynamodb

Jedlik

Jedlik is an extensible DynamoDB ODM for Node, written in TypeScript.

Jedlik allows you to utilize the power of JavaScript classes to create models of entities in your domain, while introducing behaviour that allows you to interact with DynamoDB in a simple way.

Install

Yarn: yarn add @peak-ai/jedlik NPM: npm i -S @peak-ai/jedlik

Usage

You can use package like joi to validate the schema.

import * as jedlik from '@peak-ai/jedlik';
import * as Joi from 'joi';

interface UserProps {
  id: number;
  name: string;
  type: 'admin' | 'user';
}

const schema = Joi.object({
  id: Joi.number().required(),
  name: Joi.string().required(),
  type: Joi.string().allow('admin', 'user').required(),
});

// the name of the DynamoDB table the model should write to
// it is assumed this table exists
const Users = new jedlik.Model<UserProps>({ table: 'users', schema });

const user = Users.create({ id: 1, name: 'Fred' }); // create a new document locally

await user.save(); // write the data to the database

user.set({ name: 'Ronak' }); // update an attribute locally

console.log(user.get('name')); // get an attribute

Users.on('save', (u) => {
  console.log(u.toObject()); // get the attributes as a plain object
});

await user.save(); // write the changes to the database

const user2 = await Users.get({ id: 2 }); // query the database

console.log(user2.toObject());

// advanced filtering
const admins = await Users.scan({
  filters: { key: 'type', operator: '=', value: 'admin' },
});

API

class Model<T>

constructor(options: ModelOptions, config?: ServiceConfig): Model<T>

Constructor function that creates a new Model.

options.table (String - required)

Name of the DynamoDB table to interact with

options.schema (Schema<T> - required)
  • schema.validate(item: T): { value: T, error: { name: string, details: { message: string }[] } }

A function that validates the values in a Document and applies any defaults. This is designed to be used with Joi.

config

Optional config that is passed directly to the underlying AWS.DynamoDB.DocumentClient service constructor from the AWS SDK.

Model.create(item: T): Document<T>

Returns a new Document using the attributes of the given item.

Model.query(key: Key<T>, options?: QueryOptions<T>): Promise<Document<T>[]>

Recursively queries the table and resolves with an array of documents which match the given key. Takes an optional options object.

  • options.filters?: Conditions<T> - filters to apply to the query
  • options.index?: IndexName; - the name of the index to query
  • options.limit?: number; - limit the number of documents returned
  • options.sort?: 'asc' | 'desc'; - sort direction of the results (N.B. this uses DynamoDB's ScanIndexForward to sort)

Returned items are type Document<T>.

Model.first(key: Key<T>, options? FirstOptions<T>): Promise<Document<T>>

Convenience method which returns the first document found by the Model.query method. Throws an error if no items are found.

  • options.filters?: Conditions<T> - filters to apply to the query
  • options.index?: IndexName; - the name of the index to query
  • options.sort?: 'asc' | 'desc'; - sort direction of the results (N.B. this uses DynamoDB's ScanIndexForward to sort)

Model.scan(options? ScanOptions<T>): Promise<Document<T>[]>

Performs a scan of the entire table (recursively) and returns all the found documents.

  • options.filters?: Conditions<T> - filters to apply to the scan
  • options.index?: IndexName; - the name of the index to scan
  • options.limit?: number; - limit the number of documents returned

Model.get(key: Key<T>) : Promise<Document<T>>

Resolves with the document that matches the given key parameter. Throws an error if no document exists.

N.B. The key must be the full primary key defined in the table schema. If the table has a composite key, both the partition key and sort key must be provided. You cannot search on a secondary index. If you need to do one of these, use Model.query or Model.first instead.

Model.delete(key: Key<T>, options? DeleteOptions<T>): Promise<void>

Deletes the document that matches the given key parameter.

  • options.conditions?: Conditions<T> - conditions to apply to the deletion - if the condition evaluates to false, then the delete will fail

Model.on(eventName: EventName, callback: (document?: Document<T>) => void): void

Registers an event handler to be fired after successful events. Valid event names are delete and save.

class Document<T>

A Document represents a single item in your DynamoDB table.

Document.get(key: keyof T): T[K]

Returns the value of an attribute on the document.

Document.set(props: Partial<T>): void

Sets the value of an attribute on the document.

Document.save(options? PutOptions<T>): Promise<void>

Saves the Documents attributes to DynamoDB, either overwriting the existing item with the given primary key, or creating a new one.

  • options.conditions?: Conditions<T> - - conditions to apply to the underlying put request - if the condition evaluates to false, then the request will fail

Document.toObject(): T

Returns a plain JavaScript object representation of the documents attributes.

class Client<T>

A low-level DynamoDB client. It has all of the main functionality of the AWS DynamoDB document client. Model and Document classes use this behind the scenes.

This class is similar to the main Model class, but offers support for put and update requests, and doesn't have extra features such as validation and events, and it returns the data directly, rather than converting it to Documents.

constructor(tableName: string, config?: ServiceConfig): Model<T>

Constructor function that creates a new Client.

table (String - required)

Name of the DynamoDB table to interact with

config

Optional config that is passed directly to the underlying AWS.DynamoDB.DocumentClient service constructor from the AWS SDK.

Client.query(key: Key<T>, options?: QueryOptions<T>): Promise<T[]>

Recursively queries the table and resolves with an array of documents which match the given key. Takes an optional options object.

  • options.filters?: Conditions<T> - filters to apply to the query
  • options.index?: IndexName; - the name of the index to query
  • options.limit?: number; - limit the number of documents returned
  • options.sort?: 'asc' | 'desc'; - sort direction of the results (N.B. this uses DynamoDB's ScanIndexForward to sort)

Returned items are type Document<T>.

Client.first(key: Key<T>, options? FirstOptions<T>): Promise<T>

Convenience method which returns the first document found by the Client.query method. Throws an error if no items are found.

  • options.filters?: Conditions<T> - filters to apply to the query
  • options.index?: IndexName; - the name of the index to query
  • options.sort?: 'asc' | 'desc'; - sort direction of the results (N.B. this uses DynamoDB's ScanIndexForward to sort)

Client.scan(options? ScanOptions<T>): Promise<T[]>

Performs a scan of the entire table (recursively) and returns all the found documents.

  • options.filters?: Conditions<T> - filters to apply to the scan
  • options.index?: IndexName; - the name of the index to scan
  • options.limit?: number; - limit the number of documents returned

Client.get(key: Key<T>) : Promise<T>

Resolves with the document that matches the given key parameter. Throws an error if no document exists.

N.B. The key must be the full primary key defined in the table schema. If the table has a composite key, both the partition key and sort key must be provided. You cannot search on a secondary index. If you need to do one of these, use Client.query or Client.first instead.

Client.delete(key: Key<T>, options? DeleteOptions<T>): Promise<void>

Deletes the document that matches the given key parameter.

  • options.conditions?: Conditions<T> - conditions to apply to the deletion - if the condition evaluates to false, then the delete will fail

Client.put(item: T, options: PutOptions<T> = {}): Promise<void>

Saves an item into the database.

  • options.conditions?: Conditions<T> - conditions to apply to the put request

Client.update(key: Key<T>, updates: UpdateMap<T> ): Promise<T>

Performs an update request on an item in the database. Resolves with the newly saved data.

  • options.key: Key<T> - key of the item to update
  • options.updates: UpdateMap<T> - updates to apply

Conditions<T>

Condition maps give you a nicer way of writing filter and condition expressions without worrying about ExpressionAttributeNames and ExpressionAttributeValues.

A simple condition would look like this:

const condition = { key: 'price', operator: '>', value: 10 };

This says "do this thing if the price is greater than 10" - you could use it filter results in a query, or to prevent an item from being deleted or overwritten.

You can also get more complex, using logical groups:

const complexCondition = {
  $or: [
    { key: 'price', operator: '>', value: 10 },
    {
      $and: [
        { key: 'price', operator: '>', value: 5 },
        { key: 'name', operator: '=', value: 'MyItem' },
      ],
    },
  ],
};

This says "do this thing if either a) the price is greater than 10, or b) the price is greater than 5 and the name is MyItem" - the resulting ConditionExpression/FilterExpression would look something like (#price > :price10 OR (#price > :price5 AND #name = :name)).

UpdateMap<T>

UpdateMaps are used when making update requests using the jedlik.Client class and take away some of the complexity of writing and coordinating UpdateExpressions, ExpressionAttributeNames and ExpressionAttributeValues.

You can perform set, remove, add, and delete updates. You can combine different types of update in one expression - they will be applied in the aforementioned order.

Set updates

Set updates have two parts - normal updates (which are always applied) and conditional updates (which will only be applied if the attribute exists already):

{
  set: [{ id: 10, name: 'Michael' }, { age: 20 }];
}

This says "set the id to 10 and the name to Michael, and if the age property exists, set it to 20"

For setting nested attributes, just pass the partial object that you want to update. For example, if you have this object in your database:

{
  name: 'Michael',
  address: {
    street: 'Example Avenue',
    town: 'Exampleville',
  },
};

The following update:

client.update(key, { set: [{ address: { street: 'Different Street' } }] });

would only update the street property on the address.

If you wanted to override the whole address property, you can wrap the new value in the Literal function.

import { Literal } from '@peak-ai/jedlik';

client.update(key, {
  set: [{ address: Literal({ street: 'Different Street' }) }],
});

Remove updates

Remove updates remove an attribute from an item. Just pass the path to the value you want to delete, with a value of true

client.update(key, { delete: [{ address: { street: true } }] });

The above expression would delete the items name and street.address attributes.

Add updates

Add updates add values to a DynamoDB set, or to a numeric value. You can only add to sets or numbers. Note that DynamoDB sets are different to Arrays and native JavaScript Sets.

You can create a DynamoDB Set using the createSet function.

If you have the following object in your database:

{
  age: 10,
  favouriteFoods: createSet(['Pizza', 'Ice Cream']),
}

Then the following expression:

client.update(key, {
  add: [{ age: 1, favouriteFoods: createSet(['Chocolate']) }],
});

The resulting item would look like:

{
  age: 12,
  favouriteFoods: createSet(['Pizza', 'Ice Cream', 'Chocolate']), // Chocolate is added to the set
}

Delete updates

Delete updates remove items from a DynamoDB set. Note that DynamoDB sets are different to Arrays and native JavaScript Sets.

You can create a DynamoDB Set using the createSet function.

If you have the following object in your database:

{
  age: 10,
  favouriteFoods: createSet(['Pizza', 'Ice Cream']),
}

Then the following expression:

client.update(key, {
  delete: [{ favouriteFoods: createSet(['Pizza']) }],
});

The resulting item would look like:

{
  age: 12,
  favouriteFoods: createSet(['Ice Cream']), // Pizza is no longer in the set
}

Roadmap

Some features that I'd still like to add

  • Support for more complicated filter types - the full list is here
  • Ability to add methods to Documents and Models
  • Ability to add "virtual properties" to documents - custom getters and setters
  • Timestamps
  • Key validation. For example - You should only be able to pass key properties into Model.get. And you shouldn't really be able to update the key properties on a document - if you change the value of a key property of an existing document, a new document will be created. We should make this better, even if it's just by adding better type checking. Currently when you need to give a key, it just uses Partial<T> as the type.

Development

Getting Started

Build

  • yarn build compiles the TypeScript code into a dist directory.

Test

  • yarn test will run unit and integration tests using Jest. Integration tests run against a Dockerized DynamoDB Local. You'll need the Docker daemon running for this.

Contributing

  • Branch off develop please. PR's will get merged to develop. Releases will go from develop to master

Publish

  • Run the tests and linter on the develop branch
  • Switch to master and merge in develop
  • Use yarn version to increase the package version and release the new version. This will do the following things:
    • Run the tests and linter
    • Increase the version
    • Add a new git tag for the version
    • push the new tag to GitHub
    • publish the package to npm
    • push the released code to GitHub master
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].