Script Valley
REST API Development: Complete Course from Beginner to Production
Building REST APIs with Express.jsLesson 3.2

Connecting to a Database and Performing CRUD Operations

database connection, MongoDB, Mongoose, environment variables, dotenv, CRUD with database, schema, model

Connecting to a Database and Performing CRUD Operations

A REST API without a database is merely a calculator. Persistent data storage is what makes APIs useful in real applications. This lesson covers connecting Express to MongoDB using Mongoose, defining data models, and implementing all four CRUD operations against real data.

DiagramExpress + Mongoose Data Flow

IMAGE PROMPT (replace this block with your generated image):

Flat layered architecture diagram on white background. Title: Express.js and Mongoose Architecture. Four horizontal layers stacked vertically with connecting arrows between them. Layer 1 (top): HTTP Request — browser/client icon, arrow down. Layer 2: Express Router box (light #3A5EFF fill #e8ecff) — shows GET /users/:id inside. Arrow down. Layer 3: Mongoose Model box (light blue fill) — shows User.findById(id) inside. Bidirectional arrow down. Layer 4 (bottom): MongoDB — database cylinder icon in dark blue. Return arrow going up through all layers back to client shows JSON response. Each layer has its technology name as a small badge. Down arrows are solid #3A5EFF. Labels like dotenv for .env connection string shown as a small note card attached to Layer 3. White background, clean and minimal.

Environment Variables

Never hardcode connection strings or credentials. Use a .env file and the dotenv package:

npm install dotenv mongoose
MONGODB_URI=mongodb://localhost:27017/userapi
PORT=3000

Load them at the top of index.js: require('dotenv').config();

Connecting to MongoDB

const mongoose = require('mongoose');

mongoose.connect(process.env.MONGODB_URI)
  .then(() => console.log('MongoDB connected'))
  .catch((err) => {
    console.error('MongoDB connection error:', err);
    process.exit(1);
  });

Defining a Mongoose Schema and Model

const userSchema = new mongoose.Schema({
  name: { type: String, required: true, trim: true },
  email: { type: String, required: true, unique: true, lowercase: true },
  role: { type: String, enum: ['user', 'admin'], default: 'user' },
  isActive: { type: Boolean, default: true }
}, { timestamps: true });

const User = mongoose.model('User', userSchema);
module.exports = User;

The timestamps: true option automatically adds createdAt and updatedAt fields to every document.

Implementing CRUD Controllers

const getAllUsers = async (req, res) => {
  try {
    const users = await User.find({ isActive: true }).select('-__v');
    res.status(200).json({ success: true, data: users, count: users.length });
  } catch (error) {
    res.status(500).json({ success: false, error: { message: error.message } });
  }
};

const createUser = async (req, res) => {
  try {
    const user = await User.create(req.body);
    res.status(201).json({ success: true, data: user });
  } catch (error) {
    if (error.code === 11000) {
      return res.status(409).json({
        success: false,
        error: { code: 'DUPLICATE_EMAIL', message: 'Email already exists' }
      });
    }
    res.status(500).json({ success: false, error: { message: error.message } });
  }
};

Up next

Middleware in Express: Validation, Logging, and Error Handling

Sign in to track progress