When you first dive into backend development with Node.js, it doesn’t take long before you bump into Express.js. It’s practically the default framework for building web servers and APIs in JavaScript, and for good reason, it simplifies the otherwise clunky task of handling requests, responses, and routes.
In this article, we’ll take a thorough look at Express:
- what it is and why it exists,
- how to set up a simple project,
- how routing works in practice,
- and a few must-know tricks to get you comfortable.
This will lay the foundation for the next article in the series (Part 2), where we’ll unlock the real magic of Express: middleware.
Why Use Express.js Instead of Plain Node?
You might be thinking: “Wait, Node.js can already create servers, why do I need Express?”
That’s true, but here’s what a basic raw Node server looks like:
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("Home Page")
} else if (req.url === "/users" && req.method === "GET") {
res.writeHead(200, { "Content-Type": "text/plain" })
res.end("Users Page")
} else {
res.writeHead(404, { "Content-Type": "text/plain" })
res.end("Not Found")
}
})
server.listen(3000, () => {
console.log("Server running on http://localhost:3000")
})
It works, but it gets messy fast. You have to manually check URLs, methods, and headers.
Now look at the equivalent in Express:
const express = require("express")
const app = express()
app.get("/", (req, res) => {
res.send("Home Page")
})
app.get("/users", (req, res) => {
res.send("Users Page")
})
app.listen(3000, () => {
console.log("Server running on http://localhost:3000")
})
Much cleaner. Express handles the boilerplate for you so you can focus on logic.
Setting Up Express
- Create a project folder and initialize it:
mkdir express-intro
cd express-intro
npm init -y
- Install Express:
npm install express
- Create a file called
server.js
and add:
const express = require("express")
const app = express()
// A simple route
app.get("/", (req, res) => {
res.send("Hello from Express!")
})
// Start the server
app.listen(3000, () => {
console.log("Listening on http://localhost:3000")
})
- Run the server:
node server.js
Go to http://localhost:3000
and boom, you’ve got a web server.
How Routing Works in Express
Express gives you an intuitive way to define routes. A route is just a combination of an HTTP method and a path.
GET Requests
app.get("/about", (req, res) => {
res.send("About page")
})
Visiting http://localhost:3000/about
will trigger this function.
POST Requests
app.post("/submit", (req, res) => {
res.send("Form submitted")
})
This would respond when you submit a form with POST /submit
.
Dynamic Routes
You can use route parameters:
app.get("/users/:id", (req, res) => {
res.send(`User ID: ${req.params.id}`)
})
Visiting http://localhost:3000/users/42
would return User ID: 42
.
Query Parameters
Express makes query strings easy too:
app.get("/search", (req, res) => {
const term = req.query.q
res.send(`You searched for: ${term}`)
})
Visiting http://localhost:3000/search?q=node
responds with You searched for: node
.
Serving Static Files
What if you want to serve an HTML file or an image? Express has you covered:
app.use(express.static("public"))
Now any files in your public/
folder (like index.html
or style.css
) will be automatically served.
For example: http://localhost:3000/style.css
→ serves /public/style.css
.
Sending JSON Responses
Since APIs often need to send JSON, Express makes this trivial:
app.get("/api/users", (req, res) => {
res.json([
{ id: 1, name: "Alice" },
{ id: 2, name: "Bob" }
])
})
Visiting /api/users
will give you structured JSON, no extra setup required.
Handling 404 Errors
It’s a good practice to handle unknown routes:
app.use((req, res) => {
res.status(404).send("Sorry, page not found")
})
This will catch any route that wasn’t matched earlier.
Why Express Feels So Flexible
The magic of Express is its simplicity. It doesn’t force you into a strict structure. You can:
- Start with just a single file for a prototype.
- Gradually break routes into separate files as your app grows.
- Add middlewares for logging, authentication, validation, and more (coming in Part 2).
It’s like Lego blocks: small and simple by themselves, but powerful when combined.
What’s Next: Middleware
So far, we’ve built an Express app that can:
- serve routes,
- return text, HTML, or JSON,
- handle query strings and parameters,
- and even serve static files.
But every real-world app has “stuff” that needs to happen before (or after) a request hits your route handler: logging, authentication, input validation, error handling.
That’s where middlewares come in.
In Part 2, we’ll break down middlewares in detail how they works, how to write your own, and how to avoid common mistakes. That’s when Express really starts to shine.
Top comments (0)