Skip to content
Node.js Database — MySQL & MongoDB Integration Guide

Node.js Database — MySQL & MongoDB Integration Guide

DodaTech Updated Jun 6, 2026 7 min read

Almost every real-world Node.js application needs a database — Node.js works seamlessly with both SQL (MySQL, PostgreSQL) and NoSQL (MongoDB) databases using mature, well-maintained drivers.

What You’ll Learn

By the end of this tutorial, you’ll connect to MySQL and MongoDB, perform CRUD operations, use connection pooling, design Mongoose schemas with validation, and follow database best practices.

Why Database Integration Matters

Databases store everything that matters. Durga Antivirus Pro uses MySQL for structured threat databases and MongoDB for flexible malware signature storage. Doda Browser uses MongoDB for user settings and sync data. DodaZIP uses MySQL for user accounts and billing. Choosing the right database and integrating it correctly is fundamental to building data-driven applications.

Database Learning Path

    flowchart LR
  A[Express.js] --> B[Database]
  B --> C[Advanced Node.js]
  B --> D{You Are Here}
  style D fill:#f90,color:#fff
  
Prerequisites: https://tutorials.dodatech.com/backend/nodejs/express/ or Node.js basics. Understanding SQL fundamentals helps. Familiarity with JavaScript objects/arrays is needed for MongoDB.

MySQL with mysql2 — Structured, Relational Data

Setup & Connection Pool

const mysql = require("mysql2/promise");

const pool = mysql.createPool({
  host: process.env.DB_HOST || "localhost",
  user: process.env.DB_USER || "root",
  password: process.env.DB_PASSWORD || "",
  database: process.env.DB_NAME || "myapp",
  waitForConnections: true,
  connectionLimit: 10,
  queueLimit: 0
});

Always use a connection pool in production. Creating a new connection for every request is slow and exhausts database resources. The pool maintains a reusable set of connections.

CRUD Operations

// CREATE
const [result] = await pool.execute(
  "INSERT INTO users (name, email) VALUES (?, ?)",
  ["Alice", "alice@example.com"]
);
console.log("Inserted ID:", result.insertId);

// READ
const [rows] = await pool.execute(
  "SELECT * FROM users WHERE id = ?",
  [userId]
);

// UPDATE
await pool.execute(
  "UPDATE users SET name = ? WHERE id = ?",
  ["Alice Smith", userId]
);

// DELETE
await pool.execute("DELETE FROM users WHERE id = ?", [userId]);

Parameterized queries (the ? placeholders) prevent SQL injection. Never concatenate user input into SQL strings.

Transactions

const conn = await pool.getConnection();
try {
  await conn.beginTransaction();
  await conn.execute("UPDATE accounts SET balance = balance - 100 WHERE id = ?", [1]);
  await conn.execute("UPDATE accounts SET balance = balance + 100 WHERE id = ?", [2]);
  await conn.commit();
} catch (err) {
  await conn.rollback();
  throw err;
} finally {
  conn.release();
}

Transactions ensure data integrity — either all operations succeed or none do.

MongoDB with Mongoose — Flexible, Document-Based

Schema & Model

const mongoose = require("mongoose");

mongoose.connect(process.env.MONGO_URI || "mongodb://localhost:27017/myapp");

const userSchema = new mongoose.Schema({
  name: { type: String, required: true, trim: true },
  email: { type: String, required: true, unique: true, lowercase: true },
  age: { type: Number, min: 0, max: 150 },
  role: { type: String, enum: ["user", "admin"], default: "user" },
  address: { street: String, city: String, zip: String },
  createdAt: { type: Date, default: Date.now }
});

const User = mongoose.model("User", userSchema);

Mongoose provides schema validation, type casting, and middleware — it’s the most popular way to work with MongoDB in Node.js.

CRUD with Mongoose

// CREATE
const user = await User.create({
  name: "Alice", email: "alice@example.com", age: 30
});

// READ
const user = await User.findById(id);
const users = await User.find({ age: { $gte: 18 } }).sort({ name: 1 });

// UPDATE
await User.findByIdAndUpdate(id, { name: "Alice Updated" }, { new: true });

// DELETE
await User.findByIdAndDelete(id);

Connection Best Practices

// db.js — centralized database module
let pool;
async function initMySQL() {
  pool = mysql.createPool({
    host: process.env.DB_HOST,
    user: process.env.DB_USER,
    password: process.env.DB_PASSWORD,
    database: process.env.DB_NAME,
    connectionLimit: 10
  });
  return pool;
}
function getPool() {
  if (!pool) throw new Error("Database not initialized");
  return pool;
}
module.exports = { initMySQL, getPool };

Common Mistakes

1. No connection pooling — Creating a new connection per request exhausts database resources. Always use a pool.

2. SQL injection via string interpolation — Never do `SELECT * FROM users WHERE name = '${userInput}'`. Use parameterized queries.

3. Plain text passwords — Hash passwords with bcrypt before storing. Never log or return password fields.

4. Forgetting to close connections — Always release connections in a finally block.

5. Over-fetching in MongoDB — Use .project() or .select() to return only needed fields, not entire documents.

