01How The Internet Works 02Your First Node.js Server 03HTTP Methods and JSON 04Express.js Framework 05Building REST APIs 06MongoDB Fundamentals 07Mongoose ODM 08Data Modeling 09Authentication Basics 10JWT Implementation

Settings

Theme
Sand
Cloud
Midnight
Forest
Sunset
Purple
Ocean
Crimson
Font
Merriweather
Inter
JetBrains
Space Grotesk
Fira Code
Playfair
Font Size
100%
Bookmark
No bookmark set
Day 4

Express.js Framework

Building servers with raw Node.js works, but it is verbose and repetitive. Express.js simplifies everything - routing, middleware, error handling, and more. It is the most popular Node.js framework for good reason.

Why Express.js?

Compare the raw Node.js approach to Express:

Raw Node.js - Verbose
const http = require('http');
const server = http.createServer((req, res) => {
    if (req.method === 'GET' && req.url === '/users') {
        res.statusCode = 200;
        res.setHeader('Content-Type', 'application/json');
        res.end(JSON.stringify({ users: [] }));
    }
});
server.listen(3000);
Express.js - Clean and Simple
const express = require('express');
const app = express();

app.get('/users', (req, res) => {
    res.json({ users: [] });
});

app.listen(3000);

Express Benefits

  • Cleaner Routing - app.get(), app.post() instead of if/else chains
  • Built-in JSON - res.json() automatically stringifies and sets headers
  • Middleware - Easily add functionality like logging, auth, parsing
  • Request Parsing - Easy access to params, query strings, body
  • Error Handling - Centralized error handling

Getting Started with Express

Terminal - Project Setup
# Create project folder
mkdir express-app
cd express-app

# Initialize npm project
npm init -y

# Install Express
npm install express

# Create main file
touch app.js
app.js - Basic Express Server
// Import Express
const express = require('express');

// Create Express application instance
const app = express();

// Define a route for GET requests to root URL
app.get('/', (req, res) => {
    res.send('Hello from Express!');
});

// Start server on port 3000
const PORT = 3000;
app.listen(PORT, () => {
    console.log(`Server running on http://localhost:${PORT}`);
});

Routing in Express

Express provides methods for each HTTP verb. The pattern is: app.METHOD(PATH, HANDLER)

Route Methods
// GET request to /
app.get('/', (req, res) => {
    res.send('Home page');
});

// POST request to /users
app.post('/users', (req, res) => {
    res.send('Create user');
});

// PUT request to /users/:id
app.put('/users/:id', (req, res) => {
    res.send(`Update user ${req.params.id}`);
});

// PATCH request to /users/:id
app.patch('/users/:id', (req, res) => {
    res.send(`Partial update user ${req.params.id}`);
});

// DELETE request to /users/:id
app.delete('/users/:id', (req, res) => {
    res.send(`Delete user ${req.params.id}`);
});

// Handle ALL HTTP methods
app.all('/secret', (req, res) => {
    res.send('Accessing secret area');
});

Route Parameters

Capture dynamic values from URLs using :paramName syntax:

Route Parameters
// Single parameter
// URL: /users/123  ->  req.params.id = "123"
app.get('/users/:id', (req, res) => {
    const userId = req.params.id;
    res.send(`User ID: ${userId}`);
});

// Multiple parameters
// URL: /users/123/posts/456
app.get('/users/:userId/posts/:postId', (req, res) => {
    const { userId, postId } = req.params;
    res.json({ userId, postId });
});

// Optional parameter (use regex or check for undefined)
app.get('/products/:category?', (req, res) => {
    if (req.params.category) {
        res.send(`Products in ${req.params.category}`);
    } else {
        res.send('All products');
    }
});

Query Strings

Access URL query parameters with req.query:

Query Parameters
// URL: /search?q=nodejs&limit=10&page=2
app.get('/search', (req, res) => {
    const { q, limit, page } = req.query;

    console.log(q);      // "nodejs"
    console.log(limit);  // "10" (always strings!)
    console.log(page);   // "2"

    // Convert to numbers if needed
    const limitNum = parseInt(limit) || 10;
    const pageNum = parseInt(page) || 1;

    res.json({
        query: q,
        limit: limitNum,
        page: pageNum
    });
});

Response Methods

Express provides many ways to send responses:

Response Methods
// Send plain text
res.send('Hello World');

// Send JSON (automatically sets Content-Type)
res.json({ message: 'Success', data: users });

// Send with specific status code
res.status(201).json({ id: 1, name: 'Created' });
res.status(404).json({ error: 'Not found' });
res.status(500).json({ error: 'Server error' });

// Send file
res.sendFile('/path/to/file.html');

// Download file
res.download('/path/to/file.pdf');

// Redirect to another URL
res.redirect('/new-url');
res.redirect(301, '/permanent-new-url');

// End response with no body
res.status(204).end();

// Set headers
res.set('X-Custom-Header', 'value');
res.set({
    'Content-Type': 'text/plain',
    'X-Another': 'header'
});

Middleware - The Heart of Express

