Skip to content

Express, a popular Node.js Framework

Express is a Node.js Web Framework. Node.js is an amazing tool for building networking services and applications. Express builds on top of its features to provide easy to use functionality that satisfy the needs of the Web Server use-case.

Introduction to Express

Express is a Node.js Web Framework.

Node.js is an amazing tool for building networking services and applications.

Express builds on top of its features to provide easy to use functionality that satisfies the needs of the Web Server use-case. It’s Open Source, free, easy to extend and very performant. There are also lots and lots of pre-built packages you can just drop in and use to perform all kinds of things.

Installation

You can install Express into any project with npm:

npm install express

or Yarn:

yarn add express

Both commands will also work in an empty directory, when starting your project from scratch (although npm does not create a package.json file at all, and Yarn creates a basic one).

Just run npm init or yarn init if you’re starting a new project from scratch.

Hello World

We’re ready to create our first Express Web Server.

Here is some code:

const express = require('express')
const app = express()

app.get('/', (req, res) => res.send('Hello World!'))
app.listen(3000, () => console.log('Server ready'))

Save this to an index.js file in your project root folder, and start the server using

node index.js

You can open the browser to port 3000 on localhost and you should see the Hello World! message.

Learn the basics of Express by understanding the Hello World code

Those 4 lines of code do a lot behind the scenes.

First, we import the express package to the express value.

We instantiate an application by calling the express() method.

Once we have the application object, we tell it to listen for GET requests on the / path, using the get() method.

There is a method for every HTTP verb: get(), post(), put(), delete(), patch():

app.get('/', (req, res) => { /* */ })
app.post('/', (req, res) => { /* */ })
app.put('/', (req, res) => { /* */ })
app.delete('/', (req, res) => { /* */ })
app.patch('/', (req, res) => { /* */ })

Those methods accept a callback function - which is called when a request is started - and we need to handle it.

We pass in an arrow function:

(req, res) => res.send('Hello World!')

Express sends us two objects in this callback, which we called req and res, they represent the Request and the Response objects.

Request is the HTTP request. It gives us all the request information, including the request parameters, the headers, the body of the request, and more.

Response is the HTTP response object that we’ll send to the client.

What we do in this callback is to send the ‘Hello World!’ string to the client, using the Response.send() method.

This method sets that string as the body, and it closes the connection.

The last line of the example actually starts the server, and tells it to listen on port 3000. We pass in a callback that is called when the server is ready to accept new requests.

Request parameters

I mentioned how the Request object holds all the HTTP request information.

These are the main properties you’ll likely use:

PropertyDescription
.appholds a reference to the Express app object
.baseUrlthe base path on which the app responds
.bodycontains the data submitted in the request body (must be parsed and populated manually before you can access it)
.cookiescontains the cookies sent by the request (needs the cookie-parser middleware)
.hostnamethe hostname as defined in the Host HTTP header value
.ipthe client IP
.methodthe HTTP method used
.paramsthe route named parameters
.paththe URL path
.protocolthe request protocol
.queryan object containing all the query strings used in the request
.securetrue if the request is secure (uses HTTPS)
.signedCookiescontains the signed cookies sent by the request (needs the cookie-parser middleware)
.xhrtrue if the request is an XMLHttpRequest

Sending a response

In the Hello World example we used the Response.send() method to send a simple string as a response, and to close the connection:

(req, res) => res.send('Hello World!')

If you pass in a string, it sets the Content-Type header to text/html.

If you pass in an object or an array, it sets the application/json Content-Type header, and parses that parameter into JSON.

send() automatically sets the Content-Length HTTP response header.

send() also automatically closes the connection.

Send a JSON response

The Response.json() method accepts an object or array, and converts it to JSON before sending it:

res.json({ username: 'Flavio' })

Use end() to send an empty response

An alternative way to send the response, without any body, is by using the Response.end() method:

res.end()

Change any HTTP header value

You can change any HTTP header value using Response.set():

res.set('Content-Type', 'text/html')

There is a shortcut for the Content-Type header, however:

res.type('.html')
// => 'text/html'

res.type('html')
// => 'text/html'

res.type('json')
// => 'application/json'

res.type('application/json')
// => 'application/json'

res.type('png')
// => image/png:

Set the cookies

Use the Response.cookie() method to manipulate your cookies.

Examples:

res.cookie('username', 'Flavio')

This method accepts a third parameter which contains various options:

res.cookie('username', 'Flavio', { domain: '.flaviocopes.com', path: '/administrator', secure: true })

res.cookie('username', 'Flavio', { expires: new Date(Date.now() + 900000), httpOnly: true })

The most useful parameters you can set are:

ValueDescription
domainthe cookie domain name
expiresset the cookie expiration date. If missing, or 0, the cookie is a session cookie
httpOnlyset the cookie to be accessible only by the web server. See HttpOnly
maxAgeset the expiry time relative to the current time, expressed in milliseconds
paththe cookie path. Defaults to /
secureMarks the cookie HTTPS only
signedset the cookie to be signed
sameSiteValue of SameSite

A cookie can be cleared with:

res.clearCookie('username')

Set the HTTP response status

Use the Response.status():

res.status(404).end()

or

res.status(404).send('File not found')

sendStatus() is a shortcut:

res.sendStatus(200)
// === res.status(200).send('OK')

res.sendStatus(403)
// === res.status(403).send('Forbidden')

res.sendStatus(404)
// === res.status(404).send('Not Found')

