feat(curriculum): add express error handling lessons (#67192)

Signed-off-by: Kolade Chris <65571316+Ksound22@users.noreply.github.com>
Co-authored-by: majestic-owl448 <26656284+majestic-owl448@users.noreply.github.com>
Co-authored-by: Naomi Carrigan <naomi@freecodecamp.org>
This commit is contained in:
Kolade Chris
2026-05-17 08:17:24 +01:00
committed by GitHub
parent ad0ee4d276
commit ff00be8d62
7 changed files with 878 additions and 1 deletions
+6
View File
@@ -7239,6 +7239,12 @@
"In these lessons, you will learn about routing in ExpressJS, which is how you define the different endpoints of your web application and how they respond to client requests." "In these lessons, you will learn about routing in ExpressJS, which is how you define the different endpoints of your web application and how they respond to client requests."
] ]
}, },
"lecture-understanding-error-handling-and-health-checks": {
"title": "Understanding Error Handling and Health Checks",
"intro": [
"In these lessons, you'll learn about error handling and health checks in Express."
]
},
"exam-back-end-development-and-apis-certification": { "exam-back-end-development-and-apis-certification": {
"title": "Back-End Development and APIs Certification Exam", "title": "Back-End Development and APIs Certification Exam",
"intro": [ "intro": [
@@ -0,0 +1,190 @@
---
id: 69f303a6812a8627022f9592
title: What are HTTP Response Status Codes?
challengeType: 19
dashedName: what-are-http-response-status-codes
---
# --description--
Whenever you visit a website or use an API, your browser and the server are constantly talking to each other.
One of the most important parts of that conversation? HTTP response status codes.
They're short numeric messages — like 200, 404, or 500 — that tell the client (the end user's device such as a laptop or phone) what happened after a request was made.
You've learned some of these already, but let's break them down by category so you can recall exactly what they mean and when to use them.
## 1xx Informational Responses
Codes that start with 1 are rarely seen in everyday development, but they indicate that the request was received and the process is continuing like when uploading a file.
Common example:
* 100 Continue The server tells the client, "Okay, you can send the rest of the request."
* 101 Switching Protocols The client sent an `Upgrade` header so the server is switching protocols. You'll see this most commonly when using Websockets.
## 2xx Success Responses
These mean the request was successfully received, understood, and processed.
* 200 OK The request succeeded. This is the most common success response.
* 201 Created A new resource was successfully created, for example, after registering a new user.
* 204 No Content The request succeeded, but there's no response body to send back. Often used for `DELETE` requests.
## 3xx Redirection Messages
These tell the client: "The resource you're looking for is somewhere else."
* 301 Moved Permanently The resource has a new permanent URL.
* 302 Found The resource is temporarily at a different URL.
* 304 Not Modified Used for caching. It tells the client nothing has changed, so it can use the cached version.
## 4xx Client Errors
These happen when the client sends an invalid request.
* 400 Bad Request The request is malformed or invalid.
* 401 Unauthorized Authentication is required (you need to log in).
* 403 Forbidden Authentication succeeded, but you don't have permission.
* 404 Not Found The requested resource does not exist. And yes, this is the most iconic error on the web.
## 5xx Server Errors
These mean something went wrong on the server side.
* 500 Internal Server Error A generic "something went wrong" message.
* 502 Bad Gateway A server acting as a proxy received an invalid response from another server.
* 503 Service Unavailable The server is temporarily overloaded or down for maintenance.
So now that you're familiar with some of the most common HTTP status codes, let's look at some practical examples in Express.
Let's say you're building an API. If a user sends invalid data, you can respond with a 400 status code, which means "Bad Request". You can also send the string "Bad Request" back to the client so it's clear what the 400 response code means:
```js
res.status(400).send("Bad Request");
```
If everything goes well, you can send a 200 response. You can include a message if you'd like. Here we're sending some JSON with a simple "Success" message:
```js
res.status(200).json({ message: "Success" });
```
And if something crashes on the server, you can send a 500 status code which means an internal server error occurred, along with a clear message about the error:
```js
res.status(500).send("Internal Server Error");
```
These status codes aren't just for machines. They help developers debug issues and help users (and other developers) understand what's going on.
# --questions--
## --text--
What does the status code 200 mean?
## --answers--
Page not found
### --feedback--
Think of the most common "everything went fine" message.
---
Success
---
Server error
### --feedback--
Think of the most common "everything went fine" message.
---
Resource moved
### --feedback--
Think of the most common "everything went fine" message.
## --video-solution--
2
## --text--
What's the difference between 401 and 403?
## --answers--
401 means not found, 403 means redirected
### --feedback--
One is about not being logged in, the other is about not having access.
---
401 means unauthorized, 403 means forbidden
---
401 means success, 403 means error
### --feedback--
One is about not being logged in, the other is about not having access.
---
They are the same
### --feedback--
One is about not being logged in, the other is about not having access.
## --video-solution--
2
## --text--
Which status code would you use when a resource was successfully created?
## --answers--
200
### --feedback--
Think of when you `POST` new data to the server.
---
201
---
404
### --feedback--
Think of when you `POST` new data to the server.
---
301
### --feedback--
Think of when you `POST` new data to the server.
## --video-solution--
2
@@ -0,0 +1,257 @@
---
id: 69f3046f4c0e76cbdd24858e
title: How Does Error Handling Work In Express?
challengeType: 19
dashedName: how-does-error-handling-work-in-express
---
# --description--
Have you ever wondered what happens when something goes wrong in your Express app?
What if a database connection fails? Or a user visits a route that doesn't exist?
Understanding how error handling works in Express is critical if you want to build secure, reliable web applications.
In this lesson, we'll walk through how Express handles errors, and how you can catch and respond to them using real code examples.
## How to Handle Simple Errors in Express
In Express, error handling is built on middleware.
Just like regular middleware functions, error-handling middleware has access to `req`, `res`, and `next`. But it includes one extra parameter at the beginning: `err`.
Here's what the syntax looks like:
```js
app.use((err, req, res, next) => {
console.error(err.message);
res.status(500).send("Internal Server Error");
});
```
You define this middleware at the end of your middleware stack.
Express knows it's an error handler because it has four parameters — the new one, `err`, in addition to the typical `req`, `res`, and `next` parameters for other middleware functions.
In a coming lesson, you will learn more about error-handling middleware and how it fits into the broader Express middleware stack.
## How to Handle Asynchronous Errors
Handling async errors depends on which version of Express you're using.
Note that, whichever version of Express you're using, you'll still need to define your error-handling middleware at the end of your stack, after all of your routes.
Here's the error handler again:
```js
// App routes
// ...
// Error-handling middleware
app.use((err, req, res, next) => {
console.error(err.message);
res.status(500).send("Internal Server Error");
});
```
## Express 4
In Express 4 and below, which many projects still use, async errors are **not automatically caught**.
If you're using `async`/`await` and something throws inside an `async` route handler, Express will not catch it unless you manually pass it to `next()`.
Here's how you handle async errors properly in Express 4:
```js
app.get("/user", async (req, res, next) => {
try {
const user = await getUserFromDatabase();
res.send(user);
} catch (err) {
next(err); // Manually pass the error to the error handler
}
});
```
If you forget the `try/catch` block and `next(err)`, the error may:
* Crash your app
* Or result in an unhandled promise rejection warning
That's why wrapping asynchronous logic in `try`/`catch` is essential in Express 4.
Some developers even create a helper function to avoid repeating `try/catch` everywhere:
```js
function asyncHandler(fn) {
return function (req, res, next) {
Promise.resolve(fn(req, res, next)).catch(next);
};
}
```
Then use it like this:
```js
app.get("/user", asyncHandler(async (req, res) => {
const user = await getUserFromDatabase();
res.send(user);
}));
```
This makes it so you don't need to use `try/catch` blocks in all of your `async` routes while still forwarding errors properly.
## Express 5
The current version of Express improves this behavior.
As of Express 5, if an `async` route throws an error or returns a rejected promise, Express automatically forwards it to the error-handling middleware.
Which means that this example handles errors without a `try/catch`:
```js
app.get("/user", async (req, res) => {
const user = await getUserFromDatabase();
res.send(user);
});
```
If `getUserFromDatabase()` throws an error, Express 5 will catch it and pass it to your error handler automatically.
No manual `next(err)` call required.
## How to Handle 404 Errors
404 errors are a little different. They happen when no route matches the request.
You can handle them with this middleware:
```js
app.use((req, res, next) => {
res.status(404).send("Sorry, that route does not exist.");
});
```
**Important**: This should go after your routes, but before your error-handling middleware.
And finally, here are some best practices for error handling in Express:
* Always place your error-handling middleware last.
* Handle 404 errors separately.
* Use `next(err)` to bubble up errors from routes or `async` logic in Express 4 and below.
In production, don't send stack traces to users. Log the details internally and return a user-friendly message instead.
Error handling in Express is all about setting up middleware that listens for problems and responds in a clear, safe way. Whether it's a bug in your code or a missing route, your app should never crash without a backup plan.
# --questions--
## --text--
What makes Express treat a middleware function as an error handler?
## --answers--
It includes the `req`, `res`, and `next` parameters.
### --feedback--
It's all about the number of parameters.
---
It starts with the `err` parameter.
---
It runs first in the middleware stack.
### --feedback--
It's all about the number of parameters.
---
It is placed at the top of the file.
### --feedback--
It's all about the number of parameters.
## --video-solution--
2
## --text--
When using Express 4 or lower, what should you do to handle errors in `async` route handlers?
## --answers--
Throw an error directly.
### --feedback--
Express 4 doesn't catch `async` errors by default.
---
Use a `try/catch` and call `next(err)`.
---
Use `setTimeout`.
### --feedback--
Express 4 doesn't catch async `errors` by default.
---
Handle errors after the response is sent.
### --feedback--
Express 4 doesn't catch `async` errors by default.
## --video-solution--
2
## --text--
Where should you place error-handling middleware in your Express app?
## --answers--
At the very top.
### --feedback--
Express only checks for it after all routes.
---
Right after route definitions.
### --feedback--
Express only checks for it after all routes.
---
At the end of the middleware stack.
---
Before the route handlers.
### --feedback--
Express only checks for it after all routes.
## --video-solution--
3
@@ -0,0 +1,196 @@
---
id: 69f3046f4c0e76cbdd24858f
title: How Does Debugging and Logging Work in Express?
challengeType: 19
dashedName: how-does-debugging-and-logging-work-in-express
---
# --description--
Have you ever had your Express app break with no clear error message? Debugging and logging can help you see exactly what's happening behind the scenes, so you can find and fix issues faster.
In this lesson, we'll explore how Express handles debugging and logging—and how you can use it to improve your development workflow.
## Built-In Debugging with Express
Express already includes a debugging tool called `debug`. You don't need to install anything to use it.
To turn on debugging, all you need to do is set the `DEBUG` environment variable when you run your app.
Here's an example:
```js
DEBUG=express:* node index.js
```
This command enables all debugging messages under the `express:*` namespace. You'll start seeing output like:
```js
express:router:route dispatching GET /
express:router:layer processing GET /
```
These messages help you trace route dispatches, middleware calls, and more. It's a great way to understand the flow of requests in your app.
Want to be more specific? You can narrow down the namespace:
```js
DEBUG=express:router node index.js
```
This will only show logs related to Express's router logic.
On Windows, the syntax is slightly different for PowerShell:
```js
$env:DEBUG = "express:*"; node index.js
```
And for CMD, it looks like this:
```js
set DEBUG=express:* && node index.js
```
Same idea, just a different way to set environment variables.
Now let's look at logging in Express.
For logging HTTP requests, many developers use middleware like `morgan`.
Here's how to use it:
```bash
npm install morgan
```
Then add it to your app:
```js
const express = require('express')
const morgan = require('morgan')
const app = express()
app.use(morgan('dev'))s
```
Now, every incoming request will be logged to the terminal with details like the method, URL, status code, and response time.
And why does this matter?
Debugging gives you insight into how Express is handling requests, middleware, and routing without cluttering your production logs.
Logging gives you a record of what's happening across requests, which is crucial for debugging bugs, understanding traffic, and spotting issues early.
Together, they give you control and clarity in your development process.
# --questions--
## --text--
How do you enable debugging messages in Express?
## --answers--
By using `console.log()`.
### --feedback--
Think about environment configuration.
---
By installing a debug plugin
### --feedback--
Think about environment configuration.
---
By setting the `DEBUG` environment variable
---
By passing `{ debug: true }` to the Express constructor
### --feedback--
Think about environment configuration.
## --video-solution--
3
## --text--
What does `DEBUG=express:router` do?
## --answers--
Enables debugging for all routers.
### --feedback--
Focus on namespace filtering.
---
Logs all route names.
### --feedback--
Focus on namespace filtering.
---
Enables only router-related debug output.
---
Disables debugging entirely
### --feedback--
Focus on namespace filtering.
## --video-solution--
3
## --text--
What is a common library for logging HTTP requests in Express?
## --answers--
`axios`
### --feedback--
It logs method, URL, status, and response time.
---
`morgan`
---
`lodash`
### --feedback--
It logs method, URL, status, and response time.
---
React
### --feedback--
It logs method, URL, status, and response time.
## --video-solution--
2
@@ -0,0 +1,203 @@
---
id: 69f3046f4c0e76cbdd248590
title: How do Health Checks and Graceful Shutdowns Work in Express?
challengeType: 19
dashedName: how-do-health-checks-and-graceful-shutdowns-work-in-express
---
# --description--
Ever wondered how to keep your Express app stable and production-ready, especially when it's under heavy load or shutting down?
That's where health checks and graceful shutdowns come in. These two patterns help you keep your app available, and ensure it exits cleanly without dropping requests or corrupting data.
Let's break it down.
First, what even is a "health check" in Express?
A health check is a simple route that tells whether your app is up and running.
For example:
```js
app.get('/health', (req, res) => {
res.status(200).send('OK')
})
```
Tools like load balancers or container orchestrators - think Kubernetes or AWS Elastic Beanstalk - can ping this route to decide if your app is healthy.
If the route returns a 200 status code, your service is considered healthy. As a reminder - a 200 HTTP status code means everything is okay!
If it doesn't respond, the orchestrator can restart the service automatically.
And what is a "graceful shutdown"?
A graceful shutdown is what happens when your app is about to stop, like when a server is restarting or a deployment is happening.
Instead of cutting off connections immediately, you give the app time to finish what it's doing.
In Express, you can listen for `SIGTERM`, which is a system signal sent when a process should terminate.
Here's an example:
```js
process.on('SIGTERM', () => {
server.close(() => {
console.log('Server closed. Cleaning up...')
// Close DB connections or other cleanup here
})
})
```
This tells Express to stop accepting new connections and wait for existing ones to finish before exiting.
You should also handle `SIGINT`, which is the signal sent when you press Ctrl+C to stop your app during local development.
While `SIGTERM` is typically used by orchestrators like Kubernetes in production, `SIGINT` covers the everyday developer workflow. Without handling it, hitting Ctrl+C kills the process immediately, skipping any cleanup you've set up.
Since both signals should trigger the same graceful shutdown behavior, developers commonly listen for both.
Now, lets put it all together
Imagine your app is running in a container. Kubernetes sends a `SIGTERM` to shut it down for an update.
With just a basic graceful shutdown, your app stops accepting new connections and finishes up current requests. Once all work is done, it disconnects from the database and exits cleanly. No lost data, no corrupted state.
But there's a problem — the load balancer doesn't know your app is shutting down, so it might keep sending new traffic your way. That's where the bonus pattern below comes in.
Now, I have a little bonus for you. We will look at "Marking the App as Unhealthy Before Shutdown".
Before shutting down, you can update your health check to return a 503 status. That way, the load balancer sees the unhealthy response and stops routing new traffic to your app during the shutdown window, as demonstrated in the code above.
Reminder: A `503` status code means "Service Unavailable". it signals that the server is temporarily unable to handle requests, which is exactly what's happening during a graceful shutdown.
```js
let isShuttingDown = false
app.get('/health', (req, res) => {
if (isShuttingDown) {
res.status(500).send('Shutting down')
} else {
res.status(200).send('OK')
}
})
process.on('SIGTERM', () => {
isShuttingDown = true
server.close(() => {
console.log('Graceful shutdown complete')
// Perform cleanup
})
})
```
# --questions--
## --text--
What is the main purpose of a health check route?
## --answers--
To restart the server
### --feedback--
Think about how a load balancer knows if the app is up.
---
To report whether the app is running correctly.
---
To monitor user traffic.
### --feedback--
Think about how a load balancer knows if the app is up.
---
To log requests.
### --feedback--
Think about how a load balancer knows if the app is up.
## --video-solution--
2
## --text--
What does the `SIGTERM` signal do?
## --answers--
Starts the server.
### --feedback--
It happens during deployments or shutdowns.
---
Resets the app.
### --feedback--
It happens during deployments or shutdowns.
---
Tells the app to begin shutting down.
---
Sends debug information.
### --feedback--
It happens during deployments or shutdowns.
## --video-solution--
3
## --text--
Why would you send a 500 response from your health check during shutdown?
## --answers--
To cause an error.
### --feedback--
You want no new requests during shutdown.
---
To slow down requests.
### --feedback--
You want no new requests during shutdown.
---
To tell the load balancer to stop sending traffic.
---
To trigger garbage collection.
### --feedback--
You want no new requests during shutdown.
## --video-solution--
3
@@ -0,0 +1,25 @@
{
"isUpcomingChange": true,
"dashedName": "lecture-understanding-error-handling-and-health-checks",
"helpCategory": "JavaScript",
"blockLayout": "challenge-list",
"challengeOrder": [
{
"id": "69f303a6812a8627022f9592",
"title": "What are HTTP Response Status Codes?"
},
{
"id": "69f3046f4c0e76cbdd24858e",
"title": "How Does Error Handling Work In Express?"
},
{
"id": "69f3046f4c0e76cbdd24858f",
"title": "How Does Debugging and Logging Work in Express?"
},
{
"id": "69f3046f4c0e76cbdd248590",
"title": "How do Health Checks and Graceful Shutdowns Work in Express?"
}
],
"blockLabel": "lecture"
}
@@ -62,7 +62,7 @@
{ {
"dashedName": "error-handling-in-express", "dashedName": "error-handling-in-express",
"comingSoon": true, "comingSoon": true,
"blocks": [] "blocks": ["lecture-understanding-error-handling-and-health-checks"]
}, },
{ "dashedName": "websockets", "comingSoon": true, "blocks": [] }, { "dashedName": "websockets", "comingSoon": true, "blocks": [] },
{ "dashedName": "node-and-sql", "comingSoon": true, "blocks": [] }, { "dashedName": "node-and-sql", "comingSoon": true, "blocks": [] },