Express.js Overview: From Node’s HTTP to a Full Framework

Learn why Express.js is preferred over the Node.js HTTP module and how it simplifies back-end development.

Handling HTTP requests is at the heart of every web application, whether you’re serving pages, processing form submissions, or fetching data. While Node.js provides a built-in http module, setting up a server from scratch requires a lot of boilerplate code to efficiently manage different requests.

This is where Express.js comes in. As a minimal yet powerful web framework for Node.js, Express simplifies request handling, reducing the code needed to build robust back-end applications.

A defining trait of Express is that it’s unopinionated, meaning it doesn’t enforce a specific project structure or require certain tools. Developers have full control over organizing their code and structuring their applications. This flexibility makes Express a great choice for everything from small prototypes to large-scale applications that require customization.

Before diving into Express, let’s explore why handling HTTP requests manually can be challenging.

The pain points of using the http module

The built-in http module in Node.js allows us to create a web server, but handling multiple request types requires explicitly checking the request’s URL and method before responding. We must also parse incoming data, handle errors, and structure reusable logic ourselves.

const http = require("http");

const server = http.createServer((req, res) => {
  if (req.url === "/" && req.method === "GET") {
    res.writeHead(200, { "Content-Type": "text/plain" });
    res.end("Welcome to our homepage!");
  } else if (req.url === "/about" && req.method === "GET") {
    res.writeHead(200, { "Content-Type": "text/plain" });
    res.end("About us page");
  } else {
    res.writeHead(404, { "Content-Type": "text/plain" });
    res.end("Page not found");
  }
});

server.listen(3000, () => {
  console.log("Server running on port 3000");
});
Creating a basic Node.js server

Why is this approach challenging?

  • Too much manual work: We have to manually check req.url and req.method for every request.

  • Code gets harder to manage: Adding more requests requires additional if conditions.

  • Manually setting headers: We must explicitly define response headers each time.

Clearly, we need a better solution. This is where Express.js helps us by automating these tasks.

How Express.js makes development easier

While Express.js builds on Node’s built-in http module, it provides a cleaner interface for handling HTTP requests and eliminates repetitive code. The example below behaves the same as the previous example, but is much more concise.

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

app.get("/", (req, res) => {
  res.send("Welcome to our homepage!");
});

app.get("/about", (req, res) => {
  res.send("About us page");
});

app.use((req, res) => {
  res.status(404).send("Page not found");
});

app.listen(3000, () => {
  console.log("Server running on port 3000");
});
Creating the same server in Express

What’s different?

  • Less code: No need to manually check req.url or req.method.

  • Cleaner structure: Requests are handled using simple app.get() calls.

  • Automatic response handling: res.send() sets headers for us.

  • Error handling is built-in: The app.use() method catches all undefined routes.

By using Express, we write less boilerplate code and improve code organization.

Setting up an Express server

Now that we understand why Express is useful, let’s set up a basic Express server.

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

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

app.listen(3000, () => {
  console.log("Server running on port 3000");
});
Setting up an Express server

Explanation:

  • Line 1: This imports the Express module using require("express").

  • Line 2: This calls express() to create an Express application instance, and stores it in app.

  • Line 4: This defines a request handler that responds to GET requests at /.

  • Line 5: This sends a response without needing to manually set headers.

  • Lines 8–10: This starts the server on port 3000.

With just a few lines of code, we now have a fully functional web server!

Key takeaways:

  • Express simplifies server-side development compared to the built-in HTTP module.

  • The request-response cycle is handled automatically by Express.

  • Express provides built-in request handling and response management.

  • With Express, we can build web servers quickly and efficiently.

Exercise: Creating an Express server

Task:

Create an Express server in server.js that listens on port 4000. Add a GET /welcome route that responds with "Welcome to Express!".

// Implement your solution here...
Exercise: Creating an Express server