How I Made A NoSQL Clone With JavaScript Classes
Tags: javascript, database, productivity, programming
Published: 2021-04-12 13:30:57

_This post was originally made in my blog - [How To Make a NoSQL Clone With JavaScript Classes](https://webdeverguide.com/how-to-make-a-nosql-clone-with-javascript-classes)_

Hello devers. In this article I am going to show you **how I made NoSQL Database clone with JavaScript Classes**. This project is a very short one, took me like 3 hours or so to complete it. Though it is short, it conveys in it the ability to begin the NEXT BIG THING IN NOSQL (I actually don’t have any plan for that). This program is precisely cloning MongoDB and Firestore database.

## What Inspired me to make this Project.

Learning and working with classes inspired me to work on this project. I recently just completed the [Promises module in codecademy’s JavaScript course](codecademy.com). I thought it would be nice to practice what I just learnt, so I decided to build this project.

It may have been short, but I did face a bit of challenge while making firestore form of updating documents. I will show you how I resolved it on my own.

## How this Database Works
Basically as a back-end web developer of full-stack web developer, you can create a document (works like creating modules in mongoose) and add as much data as you want with any data type of course. These documents are classes, so you will only be making a class that extends the document class.

With that class you made, you can make instances and play around with the functions (queries) of the database. It’s easier than you think. What functions or queries does this database have.

1. **create()**
2. **update()**
3. **findById()**
4. **delete()**
5. **allData()**

Pretty basic right! Enough with the theory, let’s jump into the code.

Before we jump into code, I must emphasize that this code is towards a clean code. Everything I did was with the clean code principles in mind and I may make mention of some of them.

## Making The Document as a Class

First thing I did was to create a file, named it database. Then i created a class, called it `Document`

```js
class Document {
 constructor() {
  this.documentData; // array of data
 }
}
```

Let’s leave it as that for now. So far we can easily tell that `documentData` is not assigned to anything. Now this is because I want the `documentData` to come from the class that will inherit this class.

Since this class is never going to be used directly, then we will not have any parameters. So it is compulsory that the class that will inherit this class should assign a value (array specifically) to the `documentData`

## Adding The Queries/Functions to the Document class

### Get All Data
In this query, I will use JavaScript Getters to handle this. I just love using it for getting anything in a class or object. Just a reminder, I have a full project ([appointment booking app](https://dev.to/elijahtrillionz/building-a-project-with-javascript-objects-1dn)) that uses only JavaScript Getters and Setters in JavaScript Objects. Go check it out.

```js
// still inside the Document class, right beneath the constructor function
get allData() {
 return new Promise((resolve) => {
   setTimeout(() => {
     resolve(this.documentData);
   }, 100);
 });
}
```

__Explanation:__

I declared an getter, named it `allData` (meaning it get’s all data. Good naming convention for clean code. To be more specific you could say `allDataInDocument`).

I also declared a promise that will resolve with all the document data after 100 milliseconds delay. And our `allData` accessor will return this promise whenever it is called.

### Create a Data
This function/method will create a new data in the document.
```js
// still inside the Document class, right beneath the allData() getter 
create(data) {
 return new Promise((resolve, reject) => {
  setTimeout(() => {
   if (typeof data === 'object' && !Array.isArray(data)) {
    // make sure data is an object; new Object()
 
    this.documentData.push(data);
    return resolve(this.documentData);
   }
 
   return reject('Data must be an object');
  }, 500);
 });
}
```

__Explanation:__

I created a method, called it `create`, and passed a parameter to it `data`. This method will a return promise that will be rejected if data is not an object (arrays will be rejected too).

If the `data` is an object, then the promise will push that object to the `documentData` array and then resolve to all available data in that document so far.

One more thing I’d like to add to this method is the ability to generate it’s own `id` if there isn’t any provided by the developer. Just like mongoDB’s _id. In generating this `id` you could use uuid or some other dependencies but I want to keep it simple and free from dependencies. Here is how:

First I will put together different characters in a keyboard like:

```js
const letter = 'abcdefghijklmnopqrstuvwxyz';
const allCharacters = `${letter}1234567890123456789123456789$&@*£€¥%${letter.toUpperCase()}`;
// then i will split the allCharacters into an array like so 
const allCharactersInArray = allCharacters.split('');
```

The next thing I would do is make a function that will randomly pick characters from our array above.

```js
function randomise() {
 const randomCharacter = allCharactersInArray[Math.floor(Math.random() * allCharactersInArray.length)];

 return randomCharacter;
}
```

OK it’s getting more interesting. Now let’s make another function (the main function) to randomly select as many characters as we want and turn it into one string i.e:

```js
function generateId() {
 const generatedIds = [];
 for (let i = 0; i < 16; i++) {
   suggest.push(randomise());
 }
 return generatedIds.join(''); // will generate a very strong id
}
```

The for loop in the `generateId` function will randomly select 16 characters from our `allCharactersInArray`. **DO NOT** USE THIS AS A PASSWOD GENERATOR (it's not secured). [Here is the full code snippet](https://gist.github.com/Elijah-trillionz/25ed2b79fe56f16cf404190f0116add7). Now let's use it in our `Document` class.

Note: All we did to generate id was done outside of the Document class. If we put the id generator feature in this class, we would have:

```js
create(data) {
 return new Promise((resolve, reject) => {
   setTimeout(() => {
    if (typeof data === 'object' && !Array.isArray(data)) {
      // make sure data is an object; new Object()
      if (!data.id) {
       const generatedId = generateId();
       data.id = generatedId;
      }
 
      this.documentData.push(data);
      return resolve(this.documentData);
     }
 
     return reject('Data must be an object');
   }, 500);
 });
}
```

We want to make sure that it only generates when an `id` has not been assigned to the data yet.

### Finding a Data
To find a data, I will make a method that will find data by ids.

```js
findById(id) {
 return new Promise((resolve, reject) => {
  setTimeout(() => {
    const doc = this.documentData.filter((datum) => {
    return datum.id === id;
   });
   if (!doc[0]) {
    return reject('This data does not exist');
   }
 
   return resolve(doc[0]);
  });
 }, 1000); // shouldn't take so much time
}
```

__Explanation:__

This function returns a promise that is resolved to a `data` if the `data` exists. We first of all looked up the given `id` to see which document has that id as an `id`. If we have any results we `resolve` the promise with that `data` else will `reject` with a message. The promise will delay for 1 second.

### Updating a Data
In firestore, when updating a data, the document id of that data is used to get the data. This data is an object and is updated as an object. So that means we have a new object and an old object.

Old object as in coming from the database and new object from the back-end developer. In the new object, the keys/properties are compared with the old object and any properties that matches will have its value being replaced by the new object’s value. In code:

```js
const oldObject = { name: 'John Doe', nationality: 'South Africa' };
const newObject = { name: 'John Doe Seth' };
 
oldObject.update(newObject) // update is a firestore query/method for updating documents.
// oldObject will return { name: 'John Doe Seth', nationality: 'South Africa' }
```

Now if there is a new property from the `newObject`, firestore will simply append it to the `oldObject`. Now ours is gonna be exactly like this, but the catch is how do I replace an existing object with a new object. This is where I got stuck for some time. But I figured it out with loops. [Here is the snippet](https://gist.github.com/Elijah-trillionz/b198ae5bcde04a85017958fbf0180cc2).

```js
// still inside the document class just beneath findById method
update(docId, newData) {
 // the docId represents the id either given by the back-end programmer or a default generated id for that document like mongodb's generated _id
 
 return new Promise((resolve, reject) => {
  setTimeout(async () => {
   try {
   // step 1, find the data in d database
    const oldData = await this.findById(docId);
    // step 2, update with new data
 
    // using firebase standards, updating a doc is with an object
    if (typeof newData === 'object' && !Array.isArray(newData)) {
     return resolve(changeDataInObjects(newData, oldData)); // changeDataInObjects is declared outside the document class in the next snippet
    }
 
    return reject('New data must be an object');
   } catch (err) {
    return reject(err);
   }
  }, 1200);
 });
}
```

```js
// outside the Document class
function changeDataInObjects(newData, oldData) {
 for (let i in oldData) {
  for (let j in newData) {
   if (i === j) { // i and j represents the property names of oldData and newData respectively
    oldData[i] = newData[j];
   } else {
    oldData[j] = newData[j];
   }
  }
 }
 return oldData;
}
```

The `changeDataInObjects` function is what does the updating. The reason i brought it out of the Document class is because I wanted a clean code. I will talk more on that in the future. [So you should subscribe to my newsletter](https://webdeverguide.com).

The last method is `delete`.

### Deleting a data

```js
// inside the document class just beneath the update method
delete(id) {
  return new Promise((resolve, reject) => {
   setTimeout(async () => {
    try {
     const documentToDelete = await this.findById(id);
     const indexOfDocument = this.documentData.indexOf(documentToDelete);
 
     this.documentData.splice(indexOfDocument, 1);
     resolve(this.documentData);
    } catch (err) {
      reject(err);
    }
   }, 1000);
  });
 }
}
```

Pretty straightforward.

Now that we are done with the `Document` class we will now export it with `module.exports` since it’s a node environment.

In a separate file, after I import the `Document` class I will create a class call it `Users`:

```js
class Users extends Document {
 constructor() {
  super();
 
  this.documentData = [];
 }
}
```

After I have done that, I will export the `Users` class.

In another file, I will name this file as server.js. This file is where I can make use of all the methods we have created via the `Users` class.

Let’s say I want a document for pro-users, I would say;

```js
const proUsers = new Users();
```

Now with that I can access proUsers.create(), proUsers.delete(), proUsers.update(), proUser.allData etc.

```js
async function addNewUser() {
 try {
  await proUsers.create({
   id: 1,
   name: 'John Doe',
   username: 'johndoe@gmail.com',
  });
 } catch (err) {
   console.log(err);
 }
}
```
Try to play around with the other methods.

## Conclusion
This may have been a very small project, I did learn a lot from it. Also I believe it has added to my confidence as a programmer to go for more projects. That’s why you have to keep coding, it keeps you fit. [Here is the complete code for this project](https://github.com/Elijah-trillionz/nosql-database-clone/).

I have a [Discord Server](https://discord.gg/wyretYpZua) that involves a lot of coding activities like coding tasks, project ideas, learning new technology, project challenge, 100DaysOfCode etc. I have made two bots currently on my own just to make this server helpful to all programmers and I am only at the starting point.

Also I now have my blog, which explains why I have not always been posting here. So get acquainted with my blog, join the newsletter so that you can follow along with all posts I make exactly when I make them. Am not going to post on dev.to Everytime again. It's called [Web Dever Guide](https://webdeverguide.com), for web Developers.

I recently just made a post on [How to become a Web Developer in 2021](https://webdeverguide.com/how-to-become-a-web-developer-in-2021).

Thank you for reading. Have a wonderful time coding and learning.