Express.js tutorial
last modified October 18, 2023
In this article we show how to create simple web applications in JavaScript using Express framework.
Express.js
Express.js is a free and open-source web application framework for Node.js. Express is a minimal and flexible web application framework that provides a robust set of features for web and mobile applications.
It is a fast, unopinionated, and minimalist web framework.
Express.js installation
We install the Express framework. Later, we install additional packages including Lodash, sqlite3, and Axios.
$ node -v v18.2.0
We use Node version 18.2.0.
$ npm init -y $ npm i express
We use the npm
tool to install Express.
URL
A Uniform Resource Locator (URL), is a reference to a web resource that specifies its location on a computer network and a mechanism for retrieving it. A web resource is any data that can be obtained via web, such as HTML documents, PDF files, PNG images, JSON data, or plain text.
A generic URL has the following form:
scheme:[//[user:password@]host[:port]][/]path[?query][#fragment]
The parts between the []
brackets are optional.
Express.js routes
A route associates an HTTP verb (such as GET, POST, PUT, DELETE) and a URL path to a handler function. To create routes, we use functions of the Express application object.
app.get('/', (req, res) => { });
Here we map the /
path sent in a GET request to the handler
function. The function receives request and response objects as parameters.
To group routes and separate them in modules, we can use the Router
middleware.
Express.js middleware
Express middleware is a the core of the framework. It sits in the middle of the request-response cycle. Middleware is a series of functions that are called between the request object and the response object in a pipeline. Express is a minimal framework. Most of the functionality is available as middleware functions.
Middleware functions are used to implement functionalities such as authentication, CSRF protection, logging, or cookie processing. They are not used to implement the business logic of the application.
The use
function mounts the specified middleware function or
functions at the specified path.
Express.js GET request example
The get
function routes an HTTP GET request to the
specified path with the specified callback function.
const express = require('express'); const app = express(); app.get('/', (req, res) => res.send('Hello there!')); app.listen(3000, () => console.log('Application started on port 3000'));
The application processes a GET request and sends a short message to the client.
const express = require('express');
We include the express
package.
const app = express();
The Express application is created.
app.get('/', (req, res) => res.send('Hello there!'));
With the get
function, we map the /
path
to the anonymous function, which sends a string back to the client.
app.listen(3000, () => console.log('Application started on port 3000'));
When started, the application listens on port 3000.
$ node main.js Application started on port 3000
We start the application on localhost.
$ curl localhost:3000 Hello there!
We create a request with the curl
command.
Express.js HTTP headers
The request object also includes the request headers sent from the client. Request headers are HTTP headers that contain more information about the resource to be fetched, and about the client requesting the resource.
const express = require('express'); const app = express(); app.get('/', (req, res) => { res.set({ 'Content-Type': 'text/plain; charset=utf-8' }); res.send(`The request IP is: ${req.ip}`); }); app.listen(3000, () => { console.log('Application started on port 3000'); });
The example outputs the IP address of the client that generated the request.
$ curl localhost:3000 The request IP is: ::1
This is a sample output; the ::1
is the loopback address in IPv6.
Express.js query parameters
Query string is a part of the URL which is used to add some criteria to the
request for the resource. It is often a sequence of key/value pairs. It follows
the path and starts with the ?
character.
const express = require('express'); const app = express(); app.get('/greet', (req, res) => { res.set({ 'Content-Type': 'text/plain; charset=utf-8' }); let name = req.query.name; let msg = `Hello ${name}`; res.send(msg); }); app.listen(3000, () => console.log('Application started on port 3000'));
The application creates and sends a message to the client. It uses
the value from the name
query parameter.
app.get('/greet', (req, res) => {
We route the HTTP GET request to the specified path with the specified callback function. The callback function receives two parameters: request object and response object. The request object represents the HTTP request and has properties for the request query string, parameters, body, and HTTP headers.
res.set({ 'Content-Type': 'text/plain; charset=utf-8' });
We set the response content type and character set. Our output will be plain
text. The default content type is text/html
.
let name = req.query.name;
We get the query parameter from the request query property. It is an object containing a property for each query string parameter in the route.
let msg = `Hello ${name}`;
A message is built.
res.send(msg);
The message is sent to the client.
$ curl -i localhost:3000/greet?name=Lucia HTTP/1.1 200 OK X-Powered-By: Express Content-Type: text/plain; charset=utf-8 Content-Length: 11 ETag: W/"b-QOTbNGLVpb1ETGDcTt/rfpJV0wI" Date: Tue, 21 Apr 2020 06:58:12 GMT Connection: keep-alive
We create a GET request to the application using the curl
command line tool. With the -i
option, we also include the response
header.
Express.js path parameters
Values can be send to the web application via query parameters or path
parameters. The path parameter is specified after a colon /:param
.
The req.params
property is an object containing properties mapped
to the named route parameters.
const express = require('express'); const app = express(); app.get('/show/:name/:age/', (req, res) => { res.set({ 'Content-Type': 'text/plain; charset=utf-8' }); let name = req.params.name; let age = req.params.age; let msg = `${name} is ${age} years old`; res.send(msg); }); app.listen(3000, () => console.log('Application started on port 3000'));
The application returns a message to the user, containing the two path parameters sent.
app.get('/show/:name/:age/', (req, res) => {
The path parameters are defined in the route path; the names of the parameters follow the colon character.
let name = req.params.name; let age = req.params.age;
We retrieve the path parameters from the req.params
object.
$ curl localhost:3000/show/Robert/24/ Robert is 24 years old
Express.js path pattern matching
It is possible to use regular expressions on the request paths. This way we can restrict the values passed with the request path.
const express = require('express'); const app = express(); app.get('/city/:id([0-9]{1,5})', (req, res) => { res.set({ 'Content-Type': 'text/plain; charset=utf-8' }); res.send(`id: ${req.params.id}`); }); app.listen(3000, () => { console.log('Application started on port 3000'); });
In the example, we restrict the Id value to be an integer, having between 1 and 5 digits.
$ curl localhost:3000/city/12345 id: 12345
This is the output for Id 12345.
Express JSON
JSON is a lightweight data-interchange format. It is easy for humans to read and machines to parse and generate. Web applications often consume and produce JSON data.
The res.json
function sends a JSON response. The parameter can be
any JSON type, including object, array, string, Boolean, number, or null.
$ npm i lodash
We install the Lodash library.
const express = require('express'); const _ = require('lodash'); const app = express(); app.get('/movies', (req, res) => { res.set({ 'Content-Type': 'application/json; charset=utf-8' }); let movies = { 1: 'Toy story', 2: 'The Raid', 3: 'Hero', 4: 'Ip Man', 5: 'Kung Fu Panda' }; let movie = _.sample(movies); res.json({movie}); }); app.listen(3000, () => console.log('Application started on port 3000'));
The example picks random movie from the movies
object. The
movie is returned in JSON format.
res.set({ 'Content-Type': 'application/json; charset=utf-8' });
We set the content type for JSON data.
let movies = { 1: 'Toy story', 2: 'The Raid', 3: 'Hero', 4: 'Ip Man', 5: 'Kung Fu Panda' };
We have a couple of movies in the JS object.
let movie = _.sample(movies);
With the Lodash _sample
method, we pick a random movie.
res.json({movie});
The picked movie is sent to the client in JSON format.
$ curl localhost:3000/movies {"movie":"Toy story"} $ curl localhost:3000/movies {"movie":"Kung Fu Panda"}$ $ curl localhost:3000/movies {"movie":"Kung Fu Panda"}
Express bodyparser
The body-parser
is a Node request body parsing middleware.
It parses incoming request bodies in a middleware before our handlers. The
data is available under the req.body
property.
For this example, we need to install the Axios package.
$ npm i axios
Axios is a promise based HTTP client for the browser and Node.js
const express = require("express"); const app = express(); app.use(express.json()); app.post('/info', (req, res) => { console.log(req.body); res.json(req.body); }); app.listen(3000, () => { console.log('Application started on port 3000'); });
In the example, we parse JSON bodies.
app.use(express.json());
Here we apply the body parser middleware for parsing JSON data in the request body.
res.json(req.body);
We return the parsed data back to the client in JSON format.
const axios = require('axios'); async function makePostRequest() { params = { first_name: 'John', last_name: 'Doe', email: 'gardener' } let res = await axios.post('http://localhost:3000/info/', params); console.log(`Status code: ${res.status}`); console.log(`Status text: ${res.statusText}`); console.log(`Request method: ${res.request.method}`); console.log(`Path: ${res.request.path}`); console.log(res.data); } makePostRequest();
With the Axios library, we make a POST request to the Express application.
$ node post-request.js Status code: 200 Status text: OK Request method: POST Path: /info/ { first_name: 'John', last_name: 'Doe', email: 'gardener' }
Express post form
The HTTP POST method sends data to the server. It is often used when uploading a file or when submitting a completed web form. The data sent from the form is stored in the body of the request.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Form</title> </head> <body> <form action="message" method="post"> <div> <label>Name:</label> <input type="text" name="name"> </div> <div> <label>Message</label> <input type="text" name="message"> </div> <button type="submit">Send</button> </form> </body> </html>
We have a form with two input fields: name
and message
.
const express = require('express'); const path = require('path'); const app = express(); app.use(express.static('public')); app.use(express.urlencoded({ extended: true })); app.get('/', (req, res) => { res.sendFile(path.join(__dirname, 'public', 'index.html')); }); app.post('/message', (req, res) => { res.set({ 'Content-Type': 'text/plain; charset=utf-8' }); let name = req.body.name; let message = req.body.message; let output = `${name} says: ${message}`; res.send(output); }); app.listen(3000, () => { console.log('Application started on port 3000'); })
We have two routes in the application. The /
path brings
the form to the user. The function simply sends the static HTML file
containing the form. The /message
path processes the form
and builds a message from the posted data.
app.use(express.urlencoded({ extended: true }));
We apply the urlencoded
middleware for processing of the form.
The middleware parses incoming requests with urlencoded payloads.
The application/x-www-form-urlencoded
content type is the default.
(The extended option chooses a specific library for parsing the data.)
let name = req.body.name; let message = req.body.message; let output = `${name} says: ${message}`;
We get the two parameters from the body of the request and build an output.
res.send(output);
The output is sent to the client.
Express send file
The sendFile
function transfers the file at the given path.
The image is displayed in the browser. The download
function
transfers the image; the image is offered as an attachment by browsers.
const express = require('express'); const path = require('path'); const app = express(); app.get('/file', (req, res) => { res.set({ 'Content-Type': 'image/jpeg' }); let file = path.join(__dirname, 'img/book.jpg'); // res.sendFile(file); res.download(file, 'book-image.jpg'); }); app.listen(3000, () => { console.log('Application started on port 3000'); })
The example sends an image to the client. Note that since the browsers are doing caching, we might no see a difference between the two methods. In such a case, we can open a private window.
res.set({ 'Content-Type': 'image/jpeg' });
We set the appropriate content type.
let file = path.join(__dirname, 'img/book.jpg');
We specify the path to the image.
Express static files
Static files are files that do not change. They include CSS files, JavaScript files and images; also HTML files which do not contain template directives.
To work with static files, we use the builtin static
middleware.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Home page</title> </head> <body> <p> This is home page </p> </body> </html>
This is the home page. It is an example of a static HTML file.
const express = require('express'); const path = require('path'); const app = express(); app.use(express.static('public')); app.get('/', (req, res) => { res.sendFile(path.join(__dirname, 'public/index.html')); }); app.listen(3000, () => { console.log('Application started on port 3000'); });
The example displays a simple static HTML file for the home page.
app.use(express.static('public'));
We use the static middleware; the static files are stored in the
public
directory.
app.get('/', (req, res) => { res.sendFile(path.join(__dirname, 'public/index.html')); });
The sendFile
function transfers the file at the given
path.
Express favicon
A favicon, also known as a website icon, is a small icon, associated with
a particular website or web page. To display a favicon in an Express
application, we can use the express-favicon
middleware.
$ npm i express-favicon
We install the package.
$ ls public/images/ favicon.ico
We have the favicon
in the public/images
directory.
const express = require('express'); const path = require('path'); const favicon = require('express-favicon'); const app = express(); app.use(favicon(path.join(__dirname, 'public', 'images', 'favicon.ico'))); app.get('/', (req, res) => { res.set({ 'Content-Type': 'text/plain; charset=utf-8' }); res.send('Home page'); }); app.listen(3000, () => { console.log('Application started on port 3000'); });
The example displays a favicon.
app.use(favicon(path.join(__dirname, 'public', 'images', 'favicon.ico')));
We apply the middleware function with the use
function. The icon
is located in the public/images
directory.
Express custom 404 error message
The HTTP 404, 404 Not Found, 404, Page Not Found error message is a Hypertext Transfer Protocol (HTTP) standard response code used in web communication. It indicates that the browser was able to communicate with a given server, but the server could not find requested resource.
Express provides a rudimentary 404 message. In the following example, we create our custom one.
const express = require('express'); const app = express(); app.get('/', (req, res) => { res.set({ 'Content-Type': 'text/plain; charset=utf-8' }); res.send('Home page'); }); app.get('/about', (req, res) => { res.set({ 'Content-Type': 'text/plain; charset=utf-8' }); res.send('About page'); }); app.get('/contact', (req, res) => { res.set({ 'Content-Type': 'text/plain; charset=utf-8' }); res.send('Contact page'); }); app.use((req, res) => { res.statusCode = 404; res.end("404 - page not found"); }); app.listen(3000, () => { console.log('Application started on port 3000'); });
In the example, we register an error handler function for the 404 not found error.
It shows the 404.html
file.
app.use((req, res) => { res.statusCode = 404; res.end("404 - page not found"); });
We set the status code and finish the response process. The mapping of the error handling comes after all other mappings.
Express Router middleware
Basic routing is performed with the routing methods of the application
object, such as get
, post
, put
,
head
, or delete
.
Routes can be grouped and separated into modules with the Router
middleware.
const express = require('express'); const router = express.Router(); router.get('/', (req, res) => { res.set({ 'Content-Type': 'text/plain; charset=utf-8' }); res.send('Home page'); }); router.get('/about', (req, res) => { res.set({ 'Content-Type': 'text/plain; charset=utf-8' }); res.send('About page'); }); router.get('/contact', (req, res) => { res.set({ 'Content-Type': 'text/plain; charset=utf-8' }); res.send('Contact page'); }); module.exports = router;
There are three routes in the routes.js
module.
The routes are bound to the Router
middleware, which is exposed
for inclusion.
const express = require('express'); const routes = require('./routes'); const app = express(); app.use(routes); app.listen(3000, () => { console.log('Application started on port 3000'); });
We include the routes and apply them to the application with
use
.
Express - using template engine
A template engine or template processor is a library designed to combine templates with a data model to produce documents. Template engines are used to generate large amounts of emails, in source code preprocessing, or producing dynamic HTML pages.
It is possible to use many template engines with Express; for instance Liquid, Nunjucks, Pug, or EJS.
Liquid is a JavaScript template engine, created by Shopify. Liquid
files have the extension of .liquid
; they are a mix of static
data such as HTML and Liquid constructs. Learn more about Liquid in
the Liquid.js tutorial.
$ npm i liquidjs
We install the Liquid template engine.
In Liquid, we use the double curly brace delimiters {{ }}
for
output and the curly brace percentage delimiters {% %}
for logic.
{% if user != null %} Hello {{ user.name }} {% endif %}
This code is a sample Liquid syntax.
const express = require('express'); const path = require('path'); const { Liquid } = require('liquidjs'); const app = express(); const engine = new Liquid(); app.engine('liquid', engine.express()); app.set('views', path.resolve(__dirname, 'views')); app.set('view engine', 'liquid'); app.get('/today', (req, res) => { let today = new Date(); res.render('show_date', {now: today}); }); app.use((req, res) => { res.statusCode = 404; res.end("404 - page not found"); }); app.listen(3000, () => { console.log('Application started on port 3000'); });
In the example, we read a value from a path parameter and send it to the
show_date.liquid
template file to be processed.
app.engine('liquid', engine.express()); app.set('views', path.resolve(__dirname, 'views')); app.set('view engine', 'liquid');
We set up the Liquid template engine. The template files are located
in the views
directory.
res.render('show_date', {now: today});
The render
function renders a view and sends the rendered HTML
string to the client. The first parameter is the view name (without extension);
the second parameter is the locals
object, whose properties define
local variables for the view.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Show date</title> </head> <body> <p> Today is {{ now }} </p> </body> </html>
This is the show_date.liquid
template file. The template consists
of static data and dynamic data.
<p> Today is {{ now }} </p>
With the {{}}
syntax, we output the value of the now
variable, which was passed to the template.
$ curl localhost:3000/today <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Show date</title> </head> <body> <p> Today is Tue Jun 28 2022 11:15:15 GMT+0200 (Central European Summer Time) </p> </body> </html>
Express SQLite example
In the following example, we send data from an SQLite database. SQLite is an file-based relational database engine.
$ npm install sqlite3
We use sqlite3
package.
app-sqlite.js data cities.sql test.db
This is the project structure.
BEGIN TRANSACTION; DROP TABLE IF EXISTS cities; CREATE TABLE cities(id INTEGER PRIMARY KEY, name TEXT, population INTEGER); INSERT INTO cities(name, population) VALUES('Bratislava', 432000); INSERT INTO cities(name, population) VALUES('Budapest', 1759000); INSERT INTO cities(name, population) VALUES('Prague', 1280000); INSERT INTO cities(name, population) VALUES('Warsaw', 1748000); INSERT INTO cities(name, population) VALUES('Los Angeles', 3971000); INSERT INTO cities(name, population) VALUES('New York', 8550000); INSERT INTO cities(name, population) VALUES('Edinburgh', 464000); INSERT INTO cities(name, population) VALUES('Berlin', 3671000); COMMIT;
We use this data.
$ cd data $ sqlite3 test.db SQLite version 3.37.2 2022-01-06 13:25:41 Enter ".help" for usage hints. sqlite> .read cities.sql sqlite> select * from cities; 1|Bratislava|432000 2|Budapest|1759000 3|Prague|1280000 4|Warsaw|1748000 5|Los Angeles|3971000 6|New York|8550000 7|Edinburgh|464000 8|Berlin|3671000
We load the data into the test.db
database.
const express = require('express'); const sqlite3 = require('sqlite3').verbose(); const app = express(); const db = new sqlite3.Database('data/test.db'); app.get('/', (req, res) => { res.set({ 'Content-Type': 'text/plain; charset=utf-8' }); res.send('Home page'); }); app.get('/cities', (req, res) => { const sql = 'select * from cities'; const params = []; db.all(sql, params, (err, rows) => { if (err) { res.status(400).json({'error': err.message}); return; } if (!rows) { res.status(204).json({'error': 'No cities found'}); return; } res.json({ 'message':'success', 'data':rows }); }); }); app.get('/city/:id', (req, res) => { const sql = 'select * from cities where id = ?'; const params = [req.params.id]; db.get(sql, params, (err, row) => { if (err) { res.status(400).json({'error':err.message}); return; } if (!row) { res.status(204).json({'error': 'City not found'}); return; } res.json({ 'message':'success', 'data':row }); }); }); const server = app.listen(3000, () => { console.log('Application started on port 3000'); }); process.on('SIGINT', () => { db.close((err) => { console.log('Application terminating'); if (err) { console.error(err.message); } console.log('Closing the database connection.'); }); server.close(); });
There are three routes in the application. One is for the home page, another for all cities, and third is for a specific city. The cities are returned in JSON format.
const sqlite3 = require('sqlite3').verbose();
We include the sqlite3
package. The verbose
function
produces debugging information.
const db = new sqlite3.Database('data/test.db');
We connect to the database file.
app.get('/cities', (req, res) => { const sql = 'select * from cities'; const params = []; ...
For the /cities
route, we fetch all rows from the database and
send them as JSON data to the client. There are no parameters passed to
the SQL statement.
db.all(sql, params, (err, rows) => {
The all
function runs the SQL query with the specified parameters
and calls the callback with all result rows afterwards.
if (err) { res.status(400).json({'error': err.message}); return; }
If there is an error, we send the 400 status code and return.
if (!rows) { res.status(204).json({'error': 'No cities found'}); return; }
If there is no data found, we send the 204 No content status code.
res.json({ 'message':'success', 'data':rows });
We send a JSON response with the json
function, containing the
selected rows.
app.get('/city/:id', (req, res) => { const sql = 'select * from cities where id = ?'; const params = [req.params.id]; ...
In this route, we search for a city with a specific Id. The params array contains the Id from the request path parameter.
db.get(sql, params, (err, row) => {
The get
function runs the SQL query with the specified parameters
and calls the callback with the first result row afterwards.
res.json({ 'message':'success', 'data':row });
The selected row is sent to the client in JSON format.
$ curl localhost:3000/cities {"message":"success","data":[{"id":1,"name":"Bratislava","population":432000}, {"id":2,"name":"Budapest","population":1759000}, {"id":3,"name":"Prague","population":1280000}, {"id":4,"name":"Warsaw","population":1748000}, {"id":5,"name":"Los Angeles","population":3971000}, {"id":6,"name":"New York","population":8550000}, {"id":7,"name":"Edinburgh","population":464000}, {"id":8,"name":"Berlin","population":3671000}]}
Source
In this article we have introduced the Express.js web framework.