DEV Community

Cover image for An introduction to MongoDB and Node.js using Monk
Francisco Mendes
Francisco Mendes

Posted on

An introduction to MongoDB and Node.js using Monk

Usually programmers entering the Node.js universe for the first time associate it with MongoDB and as I am publishing this article I feel that I am reinforcing this topic even more.

However, I am not here to talk about whether NoSQL databases are better than SQL databases or not. I think that each one has its own use cases and we must always need to take into consideration the context.

There are several options for connecting our Api to a MongoDB database. However, I can safely say that mongoose is the most popular package and the one that in most cases should be used. However, in this article I will talk about Monk, although they have different approaches, they solve exactly the same problem.

Both are developed by the same organization, but first we must first understand what NoSQL is.

What are NoSQL databases?

NoSQL databases are not tabular and store data differently from relational tables. In the case of MongoDB, the database instead of having tables has collections, and instead of having columns it has JSON documents.

Each document contains pairs of fields and values. Values can usually be a variety of types, such as strings, numbers, booleans, arrays, or objects, and their structures typically align with the objects that the developers are working with in the code.

Precisely for these reasons, they are great for a wide variety of use cases and can be used as a general-purpose database.

How is a Document structured?

In a NoSQL database, an article record is usually stored as a JSON document. For each article, the title, description, content, author's name and author's ID are stored as attributes in a single document. In this way, the data is optimized for faster and more intuitive development.

Why should you use a NoSQL database?

NoSQL databases are designed for various data access standards that want low-latency response times. If you want to prototype an application as soon as possible or if you don't want to deal with relationships between data in a structured way, MongoDB can be the ideal solution for you. I would also add that, unlike SQL databases, as you can see, we are free to model the data in the way that suits us best.

And MongoDB databases are flexible to the point of supporting changes without requiring a radical reengineering of the data model and has an architecture that is easy to scale.

Why Monk?

I understand that MongoDB has a driver for Node.js. And many people are against abstractions, since the MongoDB driver is already more than enough to perform any operation. However, I think that monk is easier to develop an Api. It is much easier to configure the client and to read the documentation. And these points are important for people new to Api development in Node.js, or if you just want to get your application online as soon as possible.

Let's code

Now that we have some notions of what are NoSQL databases, MongoDB and documents. We are going to create a simple CRUD.

First we will install the following dependencies for the development of the Api:

npm i express @hapi/boom monk
Enter fullscreen mode Exit fullscreen mode

Then we will create a simple Api, just to make sure everything is working correctly:

const express = require("express");

const app = express();

app.use(express.json());

app.get("/", (req, res) => {
  return res.json({ message: "Hello World 👋🇵🇹" });
});

const start = (port) => {
  try {
    app.listen(port, () => {
      console.log(`Api running at: http://localhost:${port}`);
    });
  } catch (error) {
    console.error(error);
    process.exit();
  }
};
start(3333);
Enter fullscreen mode Exit fullscreen mode

Next we will import monk and we will define the connection URL with our instance of MongoDB. The URL is structured as follows:

mongodb://[user]:[password]@[host]:[port]/[database]?authSource=admin
Enter fullscreen mode Exit fullscreen mode

So the code should be as follows:

const express = require("express");
const monk = require("monk");

const app = express();
const db = monk("mongodb://root:root@localhost:27017/monk?authSource=admin");

// Hidden for simplicity
Enter fullscreen mode Exit fullscreen mode

Now we have to give a name to the collection that will keep all of our documents. In this example we will call the collection "posts". Like this:

const express = require("express");
const monk = require("monk");

const app = express();
const db = monk("mongodb://root:root@localhost:27017/monk?authSource=admin");
const Posts = db.get("posts");

// Hidden for simplicity
Enter fullscreen mode Exit fullscreen mode

Now we are going to work on our routes and our HTTP verbs. In this case, we don't have any documents in our Posts collection yet. So let's start by creating our first endpoint using the insert method. To learn more about the method, read this.

