Efficiently Connecting to the Database Before Server Startup: Why It's a Good Practice?


Each full stack application uses a 3 tier architecture. It includes your client(frontend), server & database(backend). For easy explanation & understanding I will be using a full stack web development example but don't worry the fundamentals are same.
So our client is React.js, server is Node.js with Express.js & database is MongoDB. Client is who makes API call for resources & server is who listens these API calls responds correspondingly with performing other actions like saving data's to database. In order to access data from server, first server needs to connected to database properly.
A typical node.js server starts running at some specified port & then tries to connect with database. Suppose for some reason (may be a network issue) our server could not connect to the database & started running. Now when an API call is made to server, it might not respond correctly & may be crashed down.
Would not it be great if server only starts when it connected to database successfully. Now you might ask what if database connection failed then our server might not start. Yeah that's correct but here we will be implement a better approach to counter this issue.
Try connecting to database 3(You can choose your own) times repeatedly until you connected successfully.
If not connected after trying 3 times then notify admin/developer that there is a problem connecting to database.
The code goes something like this.
require("dotenv").config();
const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");
const app = express();
const connectToDatabase = require("./database/database");
const { serverDownEmail } = require("./helper/sendMail");
// ---------- MIDDLEWARE ------------
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cors());
app.use(express.json());
// ---------- DATA BASE CONNECTION --------------
(async () => {
try {
// either data base will connect or not connect. If not connected then retry 10 times if exceeded then email Admin
let dbConnected = false;
let retryDBConnection = 1;
while (dbConnected === false && retryDBConnection <= 3) {
dbConnected = await connectToDatabase(); // here database is connection takes place
if (dbConnected === false) {
retryDBConnection++;
}
}
if (dbConnected) {
// start the server only after database connection is ready
const port = process.env.PORT || 5000;
return app.listen(port, () => {
console.log(`Server started in the port ${port}. :) Happy coding`);
});
} else {
// -----------
const mode = process.env.MODE_ || "production";
const currentAdminEmail = process.env.ADMIN_NOTIFY_EMAIL;
if (mode === "production") {
// Email Admin !! that SERVER IS DOWN !! :: When mode is production only
const isSent = await serverDownEmail(
currentAdminEmail,
`Database connection failed with error: ${error}`
);
}
}
} catch (error) {
console.error("Unable to connect database ", error);
}
})();
// --------- DATA BASE CONNECTION -----------
const homeResponseData = {
response: "Welcome to IGIT MCA server.",
};
// -------- ALL ROUTES ----------------
app.use("/api/user/", require("./routes/userRoutes"));
app.use("/api/batch/", require("./routes/batchRoutes"));
app.use("/api/coordinators/", require("./routes/coordinators"));
app.use("/api/accounts", require("./routes/adminRoutes")); // fetches all users account for admin page
app.use("/api/user/editProfile", require("./routes/userProfileEdit")); // to edit users profile
app.use("/api/post", require("./routes/postRoute")); // to create & delete post
// ---- HOME ROUTE -----
app.get("/", (req, res) => {
res.json(homeResponseData);
});
Let me explain you all the code.
- Importing packages & functions.
require("dotenv").config();
const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");
const app = express();
const connectToDatabase = require("./database/database");
const { serverDownEmail } = require("./helper/sendMail");
Here we are importing all the necessary packages & functions to run server & start database.
- Using global middleware
// ---------- MIDDLEWARE ------------
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cors());
app.use(express.json());
Here we are using global middleware to handle CORS policy, pass JSON data.
- Connecting to database
// ---------- DATA BASE CONNECTION --------------
// All code
// ---------- DATA BASE CONNECTION --------------
Here actually the real code works. First we are putting all inside a async
function because database connection takes time so we want wait. Here we will be trying to connect to database 3 times.
let dbConnected = false;
let retryDBConnection = 1;
while (dbConnected === false && retryDBConnection <= 3) {
dbConnected = await connectToDatabase(); // here database is connection takes place
if (dbConnected === false) {
retryDBConnection++;
}
}
Now our connectToDatabase()
function looks something like below
require("dotenv").config();
const mongoose = require("mongoose");
const createDefaultAdmin = require("./createDefaultAdminUser")
const dataBaseUrl = process.env.MONGO_DB_URL;
const connectToDatabase = async ()=>{
try {
await mongoose.connect(dataBaseUrl,{
useNewUrlParser: true,
useUnifiedTopology: true,
// bufferCommands: true, bufferTimeoutMS: 30000 ,
})
console.log("connected to database");
// this function of creating an admin user only runs when database connected successfully
createDefaultAdmin();
return true;
} catch (error) {
console.log("Unable to connect database ", error);
return false;
}
}
module.exports = connectToDatabase;
So after trying to connect database we will check if the database connection is successful or not.
Successful : We will start the server
// start the server only after database connection is ready const port = process.env.PORT || 5000; return app.listen(port, () => { console.log(`Server started in the port ${port}. :) Happy coding`); });
- Failure : Notify admin/developer with costume email message if not in the development environment
// -----------
const mode = process.env.MODE_ || "production";
const currentAdminEmail = process.env.ADMIN_NOTIFY_EMAIL;
if (mode === "production") {
// Email Admin !! that SERVER IS DOWN !! :: When mode is production only
const isSent = await serverDownEmail(
currentAdminEmail,
`Database connection failed with error: ${error}`
);
}
After successful connection we are defining our API end points
const homeResponseData = {
response: "Welcome to IGIT MCA server.",
};
// -------- ALL ROUTES ----------------
app.use("/api/user/", require("./routes/userRoutes"));
app.use("/api/batch/", require("./routes/batchRoutes"));
app.use("/api/coordinators/", require("./routes/coordinators"));
app.use("/api/accounts", require("./routes/adminRoutes")); // fetches all users account for admin page
app.use("/api/user/editProfile", require("./routes/userProfileEdit")); // to edit users profile
app.use("/api/post", require("./routes/postRoute")); // to create & delete post
// ---- HOME ROUTE -----
app.get("/", (req, res) => {
res.json(homeResponseData);
});
This way I did my server start only after successful database connection.
Now our setup is ready & our frontend can make any API call while fetching data from database.
Note: If you are a senior developer or have more experience dealing in that feel free to correct me if I am wrong somewhere.
Subscribe to my newsletter
Read articles from Satyanarayan Dalei directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Satyanarayan Dalei
Satyanarayan Dalei
Hi, I'm Satyanarayan Dalei, a mid-level Full-stack web developer from India. Currently pursuing a master's in Computer Application, I've been coding since 2020. My expertise lies in the MERN stack, and I am well-versed in the software deployment life cycle, covering both production and development environments.