3 BACKEND API

Database Design (MongoDB)

💾 Database Design: SQL vs NoSQL

If Express is the brain routing incoming traffic, the Database is the long-term memory. Without a database, every time your Node.js server restarts overnight, you lose 100% of your user data because RAM is wiped clean.

Choosing between a Relational (SQL) and Non-Relational (NoSQL) database is the absolute most critical architectural decision a Backend Engineer makes on day one.


1️⃣ SQL: The Relational Architecture

SQL (Structured Query Language) databases like PostgreSQL, MySQL, and SQLite organize data strictly into mathematically defined Tables with Rows and Columns (like a highly advanced Excel Spreadsheet).

The Schema

SQL requires a strict Schema (Blueprint). If the users table demands an age (INTEGER), you physically cannot push a user into the database without defining their age as an integer. It will throw a fatal error.

-- Creating the strict blueprint
CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    username VARCHAR(50) UNIQUE NOT NULL,
    email VARCHAR(255) UNIQUE NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- Inserting Data
INSERT INTO users (username, email) VALUES ('MweroA', 'mwero@example.com');

Relationships and Constraints

SQL databases are "Relational". A post doesn't contain a user object; it just holds a user_id pointing to the users table.

CREATE TABLE posts (
    id SERIAL PRIMARY KEY,
    title VARCHAR(255) NOT NULL,
    -- The Foreign Key logically links this row to a row in the users table
    author_id INTEGER REFERENCES users(id) ON DELETE CASCADE
);

SQL databases enforce ACID Compliance (Atomicity, Consistency, Isolation, Durability). Meaning: If you transfer $50 from Alice to Bob, the database mathematically guarantees that if Node.js crashes in the middle of the second step, the entire transaction rolls back. Alice will never lose $50 without Bob receiving it.

When to use SQL: E-commerce, Banking, CMS platforms, anywhere data integrity is paramount.


2️⃣ NoSQL: The Document Architecture

NoSQL databases (like MongoDB and Firebase) threw away the strict SQL tables and replaced them with massive JSON documents.

Schema-less Flexibility

NoSQL does not require a blueprint. You can push { name: "Mwero" } into the users collection, and 5 minutes later push { dogName: "Rex", breed: "Husky" } into the exact same collection. The database accepts both happily.

// A MongoDB Document
const userDoc = {
    _id: ObjectId("5099803df3f4948bd2f98391"),
    username: "MweroA",
    friends: ["Alice", "Bob"], // Arrays are supported natively!
    address: {                 // Deeply nested objects are supported natively!
        city: "Nairobi",
        zip: 10100
    }
};

Embedding vs Referencing

In SQL, you must use complex JOIN math to stitch the users and posts tables together when querying. In NoSQL, because documents support arrays and objects, you can simply embed the data directly.

// Embedded Data (One massive disk read, incredibly fast)
const blogPost = {
    title: "Learning Node.js",
    author: { name: "MweroA", email: "mwero@example.com" }, // Embedded
    comments: [                                             // Array of Comments
        { user: "Alice", text: "Great post!" },
        { user: "Bob", text: "Thanks!" }
    ]
}

When to use NoSQL: IoT sensor data, massive catalogues with changing shapes, rapid prototyping, and real-time chat apps.


3️⃣ ORMs: bridging Node.js to the Database

Writing raw SQL strings inside your Node.js code (db.query('SELECT * FROM users')) is dangerous and tedious. It exposes you to SQL Injection attacks if you aren't perfectly careful.

Modern backend engineers use an ORM (Object-Relational Mapper) like Prisma or Sequelize.

An ORM translates standard JavaScript/TypeScript code directly into secure, optimized SQL under the hood.

Example using Prisma (The modern TS standard):

// 1. You write regular JS syntax
const newUser = await prisma.user.create({
    data: {
        username: "MweroA",
        email: "mwero@test.com"
    }
});

// 2. Prisma automatically parses this into:
// INSERT INTO users (username, email) VALUES ('MweroA', 'mwero@test.com')

// 3. Fetching Data with relationships
const allPosts = await prisma.post.findMany({
    where: { published: true },
    include: { author: true } // Prisma handles the complex SQL JOIN automatically!
});

4️⃣ Connecting Express to a Database

Every database interaction requires a network request from the Express server to the DB Server (often on a different port or AWS machine). Therefore, database calls are incredibly slow relative to CPU math. They must be handled Asynchronously.

import express from 'express';
import { PrismaClient } from '@prisma/client';

const app = express();
const prisma = new PrismaClient(); // Connect to the DB

app.get('/api/users', async (req, res) => {
    try {
        // Asynchronously pause execution until the Database replies
        const users = await prisma.user.findMany(); 
        
        // Return the JSON data to Postman or the React App
        res.status(200).json(users);
    } catch (error) {
        // If the database is offline or the query failed
        console.error(error);
        res.status(500).json({ error: "Internal Server Error" });
    }
});

💡 Summary Lexicon

FeatureSQL (PostgreSQL, MySQL)NoSQL (MongoDB, Firebase)
Data ShapeTables, Rows, ColumnsCollections, JSON Documents
SchemaRigid and mathematically strict.Flexible, dynamic, schema-less.
RelationshipsJOIN commands tying Tables together.Embedding objects directly inside other objects.
IntegrityACID Compliant (Banking grade).Eventual Consistency (High speed grade).
ScalingVertically (Buying a stronger $50k server CPU).Horizontally (Adding 10 cheap $50 servers in parallel).

Knowledge Check

Complete this quick quiz to verify your understanding and unlock the next module.