loading...

Introducing Jedlik: Nicer DynamoDB for Developers

harrim91 profile image Michael Harrison ・5 min read

AWS DynamoDB is a really cool piece of technology - a high performance, scalable, fully-managed, cloud-based database service.

However, if you've ever worked with it as a developer, you've probably found yourself getting a little frustrated at how different the experience is compared to other databases.

That's why over the last couple of years I've built Jedlik - an object-document mapper (ODM) for Node.js, written in TypeScript, which aims to make working with DynamoDB a better experience for developers.

It was published recently as an open-source project on my companies GitHub, and it's available to download from NPM.

In this post I'm going to try to explain a little what Jedlik is, and what its main features are.

What's an ODM?

An ODM (object-document mapper) or ORM (object-relational mapper) is a library that maps records in your database into JavaScript objects and generally makes your life easier when you're working with a database.

They allow you to define schemas and build models of your data, allowing you to interact with your data in an object-oriented way and providing validations to keep your data in shape. They also provide similar and intuitive ways of reading and writing data to the database.

If you've ever used Sequelize (for SQL databases) or Mongoose (for MongoDB) then you've been using an ODM!

To use DynamoDB in Node, you have to use the AWS SDK. The SDK is generally a great piece of kit, but as a database tool I always found it to be a bit clunky and unintuitive when writing queries, as well a little light on features.

Jedlik was built so that you can have all of these benefits whilst using DynamoDB.

Features

Models, Schemas, Validations

A core feature of Jedlik is that it allows you to create models of your data. Models define the schema of your data, and provide functionality that makes it a lot easier to read and write to your DynamoDB table.

Here's how you define a User model using Jedlik in TypeScript. I recommend using Joi for creating schemas!:

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

// Using TypeScript gives you awesome autocompletions and other cool things, but it's not necessary to use them!
type MovieProps = {
  title: string;
  year: number;
  genre: string;
  rating: 1 | 2 | 3 | 4 | 5;
};

// Schemas are used to validate your data before saving it!
const schema = Joi.object({
  title: Joi.string().required(),
  year: Joi.number().integer().required(),
  genre: Joi.string().required(),
  rating: Joi.number().integer().min(1).max(5).required(),
});


const Movies = new jedlik.Model<UserProps>({ table: 'movies', schema });

Using this Model it becomes a lot easier to read and write data to the database.

Let's see how we would create a new Movie using the AWS SDK for DynamoDB:

const dynamoDB = new AWS.DynamoDB.DocumentClient();

await dynamoDB.put({
  TableName: 'movies',
  Item: {
    title: 'The Lion King',
    year: 1994,
    genre: 'Amazing',
    rating: 5,
  },
}).promise();

This isn't too bad, but if you put an invalid value (e.g. a rating of 10), or if you missed out a field (e.g. no genre), or added in a new field (e.g. tagline) then DynamoDB would do nothing to tell you about it.

Using Jedlik, the same thing would look like this:

// Movies is the model we created above
const movie = Movies.create({
  title: 'The Lion King',
  year: 1994,
  genre: 'Amazing',
  rating: 5,
});

await movie.save();

Hopefully you'll agree this looks a little nicer. But also, if you tried to save some badly formatted data, Jedlik wouldn't let you. You might get a TypeScript error, which would come up in your text editor and not allow you to compile your code. But if not, at runtime you would get a validation error from the schema, and your data wouldn't be saved.

Queries

In my opinion, the worst part about DynamoDB is it's query language. Your queries are normally broken into at least three parts: an Expression (a tokenised string), and two objects ExpressionAttributeNames and ExpressionAttributeValues which define the tokens in the Expression strings.

Let's take a look at how we might use the DynamoDB SDK to get a list of horror movies from the year 1998, with a rating of at least 4.

const dynamoDB = new AWS.DynamoDB.DocumentClient();

const { Items } = await dynamoDB.query({
  TableName: 'movies',
  ExpressionAttributeNames: {
    '#year': 'year',
    '#genre': 'genre',
    '#rating': 'rating',
  },
  ExpressionAttributeValues: {
    ':year': 1998,
    ':genre': 'Horror',
    ':rating': 4,
  },
  KeyConditionExpression: '#year = :year',
  FilterExpression: '#genre = :genre AND #rating >= :rating',
}).promise();

I'm not going to explain what's going on there. But it's not very nice to read, it's not very nice to write, and it's definitely not nice to try and generate programatically!

Instead, lets take a look at how you can do this same query with Jedlik:

const movies = await Movies.query({ year: 1998 }, {
  filters: {
    $and: [
      { key: 'genre', operator: '=', value: 'horror' },
      { key: 'rating', operator: '>=', value: 4 },
    ]
  }
});

Again, hopefully you'll agree that this is a lot nicer to use - it's meant to be similar to the queries you would write with other libraries such as Mongoose or Sequelize.

Object-Oriented DynamoDB

We saw earlier when we created a new Movie using Jedlik, we were able to call a save method on the movie that was created.

This is because the objects returned by methods such as create, query etc are not plain JavaScript object, but are actually Documents - a Jedlik class which gives you some added functionality:

const movie = Movies.create({
  title: 'The Lion King',
  year: 1994,
  genre: 'Amazing',
  rating: 5,
});

// save the movie to the database
await movie.save();

// print the title
console.log(movie.get('title'));

// set the rating
movie.set({ rating: 4 });

// save it again
await movie.save();

// convert the document into a plain object
console.log(movie.toObject());

One of the next features I'd like to add is the ability to add your own custom methods to Models.

The Future

Jedlik is stable, fully-tested and ready to use! So please try it out! If you use it and like it, please let me know on here, on GitHub or on Twitter - I'd be thrilled to hear from you!

It's also an open-source project, so if you want to suggest any improvements, or even contribute any improvements to the project, you would be more than welcome!

There are still a lot of features I'd like to add, so keep your eyes peeled for new releases and features coming soon.

Posted on by:

harrim91 profile

Michael Harrison

@harrim91

Full stack TypeScript with AWS at Peak. Tutor at Manchester Codes. From Manchester, UK

Discussion

pic
Editor guide
 

Been waiting for something like this to come along. Why create a new ODM though and what's the difference between jedlik and dynamoose? Dynamoose seems to be pretty popular but my experience with it wasn't that great, it didn't cover some of my use cases at the time.

One last question. Is jedlik production ready?