Blog
3 min read

What Is Express.js? The Node.js Web Framework, Explained

Express is a minimal web framework for Node.js — the most widely used way to build APIs and servers in JavaScript. What it does, routes and middleware explained, a small REST API example, Express 5, how it compares with Fastify, Hono and Next.js API routes, and what to add for production.

Express is a small, flexible framework for building web servers and APIs with Node.js. It's been the default choice for JavaScript back-ends for over a decade, and a huge share of tutorials, libraries and AI-generated back-ends use it. (What is Node.js?)

A tiny server

import express from 'express'

const app = express()
app.use(express.json())

app.get('/api/hello', (req, res) => {
  res.json({ message: 'Hello!' })
})

app.listen(3000, () => console.log('Listening on http://localhost:3000'))
npm install express
node server.js

Visit http://localhost:3000/api/hello.

The two ideas in Express

1. Routes

A route maps an HTTP method and path to a function:

app.get('/api/products', listProducts)        // read
app.post('/api/products', createProduct)      // create
app.get('/api/products/:id', getProduct)      // :id is a path parameter
app.patch('/api/products/:id', updateProduct)
app.delete('/api/products/:id', deleteProduct)

Inside a handler, req is the request (req.params.id, req.query, req.body, req.headers) and res is the response (res.json(), res.status(404)). (GET vs POST, Query parameters)

2. Middleware

Middleware is a function that runs on requests before (or after) your route — for logging, parsing JSON, checking login, rate limiting:

function requireLogin(req, res, next) {
  if (!req.user) return res.status(401).json({ error: 'Not logged in' })
  next()   // pass control to the next middleware or route
}

app.get('/api/me', requireLogin, (req, res) => res.json(req.user))

Express apps are mostly a chain of middleware plus routes. (401 vs 403)

A small REST API

const products = []

app.post('/api/products', (req, res) => {
  const { name, price } = req.body
  if (!name || typeof price !== 'number') {
    return res.status(400).json({ error: 'name and numeric price required' })
  }
  const product = { id: products.length + 1, name, price }
  products.push(product)
  res.status(201).json(product)
})

app.get('/api/products/:id', (req, res) => {
  const product = products.find(p => p.id === Number(req.params.id))
  if (!product) return res.status(404).json({ error: 'Not found' })
  res.json(product)
})

In a real app the array becomes a database, and validation uses a library like Zod. (Designing a REST API, Validating input with Zod)

Express 5

Express 5, the first major version in many years, is now the current release. The biggest practical change: errors thrown in async route handlers are passed to your error handler automatically, instead of crashing or hanging. Some route path syntax also changed, so check the migration guide when upgrading older code.

Express vs the alternatives

Express Fastify Hono Next.js API routes
Style Minimal, flexible Fast, schema-based Tiny, runs anywhere (edge, Bun) Built into a Next.js app
Ecosystem Largest Large Growing Next.js
Best for Most APIs, learning High-throughput APIs Edge/serverless Full-stack Next.js

Express is rarely the fastest, but it's the most documented — which matters when you're learning or when an AI tool writes code for you.

What to add for production

Express is minimal on purpose. A production app typically adds:


EasySpawn runs your Express API on your own server behind HTTPS, keeps it running, and puts Postgres right next to it. See how it works or join the waitlist.

Related: How to Deploy an Express App · What Is Node.js? · Designing a REST API · What Is a Framework?

Keep reading