Database integration🪢 in node/express - A maintainable approach
Tags: beginners, node, database, mongodb
Published: 2023-03-13 06:00:00

<small>Welcome to <b>part 2</b>. Follow along this series to develop a <em>feature-packed</em> 3-tier Authentication System outlined in successive parts.</small>

_**Summary:** In this article, we learn to integrate a node application with a database using a promise-based ODM/ORM in a maintainable approach_.

> _**Note 🔔**_:  
> _A complete Repo of this series can be found on [Github](https://github.com/hane-smitter/MERN_login_system)✩. Drop a star💫️ if you found it useful._

<!-- ## Prerequisites

To follow along this tutorial comprehensively, you need at least the following:

- [NodeJs](https://nodejs.org/en/download/) installed on your computer
- Basic understanding of NodeJs and ExpressJs -->

<details>
<summary><b>Table of Contents</b></summary>
<ol>
<li><a href="#directory-structure">Directory Structure</a></li>
<li><a href="#create-raw-packagejson-endraw">Create <code>package.json</code></a></li>
<li><a href="#create-raw-env-endraw-file">Create <code>.env</code> file</a></li>
<li><a href="#install-dependencies">Install dependencies</a></li>
<li><a href="#set-up-database-connection">Set up database connection</a></li>
<li><a href="#run-database-connection-in-application">Run database connection in application</a></li>
<li><a href="#conclusion">Conclusion</a></li>
</ol>
</details>

<!-- ## Project structure


To get started create a **parent folder**(_MERN\_login\_system_) that will contain the **frontend**(_client_) and **backend**(_server_) of our application, as illustrated by the image below:

  <figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ho7ggwqckp40361kmtdo.png" alt="Root folder structure" />
  <figcaption>
  Root(MERN_login_system) folder structure
  </figcaption>
  </figure>

Now that we want to work on our backend, Let's change into the `server` directory. In the rest of the tutorial, we will refer to this `server` directory as the root. -->

## Directory structure

```diff
📂server/
  ├── 📄package.json
  ├── 📄.env
  └── 📂src/
      ├── 📄index.js
      └── 📂dbConn/
           └── 📂mongoose/
               └── index.js
```

This is the structure with focus on the `src` directory. The `src` directory will contain files with code we write to run our application. We will add code in these files as we progress through the article. You can always double check to create these files in their correct locations.

## Create `package.json`

From the root level of `server` directory, run the command `npm init -y`.<br />
This command will generate a `package.json` file inside our `server` folder. Then edit the `package.json` file to look like this:

```json
{
	"name": "api",
	"version": "1.0.0",
	"description": "",
	"private": "true",
	"main": "src/index.js",
	"scripts": {
		"start": "node src/index.js",
		"dev": "nodemon src/index.js",
		"test": "echo \"Error: no test specified\" && exit 1"
	},
	"keywords": [],
	"author": "hane-smitter",
	"license": "MIT"
}
```

Importantly, change the `main` property to point to `"src/index.js"` which will be the entry file to our application. And also add `"start"` and `"dev"` scripts:

```js
// ...
"scripts": {
    "start": "node src/index.js",
    "dev": "nodemon src/index.js",
    "test": "echo \"Error: no test specified\" && exit 1"
}
// ...
```

## Create `.env` file

A `.env` or _dotenv_ file is a **key-value** text configuration file for defining application's environment constants.

Let's create environment variables our application will use.

To do this, create a `.env` file at the `server` directory root([refer](#directory-structure "section: directory structure")) and add the following variable(s):

```
MONGODB_URI=
```

`MONGODB_URI` is the MongoDB database connection string. It is currently empty but we will assign a value to it later on.

_**Note:** Remember to **include `.env` file to your `.gitignore`**, should you initialize a git project. `.env` file is meant to be private to you and should not be shared outside of your local repository._

## Install dependencies

We have initialized our project with `package.json` file. We can now add dependencies from the [npm](https://npmjs.com) registry that we will need in this project.

We will use the following dependencies:

-   [Express](https://npmjs.com/package/express) - A web framework written in JavaScript and hosted within the Node.js runtime environment, to generally help us handle requests coming to our server.
-   [Mongoose](https://npmjs.com/package/mongoose) - An object modelling tool for MongoDB to allow us to enforce a specific mongoDB schema from our application.
-   [Dotenv](https://npmjs.com/package/dotenv) - To load environment variables declared in `.env` file.
-   [Nodemon](https://npmjs.com/package/nodemon) - To restart our server after file changes.

Therefore install the dependencies with terminal opened from the root of `server` directory by running the command:

```bash
npm install mongoose express dotenv --save
```

Then install `nodemon` as a **development dependency**, which means that we only need `nodemon` during development time of our application:

```bash
npm install nodemon --save-dev
```

## Set up database connection

In this step, we will connect to a MongoDB database hosted in the cloud, _i.e **MongoDB Atlas**_. If you do not have MongoDB installed locally on your machine, you can still hit the ground running with the hosted version👍.

**First**, you will _need a connection string connecting to MongoDB_ as a cloud service. Create [MongoDB Atlas](https://account.mongodb.com/account/register) account or [login](https://account.mongodb.com/account/login) if you already have one. Then follow this [guide](https://www.mongodb.com/docs/guides/atlas/connection-string/) to obtain your connection string.

Once you have your connection string, open the `.env` file and set the `MONGODB_URI` like shown:

```bash
MONGODB_URI=mongodb+srv://<user>:<password>@sample.host/<db_name>?retryWrites=true&w=majority
```

You can as well substitute with URI of local mongoDB installation. In this case your connection URI will look like: `mongodb://127.0.0.1:27017/<dbname>`.

**Then**, open terminal at `server` directory root, create `src` directory and change into it:

```bash
mkdir src
cd src
```

`src` is where code we write for our backend will reside.

We will start by creating a file for our MongoDB connection.<br />

Open/create `index.js` file located at `server/src/dbConn/mongoose/`([refer](#directory-structure "section: directory structure")). Paste the following code in it:

```javascript
const mongoose = require("mongoose");

// Pull in environment variable
const connURI = process.env.MONGODB_URI;

mongoose.set("strictQuery", false);
mongoose.set("bufferCommands", false); // Disable buffering

// Connect to MongoDB and store connection in variable
const db = mongoose.connect(connURI);

db.catch((err) => {
	if (err.message.code === "ETIMEDOUT") {
		console.log(`----${err.message.code}----`);
		// console.log(err);
		mongoose.connect(connURI);
	}
	console.log(`----${err.message}----`);
});

module.exports = db;
```

The code above is straight-forward where we get connection string to our MongoDB database from the environment variable: `const connURI = process.env.MONGODB_URI;`. We connect to it and store the conection in `db` variable. We chain `catch()` to handle error that may occur during the connection. Though we don't handle the _"fulfilled"_ case here. And if a **timeout** error occurs, we retry to connect. Otherwise we just log the error to the terminal.

Finally we default export the mongoose connection stored in `db` variable.

## Run database connection in application

Here we need to include database connection in our entry file where it will be actually run on application start-up. And we synchronize the successful database connection to server listening on _Tcp port_.

Open/create the entry file to our application. This file should be `index.js` located at `server/src/`([refer](#directory-structure "section: directory structure")). Remember that this is the file referenced by the `"main"` property in our `package.json` file.

Add the following code inside this file:

```javascript
require("dotenv").config();
const express = require("express");

const dbConnection = require("./dbConn/mongoose");

/* 
   1. INITIALIZE EXPRESS APPLICATION 🏁
 */
const app = express();
const PORT = process.env.PORT || 8000;

/* 
   2. APPLICATION MIDDLEWARES AND CUSTOMIZATIONS 🪛
 */

/* 
   3. APPLICATION ROUTES 🛣️
 */
// Test route
app.get("/", function (req, res) {
	res.send("Hello Welcome to API🙃 !!");
});

/* 
   4. APPLICATION ERROR HANDLING 🚔
 */

/* 
   5. APPLICATION BOOT UP 🖥️
 */
app.on("ready", () => {
	app.listen(PORT, () => {
		console.log(`App running on port ${PORT}`);
	});
});
dbConnection.then(() => {
	console.log("---Database is connected !!---");
	app.emit("ready");
});
```

_**Note:** The file `index.js` has commented sections that are numbered. Each section will have its logic added in other parts of the series_.

In the snippet above, we have imported `dotenv` at the _very top_ to load variables from `.env` file into the _environment of the Operating System_ as early as possible.

On the **5th section**, we connect to the database and once successfully connected, we emit `app`'s `"ready"` event that will inturn launch our application listening on a _Tcp `PORT`_.<br />
Therefore our Nodejs Server will start listening for requests after we are certain a successful database connection has been established.

We can test to see our app running on `PORT`. Open terminal at the `server` root and run:

```bash
npm run dev
```

If successful, you should see a terminal output like this:

   <figure>
   <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7276j4bt9wl4c0me8qdx.png" alt="Terminal npm run command" />
   <figcaption>
   Output of <code>npm run dev</code> command
   </figcaption>
   </figure>

Open your browser and browse to `http://localhost:8000/` to send first request to server. _And...🫰_, you should see TEST ROUTE has responded:

   <figure>
   <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/43uxekzzpuogt3lsay2s.png" alt="Test route response" />
   <figcaption>
   TEST ROUTE response
   </figcaption>
   </figure>

So we can draw a conclusion that we connected to our database and server then started listening on _TCP port_. You can mess with the MongoDB connection string to see how err influences what happens next.

## Conclusion

Well that is how you can perform database integration in NodeJs application. Mongoose is a _promise-based Object Document Mapper(ODM)_. It let's us connect and run database operation on MongoDB using an object-oriented paradigm. We have approached database integration in a maintainable fashion. You can change to a different database flavour using another _promise-based ORM/ODM_, while having little to refactor on the entry file. Also the logic that connects to a database is placed in a _separate module_ that contains everything necessary to execute one functionality i.e connecting to a database.

MongoDB has _"almost"_ zero data validation. A database is mostly useful if it has some data validation, at least when we want to work with structured data. Using an ODM like mongoose will help to validate data before we put it in the DB e.g enforcing column types. MySQL is quite a legendary with a legion of in-built data validation. Nonetheless generally ORM/ODM help working with database a bit easier with its _abstraction layer_. Hence simplifies development.

So this is how we connect to database in this series. Check out the next part where we learn how to win at CORS battleground. Yes, how to fix that irritating CORS error.

You wanna say something? Light up💡 the comment section 🕳🤾.

---

_Thanks for reading. Let's connect [@twitter](https://twitter.com/intent/user?screen_name=smitterhane). You may want to be part of the ideas I share._