app.post("/posts", async (req, res) => {
  try {
    const data = await Posts.insert({ ...req.body });
    return res.json(data);
  } catch (error) {
    throw boomify(error);
  }
});
Enter fullscreen mode Exit fullscreen mode

Just as the code indicates the document will be inserted in our collection according to what comes from the body.

@Hapi/boom was installed so that whenever an error occurs when consulting a resource (which in this case is our database) it formats the error so that it is easier to read and identify.

Now we will create an endpoint to search our collection for all your documents. To learn more about the find method, read this.

app.get("/posts", async (req, res) => {
  try {
    const data = await Posts.find();
    return res.json(data);
  } catch (error) {
    throw boomify(error);
  }
});
Enter fullscreen mode Exit fullscreen mode

If we want to get a document specifically from our collection, instead of using find we use findOne and specify the attribute by which we want to do the search. In this case we are going to look for the document id. To learn more about the findOne method, read this.

app.get("/posts/:id", async (req, res) => {
  try {
    const { id } = req.params;
    const data = await Posts.findOne({ _id: id });
    return res.json(data);
  } catch (error) {
    throw boomify(error);
  }
});
Enter fullscreen mode Exit fullscreen mode

If we want to update an attribute of a specific document, the ideal is to use the findOneAndUpdate method, in this way we check if the document exists and then updates it. And then returns it. To learn more about this method, read this.

app.patch("/posts/:id", async (req, res) => {
  try {
    const { id } = req.params;
    const data = await Posts.findOneAndUpdate({ _id: id }, { $set: req.body });
    return res.json(data);
  } catch (error) {
    throw boomify(error);
  }
});
Enter fullscreen mode Exit fullscreen mode

Last but not least, we just need to delete a specific document, for that we will use the findOneAndDelete method. Like the previous method, it also checks first if the document exists and then deletes it. As soon as this action is finished, the object of the deleted document returns. Learn more about this method here.

app.delete("/posts/:id", async (req, res) => {
  try {
    const { id } = req.params;
    const data = await Posts.findOneAndDelete({ _id: id });
    return res.json(data);
  } catch (error) {
    throw boomify(error);
  }
});
Enter fullscreen mode Exit fullscreen mode

The final result should look like the following:

const express = require("express");
const monk = require("monk");
const { boomify } = require("@hapi/boom");

const app = express();
const db = monk("mongodb://root:root@localhost:27017/monk?authSource=admin");
const Posts = db.get("posts");

app.use(express.json());

app.get("/", (req, res) => {
  return res.json({ message: "Hello World 👋🇵🇹" });
});

app.post("/posts", async (req, res) => {
  try {
    const data = await Posts.insert({ ...req.body });
    return res.json(data);
  } catch (error) {
    throw boomify(error);
  }
});

app.get("/posts", async (req, res) => {
  try {
    const data = await Posts.find();
    return res.json(data);
  } catch (error) {
    throw boomify(error);
  }
});

app.get("/posts/:id", async (req, res) => {
  try {
    const { id } = req.params;
    const data = await Posts.findOne({ _id: id });
    return res.json(data);
  } catch (error) {
    throw boomify(error);
  }
});

app.patch("/posts/:id", async (req, res) => {
  try {
    const { id } = req.params;
    const data = await Posts.findOneAndUpdate({ _id: id }, { $set: req.body });
    return res.json(data);
  } catch (error) {
    throw boomify(error);
  }
});

app.delete("/posts/:id", async (req, res) => {
  try {
    const { id } = req.params;
    const data = await Posts.findOneAndDelete({ _id: id });
    return res.json(data);
  } catch (error) {
    throw boomify(error);
  }
});

const start = (port) => {
  try {
    app.listen(port, () => {
      console.log(`Api running at: http://localhost:${port}`);
    });
  } catch (error) {
    console.error(error);
    process.exit();
  }
};
start(3333);
Enter fullscreen mode Exit fullscreen mode

Final notes

The idea of this article was to introduce Monk and to show that we can connect to a MongoDB database in a quick and simple way. I hope I was clear in explaining things and that you found the article interesting.

What about you?

Do you already use MongoDB in your projects?

Top comments (0)