Middleware functions have access to the request, response, and the next middleware function. They can execute code, modify req/res, end the request, or call next().

┌─────────┐     ┌──────────────┐     ┌──────────────┐     ┌───────────────┐     ┌──────────┐
│ Request │────►│ Middleware 1 │────►│ Middleware 2 │────►│ Route Handler │────►│ Response │
└─────────┘     └──────┬───────┘     └──────┬───────┘     └───────┬───────┘     └──────────┘
                       │                    │                     │
                    next()               next()              res.send()
Middleware Examples
const express = require('express');
const app = express();

// Custom logging middleware (runs on EVERY request)
app.use((req, res, next) => {
    console.log(`${new Date().toISOString()} ${req.method} ${req.url}`);
    next();  // MUST call next() to continue to next middleware
});

// Built-in middleware: Parse JSON bodies
app.use(express.json());

// Built-in middleware: Parse URL-encoded bodies (forms)
app.use(express.urlencoded({ extended: true }));

// Built-in middleware: Serve static files
app.use(express.static('public'));

// Middleware for specific path only
app.use('/api', (req, res, next) => {
    console.log('API request received');
    next();
});

// Your routes come AFTER middleware
app.get('/', (req, res) => {
    res.send('Home');
});

app.listen(3000);
Critical Point

Order matters! Middleware runs in the order it is defined. Always put express.json() BEFORE your routes, or req.body will be undefined. Forgetting to call next() will hang the request.

Complete Express CRUD API

app.js - Full CRUD API
const express = require('express');
const app = express();

// Middleware
app.use(express.json());

// In-memory database
let books = [
    { id: 1, title: 'Node.js Basics', author: 'John Doe' },
    { id: 2, title: 'Express Guide', author: 'Jane Smith' }
];
let nextId = 3;

// GET all books
app.get('/api/books', (req, res) => {
    res.json(books);
});

// GET single book
app.get('/api/books/:id', (req, res) => {
    const book = books.find(b => b.id === parseInt(req.params.id));

    if (!book) {
        return res.status(404).json({ error: 'Book not found' });
    }

    res.json(book);
});

// POST create book
app.post('/api/books', (req, res) => {
    const { title, author } = req.body;

    // Validation
    if (!title || !author) {
        return res.status(400).json({
            error: 'Title and author are required'
        });
    }

    const newBook = {
        id: nextId++,
        title,
        author
    };

    books.push(newBook);
    res.status(201).json(newBook);
});

// PUT update book (replace)
app.put('/api/books/:id', (req, res) => {
    const id = parseInt(req.params.id);
    const index = books.findIndex(b => b.id === id);

    if (index === -1) {
        return res.status(404).json({ error: 'Book not found' });
    }

    const { title, author } = req.body;

    if (!title || !author) {
        return res.status(400).json({
            error: 'Title and author are required'
        });
    }

    books[index] = { id, title, author };
    res.json(books[index]);
});

// PATCH update book (partial)
app.patch('/api/books/:id', (req, res) => {
    const id = parseInt(req.params.id);
    const book = books.find(b => b.id === id);

    if (!book) {
        return res.status(404).json({ error: 'Book not found' });
    }

    // Update only provided fields
    if (req.body.title) book.title = req.body.title;
    if (req.body.author) book.author = req.body.author;

    res.json(book);
});

// DELETE book
app.delete('/api/books/:id', (req, res) => {
    const id = parseInt(req.params.id);
    const index = books.findIndex(b => b.id === id);

    if (index === -1) {
        return res.status(404).json({ error: 'Book not found' });
    }

    books.splice(index, 1);
    res.status(204).end();
});

// 404 handler for unmatched routes
app.use((req, res) => {
    res.status(404).json({ error: 'Route not found' });
});

// Start server
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
    console.log(`Server running on http://localhost:${PORT}`);
});

Video Resources

Knowledge Check: Day 4

1. What does app.use(express.json()) do?

ASends JSON responses
BValidates JSON format
CParses JSON request bodies into req.body
DConverts responses to JSON

2. How do you access a URL parameter like /users/123?

Areq.query.id
Breq.params.id
Creq.body.id
Dreq.url.id

3. What happens if you forget to call next() in middleware?

AThe request hangs and never completes
BExpress automatically continues
CAn error is thrown
DThe middleware runs twice
Mini Project

Task Manager API

Build an Express API for managing tasks:

  • GET /api/tasks - List all tasks (support ?completed=true filter)
  • GET /api/tasks/:id - Get single task
  • POST /api/tasks - Create task (title required, completed defaults false)
  • PATCH /api/tasks/:id - Update task (toggle completed, change title)
  • DELETE /api/tasks/:id - Delete task
  • Add request logging middleware

Day 4 Complete!

You have learned Express.js - the most popular Node.js framework. You understand routing, middleware, request/response handling, and how to build a complete CRUD API with much cleaner code than raw Node.js.

Tomorrow, we will organize our code better by learning about project structure, routers, and controllers - essential patterns for building maintainable APIs.