dynamodb-loader-model

This module integrates the following modules:

Usage no npm install needed!

<script type="module">
  import dynamodbLoaderModel from 'https://cdn.skypack.dev/dynamodb-loader-model';
</script>

README

dynamodb-loader-model

This module integrates the following modules:

  • AWS-SDK
  • DataLoader
  • ElasticSearch

DynamoDB without the hassle.

Getting Started

These instructions will get you a copy of the project up and running on your local machine for development and testing purposes.

Installing

Install it via npm.

npm install dynamodb-loader-model

You can find an example in the examples folder, or a boilerplate at this repo

Usage

Configuration

It all starts with the configuration object, this declares the keys of the DynamoDB table and the fields it has. Once set the configuration, the only thing that remains is extending the base Model class, passing the table name and the configuration object.

const { Model } = require('dynamodb-loader-model')

const modelConfig = {
  hashKey: 'id',
  rangeKey: 'second_id',
  fields: {
    id: { type: 'uuid' },
    second_id: { type: 'string', required: true },
    name: { type: 'string', default: 'no name' },
  },
  elastic: 'https://elastic.endpoint'
}

class MyModel extends Model {
  constructor () {
    super('your_table_name', modelConfig)
  }
}

Configuration object

  • hashKey - string - required: Hash Key of the table.
  • rangeKey - string: Range Key of the table.
  • fields - object - required: Information about the attributes of the table. Each of them with the following options:
    • type - string - required: Type of the attribute, if set to uuid and no value is passed when creating it, it gets defaulted to a generated uuuid (v4).
    • required - boolean: Whether it's a required attribute or not.
    • default - any: Default value for that attribute.
  • elastic - string: ElasticSearch endpoint.

Extending the model

The base model is intended to be extended, for example, you can add checks before you create an element. You can extend a Model like you would extend any Class.

class MyModel extends Model {
  constructor () {
    super('your_table_name', yourConfig)

    this.create = this.create.bind(this)
  }

  create (raw) {
    if (!raw.name || raw.name === '') {
      throw new Error('Name is a required field')
    }
    if (raw.name.length >= 50) {
      throw new Error('Name can only be 50 characters max')
    }
    return super.create(raw)
  }
}

psst: remember to bind this to the method, otherwise some unwanted side effects might rise.

Methods

The module exposes the following methods:

  • create: Creates an item and returns it.
    Promise<Object> create (<Object> data)
    
    remove: Removes an item.
    Promise<Boolean> remove (<String|Object> key)
    
    fetch: Fetches an item, if true is passed as the second parameter, DataLoader is skipped and the object is fetched directly from DynamoDB.
    Promise<Object> fetch (<String|Object> key, <Boolean> fresh)
    
    batchFetch: Fetches many items, calling the fetch method.
    Promise<Object> batchFetch (<List<String|Object>> key, <Boolean> fresh)
    
    update: Updates an item.
    Promise<Boolean> update (<String|Object> key, <Object> data)
    
    search: Executes a query on the ElasticSearch endpoint.
    Promise<Boolean> remove (<Object> query)
    

Model Manager

It is good practice not to share the same dataloader instance across different users, so, in order to do that there's a helper model.

Configuration

The manager receives the model that you want to manage and a timeout to destroy old instances.

const { Model, ModelManager } = require('dynamodb-loader-model')

// ...

class MyModel extends Model {
  constructor () {
    super('your_table_name', yourConfig)

    this.create = this.create.bind(this)
  }
}

module.exports = new ModelManager(MyModel, 30000)

Usage

const Model = require('./route/to/manager')

const instance = Model.getInstance('unique_id')

getInstance returns an instance of your Model.
If you want to destroy it before the timeout you can call destroyInstance with the same id you got the instance.

Use case

If you are using apollo-server-lambda, when creating the context you could get an instance of the model and attach it to the context, so you can call it from the schema. The unique_id could be the request id from aws.
This way you could have a dataloader instance for every request, enforcing good practices.

Running the tests

Prerequisites

In order to run the tests you need to have Serverless Framework installed.

npm install -g serverless

Clone the repository and install dependencies.

git clone https://gitlab.com/gotoar/graphql-acl-service.git
npm install

Run the tests.

npm run test

This will get you a copy of dynamodb-local, run it, run the tests and then kill the dynamodb-local process.

And coding style tests

You can check linting with this command.

npm run lint

Dependencies

  • aws-sdk
  • dataloader
  • dynamo-update-expression
  • elasticsearch
  • uuid

Contributing

Please read CONTRIBUTING.MD for details on our code of conduct, and the process for submitting merge requests to us.

Versioning

We use SemVer for versioning. For the versions available, see the tags on this repository.

Authors

  • Tomas Gorkin - Initial work

See also the list of contributors who participated in this project.

License

This project is licensed under the MIT License - see the LICENSE.md file for details

Acknowledgments

  • Hat tip to anyone whose code was used
  • Inspiration
  • etc