Table of Contents
Introduction to [Link].............................................2
How to Install Express................................................2
The First "Hello, World" Example.................................................3
Request Parameters..................................................4
How to Send a Response to the Client.................................5
How to use end() to send an empty response..........................5
How to set the HTTP response status.................................5
How to Send a JSON Response.......................................6
How to Manage Cookies..............................................6
Examples:............................................................................... 6
How to Work with HTTP Headers....................................7
How to access HTTP headers values from a request...........................7
How to change any HTTP header value for a response........................7
How to Handle Redirects............................................................8
Routing in Express....................................................8
Named parameters...................................................9
How to use a regular expression to match a path................................9
Templates in Express.................................................9
Express Middleware................................................10
How to Serve Static Assets with Express.......................................11
How to Send Files to the Client...................................................12
Sessions in Express..................................................13
How to Validate Input in Express................................................14
How to Sanitizing Input in Express..............................................18
How to Handle Forms in Express................................................21
How to Handle File Uploads in Forms in Express............................22
1
MODULE 12: ExpressJs- REST API
DEVELOPMENT
Introduction to [Link]
[Link], commonly called Express, is a minimalist, fast, and flexible web framework for
[Link]. It is designed to simplify the development of server-side applications by providing a
robust set of features for building web applications, RESTful APIs, and backend services.
While [Link] provides the runtime environment to execute JavaScript on the server, Express
adds a layer of abstraction that makes handling HTTP requests, routing, middleware, and
responses much easier.
The framework follows a lightweight and unopinionated philosophy, meaning it does not
enforce strict rules on how applications should be structured, giving developers the freedom
to design their applications as needed. Express supports middleware functions, which can
process requests, modify responses, and handle errors in a modular way. It also includes
powerful routing capabilities, allowing developers to define how the server responds to
different HTTP methods and endpoints.
One of the biggest advantages of [Link] is its integration with [Link] modules and the
npm ecosystem, enabling developers to extend functionality easily using third-party libraries.
It is widely used for building APIs for web and mobile applications, microservices, real-time
applications, and dynamic websites. Its simplicity, flexibility, and strong community support
have made [Link] one of the most popular frameworks in the [Link] ecosystem.
How to Install Express
You can install Express into any project with npm.
If you're in an empty folder, first create a new [Link] project with this command:
npm init -y
then run this:
npm install express
to install Express into the project.
2
The First "Hello, World" Example
The first example we're going to create is a simple Express Web Server.
Copy this code:
const express = require('express')
const app = express()
[Link]('/', (req, res) => [Link]('Hello World!'))
[Link](3000, () => [Link]('Server ready'))
Save this to an [Link] file in your project root folder, and start the server using this
command:
node [Link]
You can open the browser to port 3000 on localhost and you should see the Hello World!
message.
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(), and patch():
[Link]('/', (req, res) => { /* */ })
[Link]('/', (req, res) => { /* */ })
[Link]('/', (req, res) => { /* */ })
[Link]('/', (req, res) => { /* */ })
[Link]('/', (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) => [Link]('Hello World!')
Express sends us two objects in this callback, which we called req and res. They represent the
Request and the Response objects.
Both are standards and you can read more on them here and here.
3
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 send the 'Hello World!' string to the client, using the
[Link]() 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:
PROPERTY DESCRIPTION
.APP holds a reference to the Express app object
.BASEURL the base path on which the app responds
.BODY contains the data submitted in the request body (must be parsed and
populated manually before you can access it)
.COOKIES contains the cookies sent by the request (needs the cookie-parser
middleware)
.HOSTNAME the hostname as defined in the Host HTTP header value
.IP the client IP
.METHOD the HTTP method used
.PARAMS the route named parameters
.PATH the URL path
.PROTOCOL the request protocol
.QUERY an object containing all the query strings used in the request
.SECURE true if the request is secure (uses HTTPS)
.SIGNEDCOOKIES contains the signed cookies sent by the request (needs the cookie-parser
middleware)
.XHR true if the request is an XMLHttpRequest
4
How to Send a Response to the Client
In the Hello World example, we used the send() method of the Response object to send a
simple string as a response, and to close the connection:
(req, res) => [Link]('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.
After this, send() closes the connection.
send() automatically sets the Content-Length HTTP response header, unlike end() which
requires you to do that.
How to use end() to send an empty response
An alternative way to send the response, without any body, is by using the [Link]()
method:
[Link]()
How to set the HTTP response status
Use the status() method on the response object:
[Link](404).end()
or
[Link](404).send('File not found')
sendStatus() is a shortcut:
[Link](200)
// === [Link](200).send('OK')
[Link](403)
// === [Link](403).send('Forbidden')
[Link](404)
// === [Link](404).send('Not Found')
5
[Link](500)
// === [Link](500).send('Internal Server Error')
How to Send a JSON Response
When you listen for connections on a route in Express, the callback function will be invoked
on every network call with a Request object instance and a Response object instance.
Example:
[Link]('/', (req, res) => [Link]('Hello World!'))
Here we used the [Link]() method, which accepts any string.
You can send JSON to the client by using [Link](), a useful method.
It accepts an object or array, and converts it to JSON before sending it:
[Link]({ username: 'Flavio' })
How to Manage Cookies
Use the [Link]() method to manipulate your cookies.
Examples:
[Link]('username', 'Flavio')
This method accepts a third parameter, which contains various options:
[Link]('username', 'Flavio', { domain: '.[Link]', path: '/administrator', secure:
true })
[Link]('username', 'Flavio', { expires: new Date([Link]() + 900000), httpOnly: true })
The most useful parameters you can set are:
Value Description
6
domain The cookie domain name
expires Set the cookie expiration date. If missing, or 0, the cookie is a session cookie
httpOnly Set the cookie to be accessible only by the web server. See HttpOnly
maxAge Set the expiry time relative to the current time, expressed in milliseconds
path The cookie path. Defaults to '/'
secure Marks the cookie HTTPS only
signed Set the cookie to be signed
sameSite Value of SameSite
A cookie can be cleared with:
[Link]('username')
How to Work with HTTP Headers
How to access HTTP headers values from a request
You can access all the HTTP headers using the [Link] property:
[Link]('/', (req, res) => {
[Link]([Link])
})
Use the [Link]() method to access one individual request header's value:
[Link]('/', (req, res) => {
[Link]('User-Agent')
})
How to change any HTTP header value for a response
You can change any HTTP header value using [Link]():
[Link]('Content-Type', 'text/html')
There is a shortcut for the Content-Type header, however:
7
[Link]('.html')
// => 'text/html'
[Link]('html')
// => 'text/html'
[Link]('json')
// => 'application/json'
[Link]('application/json')
// => 'application/json'
[Link]('png')
// => image/png:
How to Handle Redirects
Redirects are common in Web Development. You can create a redirect using the
[Link]() method:
[Link]('/go-there')
This creates a 302 redirect.
A 301 redirect is made in this way:
[Link](301, '/go-there')
You can specify an absolute path (/go-there), an absolute URL ([Link] a
relative path (go-there) or use the .. to go back one level:
[Link]('../go-there')
[Link]('..')
You can also redirect back to the Referrer HTTP header value (defaulting to / if not set)
using
[Link]('back')
Routing in Express
Routing is the process of determining what should happen when a URL is called, or also
which parts of the application should handle a specific incoming request.
8
In the Hello World example we used this code:
[Link]('/', (req, res) => { /* */ })
This creates a route that maps accessing the root domain URL / using the HTTP GET method
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 it as uppercase – and we don't want the parameter to be sent as a
query string, but as part of the URL. In a case like that, we use named parameters:
[Link]('/uppercase/:theValue', (req, res) => [Link]([Link]()))
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
[Link].
How to use a regular expression to match a path
You can use regular expressions to match multiple paths with one statement:
[Link](/post/, (req, res) => { /* */ })
will match /post, /post/first, /thepost, /posting/something, and so on.
Templates in Express
Express is capable of handling server-side template engines.
Template engines allow us to add data to a view, and generate HTML dynamically.
Express uses Jade as the default. Jade is the old version of Pug, specifically Pug 1.0.
Note that the name was changed from Jade to Pug due to a trademark issue in 2016, when the
project released version 2. You can still use Jade, aka Pug 1.0, but going forward, it's best to
use Pug 2.0
Although the last version of Jade is getting old, it's still the default in Express for backward
compatibility reasons.
In any new project, you should use Pug or another engine of your choice. The official site of
Pug is [Link]
You can use many different template engines, including Pug, Handlebars, Mustache, EJS and
more.
9
To use Pug we must first install it:
npm install pug
and when initializing the Express app, we need to set it:
const express = require('express')
const app = express()
[Link]('view engine', 'pug')
We can now start writing our templates in .pug files.
Create an about view:
[Link]('/about', (req, res) => {
[Link]('about')
})
and the template in views/[Link]:
p Hello from Flavio
This template will create a p tag with the content Hello from Flavio.
You can interpolate a variable using this code:
[Link]('/about', (req, res) => {
[Link]('about', { name: 'Flavio' })
})
p Hello from #{name}
Look at the Pug guide for more information on how to use Pug.
This online converter from HTML to Pug will be a great help: [Link]
Express Middleware
A middleware is a function that hooks into the routing process, performing an arbitrary
operation at some point in the chain (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.
Middleware is added to the execution stack like so:
10
[Link]((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, in order to pass the execution to
the next handler. That is 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. You can find a big list
of the available ones here.
One example is cookie-parser, which is used to parse cookies into the [Link] object. You
can install it using npm install cookie-parser and you use it like this:
const express = require('express')
const app = express()
const cookieParser = require('cookie-parser')
[Link]('/', (req, res) => [Link]('Hello World!'))
[Link](cookieParser())
[Link](3000, () => [Link]('Server ready'))
We can also set a middleware function to run for specific routes only (not for all), by using it
as the second parameter of the route definition:
const myMiddleware = (req, res, next) => {
/* ... */
next()
}
[Link]('/', myMiddleware, (req, res) => [Link]('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 [Link] object. It will
attach that data to the current request:
[Link] = 'Flavio'
How to Serve Static Assets with Express
It's common to have images, CSS, and more in a public subfolder, and expose them to the
root level:
11
const express = require('express')
const app = express()
[Link]([Link]('public'))
/* ... */
[Link](3000, () => [Link]('Server ready'))
If you have an [Link] file in public/, that will be served if you now hit the root domain
URL ([Link]
How to Send Files to the Client
Express provides a handy method to transfer a file as an attachment: [Link]().
Once a user hits a route that sends a file using this method, browsers will prompt the user to
download.
The [Link]() method allows you to send a file attached to the request, and the
browser, instead of showing it in the page, will save it to disk.
[Link]('/', (req, res) => [Link]('./[Link]'))
In the context of an app:
const express = require('express')
const app = express()
[Link]('/', (req, res) => [Link]('./[Link]'))
[Link](3000, () => [Link]('Server ready'))
You can set the file to be sent with a custom filename:
[Link]('./[Link]', '[Link]')
This method provides a callback function which you can use to execute code once the file has
been sent:
[Link]('./[Link]', '[Link]', (err) => {
if (err) {
//handle error
return
} else {
//do something
12
}
})
Sessions in Express
By default Express requests are sequential and no request can be linked to another. There is
no way to know if this request comes from a client that already performed a request
previously.
Users cannot be identified unless using some kind of mechanism that makes it possible.
That's what sessions are.
When implemented, every user of your API or website will be assigned a unique session, and
this allows you to store the user's state.
We'll use the express-session module, which is maintained by the Express team.
You can install it using this command:
npm install express-session
and once you're done, you can instantiate it in your application with this one:
const session = require('express-session')
This is a middleware, so you install it in Express using the following:
const express = require('express')
const session = require('express-session')
const app = express()
[Link](session({
'secret': '343ji43j4n3jn4jk3n'
}))
After this is done, all the requests to the app routes are now using sessions.
secret is the only required parameter, but there are many more you can use. It should be a
randomly unique string for your application.
The session is attached to the request, so you can access it using [Link] here:
[Link]('/', (req, res, next) => {
// [Link]
13
}
This object can be used to get data out of the session, and also to set data:
[Link] = 'Flavio'
[Link]([Link]) // 'Flavio'
This data is serialized as JSON when stored, so you are safe to use nested objects.
You can use sessions to communicate data to middleware that's executed later, or to retrieve
it later on, on subsequent requests.
Where is the session data stored? It depends on how you set up the express-session module.
It can store session data in:
memory, not meant for production
a database like MySQL or Mongo
a memory cache like Redis or Memcached
There is a big list of 3rd packages that implement a wide variety of different compatible
caching stores in [Link]
All solutions store the session id in a cookie, and keep the data server-side. The client will
receive the session id in a cookie, and will send it along with every HTTP request.
We'll reference that server-side to associate the session id with the data stored locally.
Memory is the default, and it requires no special setup on your part. It's the simplest thing but
it's meant only for development purposes.
The best choice is a memory cache like Redis, for which you need to setup its own
infrastructure.
Another popular package to manage sessions in Express is cookie-session, which has a big
difference: it stores data client-side in the cookie.
I do not recommend doing that because storing data in cookies means that it's stored client-
side, and sent back and forth in every single request made by the user. It's also limited in size,
as it can only store 4 kilobytes of data.
Cookies also need to be secured, but by default they are not, since secure Cookies are
possible on HTTPS sites and you need to configure them if you have proxies.
How to Validate Input in Express
Let's see how to validate any data coming in as input in your Express endpoints.
Say you have a POST endpoint that accepts the name, email, and age parameters:
14
const express = require('express')
const app = express()
[Link]([Link]())
[Link]('/form', (req, res) => {
const name = [Link]
const email = [Link]
const age = [Link]
})
How do you perform server-side validation on those results to make sure that:
name is a string of at least 3 characters?
email is a real email?
age is a number, between 0 and 110?
The best way to handle validation on any kind of input coming from outside in Express is by
using the express-validator package:
npm install express-validator
You require the check and validationResult objects from the package:
const { check, validationResult } = require('express-validator');
We pass an array of check() calls as the second argument of the post() call. Every check() call
accepts the parameter name as argument. Then we call validationResult() to verify there were
no validation errors. If there are any, we tell them to the client:
[Link]('/form', [
check('name').isLength({ min: 3 }),
check('email').isEmail(),
check('age').isNumeric()
], (req, res) => {
const errors = validationResult(req)
if (![Link]()) {
return [Link](422).json({ errors: [Link]() })
}
const name = [Link]
const email = [Link]
const age = [Link]
15
})
Notice I used:
isLength()
isEmail()
isNumeric()
There are many more of these methods, all coming from [Link], including:
contains(), checks if value contains the specified value
equals(), checks if value equals the specified value
isAlpha()
isAlphanumeric()
isAscii()
isBase64()
isBoolean()
isCurrency()
isDecimal()
isEmpty()
isFQDN(), checks if it's a fully qualified domain name
isFloat()
isHash()
isHexColor()
isIP()
isIn(), checks if the value is in an array of allowed values
isInt()
isJSON()
isLatLong()
isLength()
isLowercase()
isMobilePhone()
16
isNumeric()
isPostalCode()
isURL()
isUppercase()
isWhitelisted(), checks the input against a whitelist of allowed characters
You can validate the input against a regular expression using matches().
Dates can be checked using:
isAfter(), checks if the entered date is after the one you pass
isBefore(), checks if the entered date is before the one you pass
isISO8601()
isRFC3339()
For exact details on how to use those validators, refer to the docs here.
All those checks can be combined by piping them:
check('name')
.isAlpha()
.isLength({ min: 10 })
If there is any error, the server automatically sends a response to communicate the error. For
example if the email is not valid, this is what will be returned:
{
"errors": [{
"location": "body",
"msg": "Invalid value",
"param": "email"
}]
}
This default error can be overridden for each check you perform, using withMessage():
check('name')
.isAlpha()
.withMessage('Must be only alphabetical chars')
.isLength({ min: 10 })
.withMessage('Must be at least 10 chars long')
17
What if you want to write your own special, custom validator? You can use the custom
validator.
In the callback function you can reject the validation either by throwing an exception, or by
returning a rejected promise:
[Link]('/form', [
check('name').isLength({ min: 3 }),
check('email').custom(email => {
if (alreadyHaveEmail(email)) {
throw new Error('Email already registered')
}
}),
check('age').isNumeric()
], (req, res) => {
const name = [Link]
const email = [Link]
const age = [Link]
})
The custom validator:
check('email').custom(email => {
if (alreadyHaveEmail(email)) {
throw new Error('Email already registered')
}
})
can be rewritten like this:
check('email').custom(email => {
if (alreadyHaveEmail(email)) {
return [Link]('Email already registered')
}
})
How to Sanitizing Input in Express
You've seen how to validate input that comes from the outside world to your Express app.
There's one thing you quickly learn when you run a public-facing server: never trust the
input.
18
Even if you sanitize and make sure that people can't enter weird things using client-side code,
you'll still be subject to people using tools (even just the browser devtools) to POST directly
to your endpoints.
Or bots trying every possible combination of exploit known to humans.
What you need to do is sanitize your input.
The express-validator package you already use to validate input can also conveniently be
used to perform sanitization.
Say you have a POST endpoint that accepts the name, email, and age parameters:
const express = require('express')
const app = express()
[Link]([Link]())
[Link]('/form', (req, res) => {
const name = [Link]
const email = [Link]
const age = [Link]
})
You might validate it using:
const express = require('express')
const app = express()
[Link]([Link]())
[Link]('/form', [
check('name').isLength({ min: 3 }),
check('email').isEmail(),
check('age').isNumeric()
], (req, res) => {
const name = [Link]
const email = [Link]
const age = [Link]
})
You can add sanitization by piping the sanitization methods after the validation ones:
[Link]('/form', [
check('name').isLength({ min: 3 }).trim().escape(),
check('email').isEmail().normalizeEmail(),
19
check('age').isNumeric().trim().escape()
], (req, res) => {
//...
})
Here I used the methods:
trim() trims characters (whitespace by default) at the beginning and at the end of a
string
escape() replaces <, >, &, ', " and / with their corresponding HTML entities
normalizeEmail() canonicalizes an email address. Accepts several options to
lowercase email addresses or subaddresses (for example
flavio+newsletters@[Link])
Other sanitization methods:
blacklist() removes characters that appear in the blacklist
whitelist() removes characters that do not appear in the whitelist
unescape() replaces HTML encoded entities with <, >, &, ', " and /
ltrim() like trim(), but only trims characters at the start of the string
rtrim() like trim(), but only trims characters at the end of the string
stripLow() removes ASCII control characters, which are normally invisible
Force conversion to a format:
toBoolean() converts the input string to a boolean. Everything except for '0', 'false'
and '' returns true. In strict mode only '1' and 'true' return true.
toDate() converts the input string to a date, or null if the input is not a date
toFloat() converts the input string to a float, or NaN if the input is not a float
toInt() converts the input string to an integer, or NaN if the input is not an integer
Like with custom validators, you can create a custom sanitizer.
In the callback function you just return the sanitized value:
const sanitizeValue = value => {
//sanitize...
}
[Link]('/form', [
check('value').customSanitizer(value => {
return sanitizeValue(value)
20
}),
], (req, res) => {
const value = [Link]
})
How to Handle Forms in Express
This is an example of an HTML form:
<form method="POST" action="/submit-form">
<input type="text" name="username" />
<input type="submit" />
</form>
When the user presses the submit button, the browser will automatically make a POST
request to the /submit-form URL on the same origin of the page. The browser sends the data
contained, encoded as application/x-www-form-urlencoded. In this particular example, the
form data contains the username input field value.
Forms can also send data using the GET method, but the vast majority of the forms you'll
build will use POST.
The form data will be sent in the POST request body.
To extract it, you will need to use the [Link]() middleware:
const express = require('express')
const app = express()
[Link]([Link]({
extended: true
}))
Now, you need to create a POST endpoint on the /submit-form route, and any data will be
available on [Link]:
[Link]('/submit-form', (req, res) => {
const username = [Link]
//...
[Link]()
})
Don't forget to validate the data before using it with express-validator.
21
How to Handle File Uploads in Forms in Express
This is an example of an HTML form that allows a user to upload a file:
<form method="POST" action="/submit-form" enctype="multipart/form-data">
<input type="file" name="document" />
<input type="submit" />
</form>
Don't forget to add enctype="multipart/form-data" to the form, or files won't be uploaded.
When the user press the submit button, the browser will automatically make a POST request
to the /submit-form URL on the same origin of the page. The browser sends the data
contained, not encoded as as a normal form application/x-www-form-urlencoded, but as
multipart/form-data.
On the server-side, handling multipart data can be tricky and error prone, so we are going to
use a utility library called formidable. Here's the GitHub repo – it has over 4000 stars and is
well-maintained.
You can install it using:
npm install formidable
Then include it in your [Link] file:
const express = require('express')
const app = express()
const formidable = require('formidable')
Now, in the POST endpoint on the /submit-form route, we instantiate a new Formidable form
using [Link]():
[Link]('/submit-form', (req, res) => {
new [Link]()
})
After doing so, we need to be able to parse the form. We can do so synchronously by
providing a callback, which means all files are processed. Once formidable is done, it makes
them available:
[Link]('/submit-form', (req, res) => {
new [Link]().parse(req, (err, fields, files) => {
if (err) {
[Link]('Error', err)
throw err
22
}
[Link]('Fields', fields)
[Link]('Files', files)
for (const file of [Link](files)) {
[Link](file)
}
})
})
Or, you can use events instead of a callback. For example, you can be notified when each file
is parsed, or other events such as completion of file processing, receiving a non-file field, or
if an error occurred:
[Link]('/submit-form', (req, res) => {
new [Link]().parse(req)
.on('field', (name, field) => {
[Link]('Field', name, field)
})
.on('file', (name, file) => {
[Link]('Uploaded file', name, file)
})
.on('aborted', () => {
[Link]('Request aborted by the user')
})
.on('error', (err) => {
[Link]('Error', err)
throw err
})
.on('end', () => {
[Link]()
})
})
Whichever way you choose, you'll get one or more [Link] objects, which give you
information about the file uploaded. These are some of the methods you can call:
[Link], the file size in bytes
[Link], the path the file is written to
[Link], the name of the file
[Link], the MIME type of the file
23
The path defaults to the temporary folder and can be modified if you listen for the fileBegin
event:
[Link]('/submit-form', (req, res) => {
new [Link]().parse(req)
.on('fileBegin', (name, file) => {
[Link] = __dirname + '/uploads/' + [Link]
})
.on('file', (name, file) => {
[Link]('Uploaded file', name, file)
})
//...
})
24