-
Notifications
You must be signed in to change notification settings - Fork 22.8k
Update Express tutorial to v5 #40247
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
@@ -463,27 +462,8 @@ In order to use these you have to first install the database driver using npm. F | |||
npm install mongodb | |||
``` | |||
|
|||
The database itself can be installed locally or on a cloud server. In your Express code you require the driver, connect to the database, and then perform create, read, update, and delete (CRUD) operations. The example below (from the Express documentation) shows how you can find "mammal" records using MongoDB. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI This was example for a very old version of Mongoose. Removing info.
@@ -674,7 +674,7 @@ npm install mongoose | |||
|
|||
## Connect to MongoDB | |||
|
|||
Open **/app.js** (in the root of your project) and copy the following text below where you declare the _Express application object_ (after the line `const app = express();`). | |||
Open **bin/www** (from the root of your project) and copy the following text below where you set the port (after the line `app.set("port", port);`). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI moved the DB connection code to the app entry point called by the server to start everything. Makes no difference to the app, but decoupling means that we can use a different db for tests. More work needs to be done in later docs, as we do things to update this URL, and they will currently suggest doing it in app.js
files/en-us/learn_web_development/extensions/server-side/express_nodejs/routes/index.md
Outdated
Show resolved
Hide resolved
files/en-us/learn_web_development/extensions/server-side/express_nodejs/routes/index.md
Show resolved
Hide resolved
67404e6
to
f58bc20
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@hamishwillee sterling work, sir. I've read through all the changes and made a few suggestions, but nothing major.
A couple of questions:
- Do you need me to run through it all and make sure it all works, or have you already done this with confidence? I'm assuming you have, but happy to help with this if not.
- I've not checked in detail whether you've missed any instances of mass replaced terms such as 4x -> 5x, but I'm assuming you've grepped for these?
``` | ||
|
||
The approach is exactly the same as described for the [Genre detail page](/en-US/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/Displaying_data/Genre_detail_page). | ||
The route controller function uses `Promise.all()` to query the specified `Author` and their associated `Book` instances in parallel. | ||
If no matching author is found an Error object is sent to the Express error handling middleware. | ||
If no matching author is found an `Error` object is sent to the Express error handling middleware. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If no matching author is found an `Error` object is sent to the Express error handling middleware. | |
If no matching author is found, an `Error` object is sent to the Express error handling middleware. |
``` | ||
|
||
> [!NOTE] | ||
> We don't need to require any additional modules in this step, as we already imported the dependencies when we implemented the home page controller. | ||
|
||
The approach is exactly the same as described for the [Genre detail page](/en-US/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/Displaying_data/Genre_detail_page). | ||
The route controller function uses `Promise.all()` to query the specified `Book` and its associated copies (`BookInstance`) in parallel. | ||
If no matching book is found an Error object is returned with a "404: Not Found" error. | ||
If no matching book is found an `Error` object is returned with a "404: Not Found" error. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If no matching book is found an `Error` object is returned with a "404: Not Found" error. | |
If no matching book is found, an `Error` object is returned with a "404: Not Found" error. |
@@ -52,7 +52,7 @@ If the genre does not exist in the database (i.e., it may have been deleted) the | |||
In this case we want to display a "not found" page, so we create an `Error` object and pass it to the `next` middleware function in the chain. | |||
|
|||
> [!NOTE] | |||
> Errors passed to the `next` middleware function propagate through to our error handling code (this was set up when we [generated the app skeleton](/en-US/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/skeleton_website#app.js) - for more information see [Handling Errors](/en-US/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/Introduction#handling_errors)). | |||
> Errors passed to the `next` middleware function propagate through to our error handling code (this was set up when we [generated the app skeleton](/en-US/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/skeleton_website#app.js) — for more information see [Handling Errors](/en-US/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/Introduction#handling_errors) and [Handling errors and exceptions in the route functions](/en-US/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/routes#handling_errors_and_exceptions_in_the_route_functions)). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
> Errors passed to the `next` middleware function propagate through to our error handling code (this was set up when we [generated the app skeleton](/en-US/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/skeleton_website#app.js) — for more information see [Handling Errors](/en-US/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/Introduction#handling_errors) and [Handling errors and exceptions in the route functions](/en-US/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/routes#handling_errors_and_exceptions_in_the_route_functions)). | |
> Errors passed to the `next` middleware function propagate through to our error handling code (this was set up when we [generated the app skeleton](/en-US/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/skeleton_website#app.js). For more information, see [Handling Errors](/en-US/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/Introduction#handling_errors) and [Handling errors and exceptions in the route functions](/en-US/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/routes#handling_errors_and_exceptions_in_the_route_functions)). |
@@ -48,7 +48,7 @@ It `awaits` on the promise returned by `Promise.all()` to get the specified `Boo | |||
When the operations complete the function checks whether any books were found, and if none were found sends an error "Book not found" to the error handling middleware. | |||
|
|||
> [!NOTE] | |||
> Not finding any book results is **not an error** for a search — but it is for this application because we know there must be a matching book record! The code above compares for (`book===null`) in the callback, but it could equally well have daisy chained the method [orFail()](<https://mongoosejs.com/docs/api/query.html#Query.prototype.orFail()>) to the query. | |||
> Not finding any book results is **not an error** for a search — but it is for this application because we know there must be a matching book record! The code above compares for (`book===null`) in the callback, but it could equally well have daisy chained the method [`orFail()`](<https://mongoosejs.com/docs/api/query.html#Query.prototype.orFail()>) to the query. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
> Not finding any book results is **not an error** for a search — but it is for this application because we know there must be a matching book record! The code above compares for (`book===null`) in the callback, but it could equally well have daisy chained the method [`orFail()`](<https://mongoosejs.com/docs/api/query.html#Query.prototype.orFail()>) to the query. | |
> Not finding any book results is **not an error** for a search, but it is for this application because we know there must be a matching book record! The code above tests for (`book===null`) in the callback, but it could equally well have daisy-chained the method [`orFail()`](<https://mongoosejs.com/docs/api/query.html#Query.prototype.orFail()>) to the query. |
"compares for" sounded a bit odd to me.
@@ -249,16 +245,19 @@ setTimeout(() => { | |||
console.log("Second"); | |||
``` | |||
|
|||
Using non-blocking asynchronous APIs is even more important on Node than in the browser because _Node_ is a single-threaded event-driven execution environment. "Single threaded" means that all requests to the server are run on the same thread (rather than being spawned off into separate processes). This model is extremely efficient in terms of speed and server resources, but it does mean that if any of your functions call synchronous methods that take a long time to complete, they will block not just the current request, but every other request being handled by your web application. | |||
Using non-blocking asynchronous APIs is even more important on Node than in the browser because _Node_ applications are often written as a single-threaded event-driven execution environment. "Single threaded" means that all requests to the server are run on the same thread (rather than being spawned off into separate processes). This model is extremely efficient in terms of speed and server resources, but it does mean that if any of your functions call synchronous methods that take a long time to complete, they will block not just the current request, but every other request being handled by your web application. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using non-blocking asynchronous APIs is even more important on Node than in the browser because _Node_ applications are often written as a single-threaded event-driven execution environment. "Single threaded" means that all requests to the server are run on the same thread (rather than being spawned off into separate processes). This model is extremely efficient in terms of speed and server resources, but it does mean that if any of your functions call synchronous methods that take a long time to complete, they will block not just the current request, but every other request being handled by your web application. | |
Using non-blocking asynchronous APIs is even more important on Node than in the browser because _Node_ applications are often written as a single-threaded event-driven execution environment. "Single threaded" means that all requests to the server are run on the same thread (rather than being spawned off into separate processes). This model is extremely efficient in terms of speed and server resources. However, it does mean that if any of your functions call synchronous methods that take a long time to complete, they will block not only the current request, but every other request being handled by your web application. |
app.get("/", (req, res, next) => { | ||
setTimeout(() => { | ||
try { | ||
throw new Error("AsynchronousException"); // You must catch and propagate yourself. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
throw new Error("AsynchronousException"); // You must catch and propagate yourself. | |
// You must catch and propagate this error yourself | |
throw new Error("AsynchronousException"); |
router.get("/about", (req, res, next) => { | ||
About.find({}).exec((err, queryResults) => { | ||
if (err) { | ||
return next(err); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return next(err); | |
// Propagate the error | |
return next(err); |
@@ -112,7 +108,7 @@ Generally speaking, you should select a templating engine that delivers all the | |||
> [!NOTE] | |||
> There are many resources on the Internet to help you compare the different options! | |||
|
|||
For this project, we'll use the [Pug](https://pugjs.org/api/getting-started.html) templating engine (this is the recently-renamed Jade engine), as this is one of the most popular Express/JavaScript templating languages and is supported out of the box by the generator. | |||
For this project, we'll use the [Pug](https://pugjs.org/api/getting-started.html) templating engine (this used to be called "Jade"), as this is one of the most popular Express/JavaScript templating languages and is supported out of the box by the generator. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For this project, we'll use the [Pug](https://pugjs.org/api/getting-started.html) templating engine (this used to be called "Jade"), as this is one of the most popular Express/JavaScript templating languages and is supported out of the box by the generator. | |
For this project, we'll use the [Pug](https://pugjs.org/api/getting-started.html) templating engine (previously called "Jade"), as this is one of the most popular Express/JavaScript templating languages and is supported out of the box by the generator. |
} | ||
``` | ||
|
||
Because the tool isn't installed globally we can't launch it from the command line (unless we add it to the path) but we can call it from an npm script because npm knows all about the installed packages. Find the `scripts` section of your package.json. Initially, it will contain one line, which begins with `"start"`. Update it by putting a comma at the end of that line, and adding the `"devstart"` and `"serverstart"` lines: | ||
Because the tool isn't installed globally we can't launch it from the command line (unless we add it to the path) but we can call it from an npm script because npm knows all about the installed packages. Find the `scripts` section of your **package.json**. Initially, it will contain one line, which begins with `"start"`. Update it by putting a comma at the end of that line, and adding the `"devstart"` and `"serverstart"` lines: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because the tool isn't installed globally we can't launch it from the command line (unless we add it to the path) but we can call it from an npm script because npm knows all about the installed packages. Find the `scripts` section of your **package.json**. Initially, it will contain one line, which begins with `"start"`. Update it by putting a comma at the end of that line, and adding the `"devstart"` and `"serverstart"` lines: | |
Because the tool isn't installed globally, we can't launch it from the command line (unless we add it to the path). However, we can call it from an npm script because npm knows which packages are installed. Find the `scripts` section of your **package.json**. Initially, it will contain one line, which begins with `"start"`. Update it by putting a comma at the end of that line, and adding the `"devstart"` and `"serverstart"` lines: |
@@ -345,11 +342,12 @@ The **package.json** file defines the application dependencies and other informa | |||
"debug": "~2.6.9", | |||
"express": "~4.16.1", | |||
"http-errors": "~1.6.3", | |||
"jade": "~1.11.0", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do we need a jade dependency if it is now called pug?
This pull request has merge conflicts that must be resolved before it can be merged. |
This updates the Express Tutorial to v5
I wrote some tests in mdn/express-locallibrary-tutorial#327, then updated Express to v5 along with all dependencies - everything passed. Then ran all the code mods (
npx @expressjs/codemod upgrade
) suggested in the migration guide - nothing was changed. So I don't think I was using anything impacted by a changed or removed API.I have updated the docs now. Mostly this was removing code to do async error handling in routes using
asyncHandler
since Express now handles that automatically. Also updated the Express sections on handling errors so this is more clear.The route path handling is also different, so I had to update that section - it wasn't anything I was using in the demo, but it was significant for readers.
I also updated the database connection to to be done in
bin/www
since that allows for testing.The way it was done in the app.js means that tests can't work (because you can't have multiple connections, and if you do this in the app there is already a connection by the time you get to the test runner).
Last of all I went through the lot and checked all the docs and versions of libraries. I did not retest the deployment though (mostly because I did that fairly recently).
Actions:
Fixes #38922