res.sendStatus(500)
// === res.status(500).send('Internal Server Error')

Handling redirects

Redirects are common in Web Development. You can create a redirect using the Response.redirect() method:

res.redirect('/go-there')

This creates a 302 redirect.

A 301 redirect is made in this way:

res.redirect(301, '/go-there')

You can specify an absolute path (/go-there), an absolute url (https://anothersite.com), a relative path (go-there) or use the .. to go back one level:

res.redirect('../go-there')
res.redirect('..')

You can also redirect back to the Referer HTTP header value (defaulting to / if not set) using

res.redirect('back')

Send a file to be downloaded

The Response.download() method allows you to send a file attached to the request. Instead of showing the page, the browser will save the file to disk.

res.download('/file.pdf')

res.download('/file.pdf', 'user-facing-filename.pdf')

Support for JSONP

JSONP is a way to consume cross-origin APIs from client-side JavaScript.

You can support it in your API by using the Response.jsonp() method, which is like Response.json() but handles JSONP callbacks:

res.jsonp({ username: 'Flavio' })

Routing

In the Hello World example we used this code:

app.get('/', (req, res) => { /* */ })

This creates a route that maps accessing the root domain URL / to the response we want to provide.

Named parameters

What if we want to listen for custom requests? Maybe we want to create a service that accepts a string, and returns that string in uppercase. If we don’t want the parameter to be sent as a query string, but part of the URL, we use named parameters:

app.get('/uppercase/:theValue', (req, res) => res.send(req.params.theValue.toUpperCase()))

If we send a request to /uppercase/test, we’ll get TEST in the body of the response.

You can use multiple named parameters in the same URL, and they will all be stored in req.params.

Use a regular expression to match a path

You can use regular expressions to match multiple paths with one statement:

app.get(/post/, (req, res) => { /* */ })

will match /post, /post/first, /thepost, /posting/something, and so on.

Middleware

A middleware is a function that hooks into the routing process and performs an operation at some point, depending on what we want it to do.

It’s commonly used to edit the request or response objects, or terminate the request before it reaches the route handler code.

It’s added to the execution stack like this:

app.use((req, res, next) => { /* */ })

This is similar to defining a route, but in addition to the Request and Response objects instances, we also have a reference to the next middleware function, which we assign to the variable next.

We always call next() at the end of our middleware function, to pass the execution to the next handler, unless we want to prematurely end the response, and send it back to the client.

You typically use pre-made middleware, in the form of npm packages. A big list of the available ones is here.

One example is cookie-parser, which is used to parse the cookies into the req.cookies object. You install it using npm install cookie-parser and you can use it like this:

const express = require('express')
const app = express()
const cookieParser = require('cookie-parser')

app.get('/', (req, res) => res.send('Hello World!'))

app.use(cookieParser())
app.listen(3000, () => console.log('Server ready'))

You can also set a middleware function to run for specific routes only, by using it as the second parameter of the route definition:

const myMiddleware = (req, res, next) => {
  /* ... */
  next()
}

app.get('/', myMiddleware, (req, res) => res.send('Hello World!'))

If you need to store data that’s generated in a middleware to pass it down to subsequent middleware functions, or to the request handler, you can use the Request.locals object. It will attach that data to the current request:

req.locals.name = 'Flavio'

Serving static assets

It’s common to have images, CSS and more in a public subfolder, and expose them to the root level:

const express = require('express')
const app = express()

app.use(express.static('public'))

/* ... */

app.listen(3000, () => console.log('Server ready'))

If you have an index.html file in public/, that will be served if you now hit the root domain URL (http://localhost:3000)

Templating

Express is capable of handling server-side templates.

Jade is the default template engine, but you can use many different template ones, including Pug, Mustache, EJS and more.

Say I don’t like Jade (I don’t), and I want to use Handlebars instead.

I can install it using npm install hbs.

Put an about.hbs template file in the views/ folder:

Hello from {{name}}

and then use this Express configuration to serve it on /about:

const express = require('express')
const app = express()
const hbs = require('hbs')

app.set('view engine', 'hbs')

app.get('/about', (req, res) => {
  res.render('about', { name: 'Flavio' })
})

app.listen(3000, () => console.log('Server ready'))

You can also render a React application server-side, using the express-react-views package.

Start with npm install express-react-views react react-dom.

Now instead of requiring hbs we require express-react-views and use that as the engine, using jsx files:

const express = require('express')
const app = express()

app.set('view engine', 'jsx')
app.engine('jsx', require('express-react-views').createEngine())

app.get('/about', (req, res) => {
  res.render('about', { name: 'Flavio' })
})

app.listen(3000, () => console.log('Server ready'))

Just put an about.jsx file in views/, and calling /about should present you an “Hello from Flavio” string:

const React = require('react')

class HelloMessage extends React.Component {
  render() {
    return <div>Hello from {this.props.name}</div>
  }
}

module.exports = HelloMessage

What’s next?

Express has all the power of Node.js under the hood.

You can do anything that you want, including connecting to a database, using any kind of caching, Redis, using sockets, and everything you can imagine doing on a server.

The sky is the limit.


→ Here's my latest YouTube video

→ Get my Express.js Handbook

→ I wrote 17 books to help you become a better developer, download them all at $0 cost by joining my newsletter

JOIN MY CODING BOOTCAMP, an amazing cohort course that will be a huge step up in your coding career - covering React, Next.js - next edition February 2025

Bootcamp 2025

Join the waiting list