Common Patterns

Repository Pattern for Database Access

Instead of scattering queries across controllers, centralize them in a repository layer. This makes testing and swapping databases easier:

// userRepository.js
const pool = require("./db");

const UserRepository = {
  async findById(id) {
    const [rows] = await pool.execute("SELECT * FROM users WHERE id = ?", [id]);
    return rows[0];
  },

  async findByEmail(email) {
    const [rows] = await pool.execute("SELECT * FROM users WHERE email = ?", [email]);
    return rows[0];
  },

  async create({ name, email, password }) {
    const [result] = await pool.execute(
      "INSERT INTO users (name, email, password) VALUES (?, ?, ?)",
      [name, email, password]
    );
    return result.insertId;
  }
};

module.exports = UserRepository;

Expected output when calling UserRepository.findByEmail("alice@example.com"):

{ id: 1, name: "Alice", email: "alice@example.com", password: "$2b$12$..." }

Why it matters: Controllers become thin — they call UserRepository methods instead of writing raw SQL. When you migrate from MySQL to PostgreSQL, only the repository changes. DodaZIP uses this pattern for user accounts, billing, and license management.

Mongoose Virtual Fields

Virtual fields compute values from stored data without persisting them to the database:

const userSchema = new mongoose.Schema({
  firstName: String,
  lastName: String,
  passwordHash: String
});

userSchema.virtual("fullName").get(function () {
  return `${this.firstName} ${this.lastName}`;
});

// Remove sensitive fields from JSON output
userSchema.set("toJSON", {
  transform: (doc, ret) => {
    delete ret.passwordHash;
    delete ret.__v;
    return ret;
  }
});

Why: The client gets fullName without storing computed data, and passwordHash is never accidentally exposed in API responses.

Read/Write Splitting (MySQL)

High-traffic apps often separate read and write operations to different database replicas:

const readPool = mysql.createPool({ host: "read-replica.example.com", ... });
const writePool = mysql.createPool({ host: "primary.example.com", ... });

// Writes go to primary
const [result] = await writePool.execute("INSERT INTO logs ...", [...data]);

// Reads go to replica
const [rows] = await readPool.execute("SELECT * FROM logs ORDER BY created_at DESC LIMIT 50");

Real-world use: Durga Antivirus Pro routes threat signature queries (read-heavy) to replicas and new threat submissions (write-heavy) to the primary. This keeps the main database responsive under millions of daily queries.

Practice Questions

1. What’s connection pooling?

A cache of database connections reused across requests, avoiding the overhead of establishing a new connection each time.

2. How do you prevent SQL injection in mysql2?

Use parameterized queries with ? placeholders: pool.execute("SELECT * FROM users WHERE id = ?", [id]).

3. Mongoose vs native MongoDB driver — when to use which?

Mongoose for schema validation, middleware, and higher-level API. Native driver for lighter weight and more control.

4. What is an ObjectId in MongoDB?

A 12-byte identifier used as the default _id for documents — contains timestamp, machine ID, process ID, and counter.

5. Challenge: Build a User CRUD REST API with Express + Mongoose.

const express = require("express");
const mongoose = require("mongoose");
const app = express();
app.use(express.json());

mongoose.connect("mongodb://localhost:27017/userdb");

const userSchema = new mongoose.Schema({
  name: { type: String, required: true },
  email: { type: String, required: true, unique: true }
});
const User = mongoose.model("User", userSchema);

app.post("/api/users", async (req, res, next) => {
  try { const user = await User.create(req.body); res.status(201).json(user); }
  catch (err) { next(err); }
});
app.get("/api/users", async (req, res, next) => {
  try { const users = await User.find().sort({ name: 1 }); res.json(users); }
  catch (err) { next(err); }
});

app.listen(3000);

FAQ

Should I use MySQL or MongoDB?
MySQL for structured data with relationships (e-commerce, finance). MongoDB for flexible schemas, nested data, and rapid prototyping.
How do I handle database migrations?
For MySQL: knex.js or db-migrate. For MongoDB: migrate-mongo or handle schema evolution in application code.
What’s the best way to store passwords?
Use bcrypt to hash passwords with cost factor 12. Never store plain text or use MD5/SHA1.

Try It Yourself

Create a User CRUD server with Express and Mongoose using the challenge code above. Test with curl or Postman.

What’s Next

LessonDescription
https://tutorials.dodatech.com/backend/nodejs/nodejs-advanced/Security, testing, deployment
https://tutorials.dodatech.com/backend/nodejs/express/Express.js web framework
https://tutorials.dodatech.com/backend/php/php-forms/PHP form processing
MySQLSQL fundamentals
MongoDBNoSQL deep dive

What’s Next

Congratulations on completing this Nodejs Database tutorial! Here’s where to go from here:

  • Practice daily — Consistency is more important than long study sessions
  • Build a project — Apply what you learned by building something real
  • Explore related topics — Check out other tutorials in the same category
  • Join the community — Discuss with other learners and share your progress

Remember: every expert was once a beginner. Keep coding!

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro