Node.js Intermediate

Express.js Framework: Building Web Applications with Ease

CodingerWeb
CodingerWeb
23 views 70 min read

Introduction to Express.js

Express.js is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications. It simplifies the process of building web servers and APIs.

Installing Express.js

# Initialize a new project
npm init -y

# Install Express.js
npm install express

# Install development dependencies
npm install --save-dev nodemon

Your First Express Application

// app.js
const express = require("express");
const app = express();
const PORT = 3000;

// Basic route
app.get("/", (req, res) => {
    res.send("Hello, Express.js!");
});

// JSON response
app.get("/api/info", (req, res) => {
    res.json({
        message: "Welcome to Express.js API",
        version: "1.0.0",
        timestamp: new Date().toISOString()
    });
});

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

Middleware in Express

Middleware functions execute during the request-response cycle:

const express = require("express");
const app = express();

// Built-in middleware for parsing JSON
app.use(express.json());

// Built-in middleware for parsing URL-encoded data
app.use(express.urlencoded({ extended: true }));

// Custom logging middleware
app.use((req, res, next) => {
    console.log(`${new Date().toISOString()} - ${req.method} ${req.url}`);
    next(); // Pass control to next middleware
});

// Authentication middleware
const authenticate = (req, res, next) => {
    const token = req.headers.authorization;
    
    if (!token) {
        return res.status(401).json({ error: "No token provided" });
    }
    
    // Simulate token validation
    if (token === "Bearer valid-token") {
        req.user = { id: 1, name: "John Doe" };
        next();
    } else {
        res.status(401).json({ error: "Invalid token" });
    }
};

// Protected route
app.get("/api/profile", authenticate, (req, res) => {
    res.json({
        message: "Profile data",
        user: req.user
    });
});

app.listen(3000);

Routing in Express

const express = require("express");
const app = express();

app.use(express.json());

// Route parameters
app.get("/users/:id", (req, res) => {
    const userId = req.params.id;
    res.json({
        message: `User profile for ID: ${userId}`,
        userId: userId
    });
});

// Multiple parameters
app.get("/users/:userId/posts/:postId", (req, res) => {
    const { userId, postId } = req.params;
    res.json({
        message: `Post ${postId} by user ${userId}`,
        userId,
        postId
    });
});

// Query parameters
app.get("/search", (req, res) => {
    const { q, limit = 10, page = 1 } = req.query;
    res.json({
        query: q,
        limit: parseInt(limit),
        page: parseInt(page),
        results: []
    });
});

// Route with optional parameters
app.get("/products/:category/:id?", (req, res) => {
    const { category, id } = req.params;
    
    if (id) {
        res.json({ message: `Product ${id} in ${category}` });
    } else {
        res.json({ message: `All products in ${category}` });
    }
});

app.listen(3000);

Building a RESTful API

const express = require("express");
const app = express();

app.use(express.json());

// In-memory data store
let books = [
    { id: 1, title: "Node.js Guide", author: "John Doe", year: 2023 },
    { id: 2, title: "Express.js Handbook", author: "Jane Smith", year: 2023 }
];

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 id = parseInt(req.params.id);
    const book = books.find(b => b.id === id);
    
    if (book) {
        res.json({ book });
    } else {
        res.status(404).json({ error: "Book not found" });
    }
});

// POST new book
app.post("/api/books", (req, res) => {
    const { title, author, year } = req.body;
    
    // Validation
    if (!title || !author || !year) {
        return res.status(400).json({
            error: "Title, author, and year are required"
        });
    }
    
    const newBook = {
        id: nextId++,
        title,
        author,
        year: parseInt(year)
    };
    
    books.push(newBook);
    res.status(201).json({ book: newBook });
});

// PUT update book
app.put("/api/books/:id", (req, res) => {
    const id = parseInt(req.params.id);
    const bookIndex = books.findIndex(b => b.id === id);
    
    if (bookIndex === -1) {
        return res.status(404).json({ error: "Book not found" });
    }
    
    const { title, author, year } = req.body;
    
    if (title) books[bookIndex].title = title;
    if (author) books[bookIndex].author = author;
    if (year) books[bookIndex].year = parseInt(year);
    
    res.json({ book: books[bookIndex] });
});

// DELETE book
app.delete("/api/books/:id", (req, res) => {
    const id = parseInt(req.params.id);
    const bookIndex = books.findIndex(b => b.id === id);
    
    if (bookIndex === -1) {
        return res.status(404).json({ error: "Book not found" });
    }
    
    const deletedBook = books.splice(bookIndex, 1)[0];
    res.json({ message: "Book deleted", book: deletedBook });
});

app.listen(3000, () => {
    console.log("Books API running on http://localhost:3000");
});

Error Handling

const express = require("express");
const app = express();

app.use(express.json());

// Async error handling wrapper
const asyncHandler = (fn) => (req, res, next) => {
    Promise.resolve(fn(req, res, next)).catch(next);
};

// Route with potential error
app.get("/api/error-demo", asyncHandler(async (req, res) => {
    // Simulate async operation that might fail
    const random = Math.random();
    
    if (random < 0.5) {
        throw new Error("Something went wrong!");
    }
    
    res.json({ message: "Success!", random });
}));

// 404 handler (should be after all routes)
app.use("*", (req, res) => {
    res.status(404).json({
        error: "Route not found",
        path: req.originalUrl
    });
});

// Global error handler (should be last)
app.use((err, req, res, next) => {
    console.error("Error:", err.message);
    
    res.status(err.status || 500).json({
        error: err.message || "Internal server error",
        ...(process.env.NODE_ENV === "development" && { stack: err.stack })
    });
});

app.listen(3000);

Serving Static Files

const express = require("express");
const path = require("path");
const app = express();

// Serve static files from public directory
app.use(express.static("public"));

// Serve static files with custom path
app.use("/assets", express.static("assets"));

// API routes
app.get("/api/data", (req, res) => {
    res.json({ message: "API data" });
});

// Catch-all handler for SPA (Single Page Application)
app.get("*", (req, res) => {
    res.sendFile(path.join(__dirname, "public", "index.html"));
});

app.listen(3000);

Exercise

Create a task management API with Express.js that includes:

  1. CRUD operations for tasks
  2. User authentication middleware
  3. Input validation
  4. Error handling
  5. Static file serving for a frontend

What's Next?

In the next lesson, we'll learn to work with databases in Node.js, connecting to MongoDB and MySQL.