Welcome to part 2. Follow along this series to develop a feature-packed 3-tier Authentication System outlined in successive parts.
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✩. Drop a star💫️ if you found it useful.
Table of Contents
- Directory Structure
- Create
package.json
- Create
.env
file - Install dependencies
- Set up database connection
- Run database connection in application
- Conclusion
Directory structure
📂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
.
This command will generate a package.json
file inside our server
folder. Then edit the package.json
file to look like this:
{
"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:
// ...
"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) 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 registry that we will need in this project.
We will use the following dependencies:
- 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 - An object modelling tool for MongoDB to allow us to enforce a specific mongoDB schema from our application.
- Dotenv - To load environment variables declared in
.env
file. - 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:
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:
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 account or login if you already have one. Then follow this guide to obtain your connection string.
Once you have your connection string, open the .env
file and set the MONGODB_URI
like shown:
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:
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.
Open/create index.js
file located at server/src/dbConn/mongoose/
(refer). Paste the following code in it:
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). Remember that this is the file referenced by the "main"
property in our package.json
file.
Add the following code inside this file:
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
.
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:
npm run dev
If successful, you should see a terminal output like this:
Open your browser and browse to http://localhost:8000/
to send first request to server. And...🫰, you should see TEST ROUTE has responded:
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. You may want to be part of the ideas I share.
Top comments (4)
Ah! this article was helpful. I have always connected to the Database after I start my node server, I see many people do it too. There are many benefits of the above approach no doubt.
I am glad you found it useful
Wonderful series! I've had little experience with this whole authentication process and I'm in need to review this again. Thank you!
Welcome, you can always refer to this article again