Express.js is among the most popular Node.js frameworks, and new versions are frequently published to upgrade or add new functionality. Some past releases have minimized the framework’s memory footprint, improved page load speeds, added new APIs and framework integrations, ensured code maintainability, and provided a better shorthand technique for calling and using functions.
The newly released Express.js v5 includes a slew of new features and enhancements aimed at improving the framework’s efficiency. However, this update may cause problems or generate deprecation warnings, making the migration a little unnerving.
In this tutorial, we’ll demonstrate how to upgrade from Express v4 to Express v5. We’ll also take a look at the upgrades and new features available in Express 5, and we’ll see how to build a web app in Express 5.
Let’s get going!
Jump ahead:
Express is a Node.js backend web application framework released under the MIT License as free and open source software. This popular Node web framework serves as the foundation for many other Node web frameworks, with a mechanism to:
TJ Holowaychuk, the original author of Express, referred to it as a Sinatra-inspired server, a reference to its simplicity and to its numerous functionalities that are available as plugins.
Express, along with the MongoDB database software as well as a JavaScript frontend framework or library, is the backend component of makes up popular tech stacks such as MEAN, MERN, and MEVN.
Express 5 is still in beta, but as I mentioned earlier, it offers several updates over Express 4. These improvements are intended to increase the efficiency of the framework. The most significant changes relate to path route matching syntax. Let’s take a closer look.
In Express 5, several changes have been made to the way a path string is matched to an incoming request:
?
, *
, and +
req.params
/(d+)
replaces /\\d+
(.*)
replaces (*)
*
path segment behavior: this has been removed; /id/*/user
will match a literal *
as the middle segmentThe string passed as the first input to the app.all()
, app.use()
, app.METHOD()
, router.all()
, router.METHOD()
, or router.use()
APIs is referred to as the path route matching syntax.
In Express 4, users rely on packages like express-async-handler
or write their helper functions to catch rejections of handlers that return promises.
However, in Express 5, any rejected promise or error thrown from middleware or handlers is forwarded as an error to the error handling middleware. This helps prevent the app from crashing.
This makes it easier to use async functions as middleware and handlers. When an async function throws an error or a rejected promise is awaited inside an async function, the error is sent to the error handler by calling the next (err)
function.
app.use(async (req, res, next) => { throw new Error("Something went wrong"); });
The above is equivalent to returning a Promise explicitly from a middleware or route handler in Express 4:
app.use((req, res, next) => { return new Promise((resolve, reject) => { reject(new Error("Something went wrong")); }); });
app.router
objectExpress 5 has brought back the app.router
object, which was removed in Express 4. Unlike Express 3, in which an app was required to explicitly load the object, this object in Express 5 is essentially a reference to the underlying Express router.
req.host
objectExpress 5 maintains the port number in the req.host
object. Previously, if a port number was provided, the req.host
method in Express 4 improperly removed the number.
req.query
object changed to getterThe req.query
property in Express 5 has been changed from a writable property to a getter. Additionally, the default query parser has been renamed “simple” from “extended”.
Except for the updates mentioned previously, Express 5 is very similar to Express 4. The API hasn’t changed nearly as much as it did from v3 to v4. However, there are certain breaking changes, even though the underlying API has remained the same. In other words, updating an old Express 4 program to use Express 5 may cause it to fail.
Let’s take a closer look at the breaking changes:
app.del()
: this feature is no longer supported in Express 5, and an error will be thrown if you use this function
app.delete()
function in place of app.del()
; delete
is a reserved keyword in JavaScript, so del
was initially used instead of delete
. In ECMAScript 6, delete
and other reserved keywords can now be properly used as property names:
app.del(‘/user’, (req, res) => res.send(‘user deleted’)); // Will give deprecation warning in Express v4 // Will throw TypeError in Express v5
app.param(name, fn)
: this function has been depreciated since v4.11.0, and is not supported by Express 5; its functionality was changed using the app.param(fn)
signaturereq.acceptsCharset()
, req.acceptsEncoding()
, req.acceptsLanguage()
have been pluralized is Express 5
req.acceptsCharsets()
, req.acceptsEncodings()
, and req.acceptsLanguages()
req.param(name)
: it’s not possible to retrieve form data with this function in Express 5
req.params
, req.body
, or req.query
object as shown in the following example:
app.get(‘/user/:id’, (req, res) => { res.send(User.find(req.param(‘id’))); }); // Will give deprecation warning in Express v4 // Will throw TypeError in express v5 "req.param is not a function" // Do this instead in v5 app.get(‘/user/:id’, (req, res) => { res.send(User.find(req.params.id)); });
res.json
: this signature is not supported by Express 5. (obj, status).
res.status(status).json(obj
syntax to change the status and then chain it to the res.json()
method, like so:
// Version 4 app.get(‘/user/:id’, (req, res) => { res.json(200, {user: User.find(req.param.id)}); }); // Version 5 app.get(‘/user/:id’, (req, res) => { res.status(200).json({ user: User.find(req.param.id) }); });
res.send(obj, status)
: this signature is not supported in Express 5
res.status(status).send()
to set the status and then chain it to the res.send()
method, like so:
// Version 4 app.get(‘/user/:id’, (req, res) => { res.send(200, {user: User.find(req.param.id)}); }); // Version 5 app.get(‘/user/:id’, (req, res) => { res.status(200).send({ user: User.find(req.params.id) }); });
res.send(status)
: this signature, where status
is an integer, is not supported in Express 5
res.sendStatus(statusCode)
function, which delivers the text version of the HTTP response header status code, such as “Not Found” or “Internal Server Error”; if you need to send a number using the res.send()
function, use quote marks to convert it to a string – otherwise, Express 5 may think you’re trying to use an unsupported old signature
// Old method res.send(status) // Version 5 res.sendStatus(statusCode)
Migrating your Express 4 application to Express 5 is not as big of a lift as you may think.
To get started, move into the root directory of your project and install Express 5 using the following command:
npm install "express@>=5.0.0-beta.1" --save
This command will update your project dependencies and also update the package.json
file.
Next, rebuild the project or rerun your tests to determine if anything failed. Use the updates covered in this tutorial to fix any issues that may have occurred.
If you’d like to update any other npm packages in the project, run the npm outdated
command. This will enable you to see other packages in your project that need to be updated. Then, update them using the npm update
command.
To demonstrate what we’ve learned so far about Express 5, let’s create a demo todo application using the Express 5 syntax.
To get started, create a new folder for the application, like so:
mkdir express-5-demo && cd express-5-demo
Then, initialize a new Node.js project:
npm init -y
The above command will generate a package.json
file. Open the file and add a start script:
{ ... "start": "node index.js" }
Now, install Express 5 and other required packages for this application with the following command:
npm i [email protected] body-parser
Wait for the installation to finish before continuing. Once you’re certain that the installation is complete, create an index.js
file in the project’s root directory, and set up a basic Express server using the following code snippet:
const express = require("express"); const app = express(); app.listen(3000, ()=>console.log("Express server running on port 3000"));
With the Express server configured, let’s create some basic routes for the application. Update the index.js
file with the following code snippets:
const todos = [{ id: 1, title: "Learn Node.js", completed: true }, { id: 2, title: "Learn React.js", completed: false }, { id: 3, title: "Learn Angular.js", completed: false } ] app.get("/todos", (req, res) => { res.status(200).json(todos); }); app.post("/todos", (req, res) => { const todo = req.body; console.log(req.body); todos.push(todo); res.status(200).json(todos); }); app.get("/todos/:id", (req, res) => { const id = parseInt(req.params.id); const todo = todos.find(todo => todo.id === id); res.status(200).json(todo); }); app.put("/todos/:id", (req, res) => { const id = parseInt(req.params.id); const todo = todos.find(todo => todo.id === id); todo.completed = !todo.completed; res.status(200).json(todos); }); app.delete("/todos/:id", (req, res) => { const id = parseInt(req.params.id); const todo = todos.find(todo => todo.id === id); todos.splice(todos.indexOf(todo), 1); res.status(200).json(todos); });
In the above code, we created a todos
array with some dummy data. Then, we defined four API routes to get, add, update, and delete a todo
from the application.
To allow users to create a new todo
, we need to define a middleware for the body-parser package we just installed. Add the following code before the todos
array:
app.use(bodyParser.json());
We’ve completed the setup for our routes, so let’s create another router and middleware to serve up an HTML page to display the todos to the users.
First, create a new folder and name it public
. Then, create a new file called index.html
. Open the index.html
file and add the following code snippet:
<html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Todos</title> </head> <body> <div> <h3>Todos</h3> <ul id="data"></ul> </div> </body> </html> <style> #data{ list-style: none; padding: 0; } #data li{ display: inline-block; margin: 10px; padding: 10px; border: 1px solid #ccc; border-radius: 5px; } </style> <script src="app.js"></script>
In the above code, we created an HTML document to display the todos. The todos will be displayed in the <ul id="data"></ul>
section of the page, which we’ll handle with JavaScript. We also defined some styling and referenced the JavaScript file which we’ll create shortly to get all the todos.
Now create an app.js
file in the public folder and add the following code:
const Tododata = document.getElementById('data'); fetch('http://localhost:3001/todos', { method: 'GET', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' } }) .then(res => res.json()) .then(data => { if(data){ data.forEach(element => { const li = document.createElement('li'); li.innerHTML = element.title; Tododata.appendChild(li); }); } }) .catch(err => console.log(err));
Here we send a request to the backend, using the JavaScript fetch
API, to check if a todo
exists in the todos
array. Then we create a list element to display the details of the todos.
For the page to render, we need to add one more configuration to the root index.js
file. Add the code below after const app = express();
app.use(express.static(__dirname + "/public"));
Now we’ll be able to render all the static files in the public
directory.
To test the application, run the following command to start the Express server:
npm start
Then, navigate in your browser to localhost:3000
, and you should see the following output:
In this article, we explored the new features and improvements available in Express.js v5 and reviewed how to migrate an existing Express 4 app to Express 5. To demonstrate how to take advantage of the new functionalities available in version 5, we created a step-by-step demo showing how to create a web app in Express 5.
What are your thoughts on Express 4 vs. Express 5? Do you plan to migrate to Express 5 in your next project? For additional information about Express 5, check out the official documentation.
Deploying a Node-based web app or website is the easy part. Making sure your Node instance continues to serve resources to your app is where things get tougher. If you’re interested in ensuring requests to the backend or third-party services are successful, try LogRocket.
LogRocket is like a DVR for web and mobile apps, recording literally everything that happens while a user interacts with your app. Instead of guessing why problems happen, you can aggregate and report on problematic network requests to quickly understand the root cause.
LogRocket instruments your app to record baseline performance timings such as page load time, time to first byte, slow network requests, and also logs Redux, NgRx, and Vuex actions/state. Start monitoring for free.
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to implement one-way and two-way data binding in Vue.js, using v-model and advanced techniques like defineModel for better apps.
Compare Prisma and Drizzle ORMs to learn their differences, strengths, and weaknesses for data access and migrations.
It’s easy for devs to default to JavaScript to fix every problem. Let’s use the RoLP to find simpler alternatives with HTML and CSS.
Learn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.