Creating an ODM with JavaScript
Tags: orm, odm, javascript
Published: 2018-02-11 12:45:43

---
title: Creating an ODM with JavaScript
published: true
description: Create an ODM with only 30 lines of code
tags: ORM, ODM, JavaScript
cover_image: https://thepracticaldev.s3.amazonaws.com/i/i30gg2usplyy89r5megg.jpg
---

I think the first time I heard about [ORM](https://en.wikipedia.org/wiki/Object-relational_mapping) was 10 years ago. I discovered this technique on a [NetBeans](https://netbeans.org) tutorial that explained how to generate Java classes from a MySQL database. I made some tests and it worked pretty well. I really liked the concepts but not the execution. I was quite frustrated with the generation step because I had always to regenerate classes for every structure update of the database. This problem was in fact related to the language used for the mapping that needs to be compiled. I said to myself that **it could be simpler to use a dynamic language that could generate these classes at runtime**. That’s why I started at that time to create my own *ORM* with JavaScript. It worked quite well but I get stuck with a big limitation: MySQL. **The relational structure of the tables did not match with JavaScript native objects**. So the mapping was not as easy as I wanted.

But things changed a few years later when NoSQL databases became more and more popular. We could use pure JSON objects as documents and we could manage NoSQL data as native JavaScript objects.

I will show you in this post how it is easy to create now an *ODM* (*Object-Document Mapping*) with JavaScript.

##My first ODM

Let’s start by choosing a NoSQL database. We will use my favorite one, I call it the **universal database**: **{}**.

```js
const db = {};
```

It is light, can work on a server or on a browser. Everything I like!

Now that we have the database, let’s stop a minute to think about object creation in JavaScript. Generally we use many parameters to create an object, like this:

```js
const luke = new Jedi('Luke', 'Skywalker');
```

But we could also pass one object as parameter:

```js
const luke = new Jedi({
  firstName: 'Luke',
  lastName: 'Skywalker'
});
```

Did you notice that this parameter looks like a document? That is the main idea of *ODM*: **use a document as a parameter of the class constructor**.

Now we have that in mind, let’s create the class that will manage the mapping between documents and class instances:

```js
class ODM {
  constructor(document) {
    // get class name
    const name = this.constructor.name;

    // add unique id
    if (!document._id) document._id = Math.random().toString();

    // create document
    if (!db[name]) db[name] = {};
    db[name][document._id] = document;

    // define accessors
    const configuration = {};
    Object.keys(document).forEach((prop) => {
      configuration[prop] = {
        get() {
          return db[name][document._id][prop];
        },
        set(value) {
          db[name][document._id][prop] = value;
        }
      };
    });

    // set accessors
    Object.defineProperties(this, configuration);
  }
}
```

In this class we made several things:

* we get the name of the collection in the database: in our case the class name,
* we generate a unique id for the document,
* we add the document in the database and
* we create getter and setter for every property of the instance that will manage the related document in the database.

Now let’s do some tests with it:

```js
// create a Jedi class
class Jedi extends ODM { };

// create an instance with a document
const luke = new Jedi({
  _id: 'luke',
  firstName: 'Luke',
  lastName: 'Skywaker'
}); 

// update the instance
luke.lastName = 'Skywalker';

// check that the value has been changed in the database
db.Jedi.luke.lastName;
// => 'Skywalker'
```

**We have now a complete synchronization between documents and instances**. And we did that with only 30 lines of code!

##Documents exportation

Let go further. And if want to **export documents**? It is very easy to do that:

```js
db.export = (name) => {
  return JSON.stringify(db[name]);
};
```

In our case **we suppose that all the documents are JSON valid** so that we can export them with a native JavaScript API.

Now let’s make some tests with it:

```js
// create the Jedi class
class Jedi extends ODM { };

// create an object with a document
const luke = new Jedi({
  _id: 'luke',
  firstName: 'Luke',
  lastName: 'Skywaker'
}); 

db.export('Jedi');
// => '{\"luke\":{\"firstName\":\"Luke\",\"lastName\":\"Skywaker\",\"_id\":\"luke\"}}'
```

In this example we export all the documents created for a specific class. That means that **we can now serialize all the objects into a string**. Pretty cool, does it?

##Documents importation

Now we will do something a little more complicated with the importation of documents. **When we import documents on a specific collection, we want to create their related objects:**

```js
// create classes list
const classes = {}; 

db.import = (name, documents) => {
  db[name] = JSON.parse(documents);

  // create instances
  Object.keys(db[name]).forEach((id) => {
    new classes[name](db[name][id]);
  });
};
```

Now let’s update a bit the main class for that purpose:

```js
// create instances list
const instances = {}; 

class ODM {

  constructor(document) {
    // get class name
    const name = this.constructor.name;

    // add unique id
    if (!document._id) document._id = Math.random().toString();

    // create document
    if (!db[name]) db[name] = {};
    db[name][document._id] = document;

    // define accessors
    const configuration = {};
    Object.keys(document).forEach((prop) => {
      configuration[prop] = {
        get() {
          return db[name][document._id][prop];
        },
        set(value) {
          db[name][document._id][prop] = value;
        }
      };
    });

    // set accessors
    Object.defineProperties(this, configuration);

    // add it to the list of instances
    instances[document._id] = this;
  }
}
```

The difference with the previous class is that we add now the created instance in the list *instances*.

Let ‘s test it:

```js
// create Jedi class
classes.Jedi = class Jedi extends ODM {};

// import Jedi documents
db.import('Jedi', '{\"luke\":{\"firstName\":\"Luke\",\"lastName\":\"Skywalker\",\"_id\":\"luke\"}}');

// access the created instance
instances.luke.firstName;
// => 'Luke'

```

We can now deserialize datas into objects. Moreover **we can also know the exact number of created objects at any moments**, it is the number of objects in my *instances* list.

##Managing data relationships

And what about relations? In NoSQL world, we can simulate relations by using the *id* of a document as a value of a property to create a link. If we follow this pattern, managing relations becomes very simple:

```js
class ODM {

  constructor(document) {
    // get class name
    const name = this.constructor.name;

    // add unique id
    if (!document._id) document._id = Math.random().toString();

    // create document
    if (!db[name]) db[name] = {};
    db[name][document._id] = document;

    // define accessors
    const configuration = {};
    Object.keys(document).forEach((prop) => {
      configuration[prop] = {
        get() {
          const value = db[name][document._id][prop];
          // return an instance or a value
          return value.indexOf('@') !== -1 ? instances[value.replace('@','')] : value;
        },
        set(value) {
          if (classes[value.constructor.name]) {
            // store the id of the instance
            db[name][document._id][prop] = value._id;
          } else {
            db[name][document._id][prop] = value;
          }
        }
      };
    });

    // set accessors
    Object.defineProperties(this, configuration);

    // add it to the list of instances
    instances[document._id] = this;
  }
}
```

To distinguish a value from a link, we add this new rule: if a value begins with **@**, it means that it represents the *id* of a document.

Let’s create now a link between objects:

```js
const vador = new classes.Jedi({
  _id: 'vador',
  'firstName': 'Dark',
  'lastName': 'Vador'
)};

const luke = new classes.Jedi({
  _id: 'luke',
  'firstName': 'Luke',
  'lastName': 'Skywalker',
  'father': '@vador'
)};
  
luke.father.lastName;
// => 'Vador'
```

Now, let’s do this link at API level:

```js
const vador = new classes.Jedi({
  _id: 'vador',
  'firstName': 'Dark',
  'lastName': 'Vador'
});

const luke = new classes.Jedi({
  _id: 'luke',
  'firstName': 'Luke',
  'lastName': 'Skywalker'
});
  
// set father link  
luke.father = vador;

db.export('Jedi');  
// => '{\"vador\":{\"_id\":\"vador\",\"firstName\":\"Dark\",\"lastName\":\"Vador\"},
// \"luke\":{\"_id\":\"luke\",\"firstName\":\"Luke\",\"lastName\":\"Skywalker\",\"father\":\"@vador\"}}'
   
```

As you see, we can create *one-to-one* relationship very easily with *ODM*.

##Conclusion

*ODM* is a technique that you have to use more often in your code, it is not complicated and very powerful. Because of the heavy coupling between documents and objets, **you know at every moments what are the components of your application, how many they are and what data they manage**.

If you think harder, you realize that in fact **ODM is a way to manage the store of your application**. Every kind of components has its own store (i.e. collection in the database) and can be managed like in *Redux*. But here you are at the functional level (you manage functional objects), not at the technical level (where you manage data).

I made some *CodePen* examples so that you can start to play right now with *ODM*:
* [Example 1](https://codepen.io/ecarriou/pen/EQmavZ?editors=0011)
* [Example 2](https://codepen.io/ecarriou/pen/oEWgop?editors=0011)
* [Example 3](https://codepen.io/ecarriou/pen/LQyErQ?editors=0011)

If you want to go deeper, you can have a look at [System Runtime](https://github.com/design-first/system-runtime), a JavaScript library that I have created that applies all the pattern I talked about in this post.

___

*Credits: cover image by [Sebastien Gabriel](https://unsplash.com/@sgabriel).*