Learning Goals
3 minBy the end of this lesson you will be able to:
- Name the four families of status code —
2xx,3xx,4xx,5xx— in one line each. - Set a code on any reply with
res.status(code)before.json(...). - Return the right code from each CRUD route:
201,204,400,404. - Run
curl -iand read the status line at the top of the response.
Warm-Up · Which Number Fits?
5 minLast lesson you built full CRUD routes. Here are four things that can happen when someone calls your students API. Match each to a status code you have half-heard of — 200, 201, 204, 400, or 404:
1. GET /api/students/999 — that id does not exist -> ?
2. POST a new student, and it saves successfully -> ?
3. POST a student, but the "name" field is missing -> ?
4. DELETE /api/students/2 — gone, nothing left to send -> ?Reveal
1 = 404 Not Found (no such student), 2 = 201 Created (a new record was made), 3 = 400 Bad Request (the caller sent bad data), and 4 = 204 No Content (done, with nothing to send back). Plain 200 means “OK, here is what you asked for” — right for a GET that finds its item.
New Concept · A Grade For Every Request
12 minThink of a status code as an exam grade for a request. Before you read a single word of the reply, the number alone tells you what happened — a pass, a fail, or a problem at the marker's end.
Every HTTP reply carries one. They come in families, and the first digit tells you the family at a glance:
2xx Success the request worked 200, 201, 204
3xx Redirect look somewhere else 301, 302
4xx Client error the CALLER got it wrong 400, 401, 403, 404
5xx Server error YOUR code broke 500The syntax box
In Express you set the code with res.status(...), then chain the body on the end:
res.status(201).json({ id: 3, name: 'Priya' }) // createdFor a reply with no body, like a successful delete, use .end() instead of .json():
res.status(204).end() // no contentThe eight you will use most
Good news (2xx)
200 OK — here is what you asked for.
201 Created — a new record was made.
204 No Content — done, nothing to send.
Bad news (4xx & 5xx)
400 Bad Request — the sent data is wrong.
401 Unauthorised · 403 Forbidden.
404 Not Found · 500 Server Error.
Why it matters
Your React app branches on the code. A 200 shows the data. A 404 shows a friendly “not found” screen. A 400 shows a form error next to the field. Send the wrong code and the front end simply cannot tell success from failure.
Worked Example · Fixing the Students API
12 minOpen the server.js from last lesson — a small CRUD API for students. Right now every route replies 200. We will set the correct code on each. Restart with node server.js after each change.
Step 1 — 201 on a successful POST
Creating a record deserves 201 Created, and the reply should include the object that was made:
app.post('/api/students', (req, res) => {
const name = req.body.name
const student = { id: students.length + 1, name }
students.push(student)
res.status(201).json(student) // created, here it is
})Step 2 — 400 when a field is missing
If name is absent, the caller made the mistake. Reply 400 with a helpful message, and stop early with return:
app.post('/api/students', (req, res) => {
const name = req.body.name
if (!name) {
return res.status(400).json({ error: 'name is required' })
}
const student = { id: students.length + 1, name }
students.push(student)
res.status(201).json(student)
})Step 3 — 404 when the id is missing
The GET-one route should say 404 when no student matches:
app.get('/api/students/:id', (req, res) => {
const id = Number(req.params.id)
const student = students.find((s) => s.id === id)
if (!student) {
return res.status(404).json({ error: 'Student not found' })
}
res.json(student) // a plain 200
})Step 4 — 204 on a successful DELETE
A delete has nothing useful to send back, so reply 204 with an empty body:
app.delete('/api/students/:id', (req, res) => {
const id = Number(req.params.id)
students = students.filter((s) => s.id !== id)
res.status(204).end() // done, nothing to send
})Step 5 — See the status line with curl -i
The -i flag tells curl to print the response headers, including the all-important first line. On Windows, type curl.exe so PowerShell uses the real tool:
$ curl.exe -i -X POST http://localhost:3000/api/students -H "Content-Type: application/json" -d "{\"name\":\"Priya\"}"The very first line is the status line:
HTTP/1.1 201 Created
Content-Type: application/json; charset=utf-8
{"id":3,"name":"Priya"}Now delete that student and watch the code change:
$ curl.exe -i -X DELETE http://localhost:3000/api/students/3HTTP/1.1 204 No ContentWhat changed? Last lesson every route shouted 200 no matter what. Now the very first line of each reply tells the truth — 201 for a create, 204 for a delete — before the body is even read.
Try It Yourself
13 minWork in your students server.js. Restart with node server.js after each change, then check with curl.exe -i.
Task 1 — 201 on your POST
Change your create route so a successful POST replies
res.status(201).json(student). Send one withcurl.exe -iand confirm the first line readsHTTP/1.1 201 Created.Task 2 — 400 when name is missing
Guard the same route: if
req.body.nameis empty,return res.status(400).json(...)with a clear message. POST an empty body and check you get400 Bad Request, not a broken student.Task 3 — 404 on your GET-one route
In
GET /api/students/:id, reply404when.findreturns nothing. Ask for a made-up id like/api/students/999and confirm the status line reads404 Not Found.
Mini-Challenge · The Careful Sign-Up
8 minBuild one POST /api/students route that does both jobs well. This mixes today's status codes with req.body from last lesson.
It works if: a POST with a name replies 201 plus the created object, and a POST with no name replies 400 plus a helpful message — never a nameless student.
Reveal a sample answer
app.post('/api/students', (req, res) => {
const name = req.body.name
if (!name) {
return res.status(400).json({ error: 'name is required' })
}
const student = { id: students.length + 1, name }
students.push(student)
res.status(201).json(student)
})A good POST with { "name": "Arjun" } gives:
HTTP/1.1 201 Created
{"id":4,"name":"Arjun"}An empty POST gives a clear refusal instead:
HTTP/1.1 400 Bad Request
{"error":"name is required"}The return is the safety catch: it stops the route before a bad student is ever pushed into the array.
Recap
3 min- A status code is the grade of a request — the number alone tells the caller what happened.
- 2xx = success, 4xx = the caller's mistake, 5xx = your server broke.
- Set one with
res.status(code), then chain.json(...)or.end(). - CRUD codes: POST →
201, DELETE →204, not found →404, bad input →400. - Read the status line at the top of the reply with
curl -i(curl.exe -ion Windows).
New words
- Status code — the number at the top of every HTTP reply, giving the outcome.
- 2xx / 4xx / 5xx — the families: success, client error, server error.
- 201 · 204 — Created (with the new object) · No Content (done, empty body).
- 400 · 404 · 500 — Bad Request · Not Found · Server Error.
Homework
4 min to brief · ~20 min to doRequired
- Audit your students API. Go through every route and set the correct status code:
201on POST,204on DELETE,404when the id is missing, and400when a required field is absent. - Run
curl.exe -iagainst a create, a delete, and a miss. Take a screenshot of each status line and bring the three screenshots to next lesson.
Optional stretch
- Look up
401 Unauthorisedversus403 Forbiddenon the MDN status-code page. Write the difference in one sentence of your own. - Add a
PUT /api/students/:idroute that replies200with the updated student, or404if the id does not exist.