DEV Community

Cover image for Getting Started with Express.js: The Basics
Adrian Jiga
Adrian Jiga

Posted on

Getting Started with Express.js: The Basics

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")
})
Enter fullscreen mode Exit fullscreen mode

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")
})
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
  • Install Express:
npm install express
Enter fullscreen mode Exit fullscreen mode
  • 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")
})
Enter fullscreen mode Exit fullscreen mode
  • Run the server:
node server.js
Enter fullscreen mode Exit fullscreen mode

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")
})
Enter fullscreen mode Exit fullscreen mode

Visiting http://localhost:3000/about will trigger this function.

POST Requests

app.post("/submit", (req, res) => {
  res.send("Form submitted")
})
Enter fullscreen mode Exit fullscreen mode

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}`)
})
Enter fullscreen mode Exit fullscreen mode

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}`)
})
Enter fullscreen mode Exit fullscreen mode

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"))
Enter fullscreen mode Exit fullscreen mode

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" }
  ])
})
Enter fullscreen mode Exit fullscreen mode

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")
})
Enter fullscreen mode Exit fullscreen mode

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)