Learning Goals
3 minBy the end of this lesson you will be able to:
- Rename verb-based routes like
/getStudentsinto RESTful ones with plural nouns. - Design a nested resource route such as
/students/:id/grades. - Add a query param like
?sort=nameto a list route and read it in your handler. - Move one resource's routes into a express.Router module and mount it, then hit it in the browser.
Warm-Up · Spot the Bad Routes
5 minLast lesson you returned the right status codes. Today we tidy up the addresses themselves.
Here are four routes from a student API. Each one hides its verb inside the path. Read them, then rename each one the REST way:
GET /getAllStudents
POST /createStudent
GET /getStudentById/5
POST /deleteStudentById/5Think together
- The method already says the action. Do we need the verb in the path too?
- What one noun could all four routes share?
Reveal
The method is the verb, so the path only needs the noun — plural students. Clean set:
GET /students // was /getAllStudents
POST /students // was /createStudent
GET /students/5 // was /getStudentById/5
DELETE /students/5 // was /deleteStudentById/5One noun, four methods. Notice delete becomes the DELETE method, not a POST.
New Concept · Routes as an Address System
12 minThink of a KL address: Jalan Bukit Bintang → No. 12 → Unit 3. Because everyone uses the same pattern, a postman finds any home without a map. Good routes work the same way — predictable, so any teammate can guess where a resource lives.
A resource is a kind of thing your API stores: students, classes, orders. The rules are short:
The messy way
Verbs in the path: /getStudents, /createStudent.
Singular here, plural there — no pattern.
Every route is a surprise to your teammates.
The REST way
Nouns, never verbs — the method is the verb.
Plural resource names: /students.
/students/:id for one item. Predictable everywhere.
The standard resource table
For a single resource, five routes cover the whole life cycle. Learn this shape once and it repeats for every resource:
GET /students // list every student
POST /students // create a new student
GET /students/:id // read one student
PUT /students/:id // update one student
DELETE /students/:id // delete one studentNested resources and query params
A thing that belongs to another thing goes inside it. A student's grades: /students/:id/grades. That is a nested resource.
For sorting or filtering a list, do not invent a new route. Add a query param after a ?:
GET /students?sort=name // same list, sorted by name
GET /students?class=5A // same list, only class 5AWhy it matters
Consistent routes are easy for a teammate to guess and easy for your React app to call. When every resource follows one pattern, nobody has to memorise a map.
Worked Example · Refactor to a Router
12 minAisyah has a working but messy student API. We will rename her routes, then move them into their own module. Keep the server running with node server.js and restart after each change.
Step 1 — The messy verb routes
Here is what she started with. It works, but the paths repeat the verb and mix singular with plural:
// server.js — messy
app.get('/getAllStudents', (req, res) => { /* ... */ })
app.post('/createStudent', (req, res) => { /* ... */ })
app.get('/getStudentById/:id', (req, res) => { /* ... */ })
app.post('/deleteStudentById/:id', (req, res) => { /* ... */ })Step 2 — Rename to a clean resource set
Drop the verbs, use the plural noun students, and let the method carry the action:
app.get('/students', (req, res) => res.json(students))
app.post('/students', (req, res) => res.status(201).json(newStudent))
app.get('/students/:id', (req, res) => res.json(oneStudent))
app.delete('/students/:id', (req, res) => res.status(204).end())Step 3 — Move them into routes/students.js
Make a folder routes with a file students.js. Build a small express.Router — a mini-app that holds only this resource's routes — and export it:
// routes/students.js
const express = require('express')
const router = express.Router()
const students = [{ id: 1, name: 'Aisyah' }]
router.get('/', (req, res) => res.json(students)) // GET /students
router.post('/', (req, res) => res.status(201).json({})) // POST /students
router.get('/:id', (req, res) => res.json(students[0])) // GET /students/:id
router.delete('/:id', (req, res) => res.status(204).end()) // DELETE /students/:id
module.exports = routerNotice the paths are now / and /:id — the /students part moves to where we mount the router.
Step 4 — Mount the router
In server.js, require the module and mount it under one base path with app.use:
// server.js
const express = require('express')
const app = express()
const studentsRouter = require('./routes/students')
app.use('/api/students', studentsRouter) // every route gets this prefix
app.listen(3000, () => console.log('Listening on 3000'))Start the server, then open this address in the browser:
http://localhost:3000/api/studentsYou see the list, exactly as before:
[{"id":1,"name":"Aisyah"}]What changed? The routes behave the same, but server.js is now tiny and every student route lives in one file. Add an orders resource later and it gets its own router too.
Try It Yourself
13 minWork in the same project. Restart the server after each change and test each route in the browser.
Task 1 — Design a nested resource
A
classhas many students. On paper, write the five REST routes for aclassesresource, then add one nested route that lists the students in one class:/classes/:id/students. Say out loud which method each route uses.Task 2 — Move a resource into a Router
Make
routes/classes.js. Build anexpress.Router, add arouter.get('/', ...)that returns a short list of classes as JSON, andmodule.exports = router. Inserver.js, mount it withapp.use('/api/classes', classesRouter)and open/api/classes.Task 3 — Add a ?sort= query param
In your students list route, read
req.query.sort. If it equals'name', return the list sorted by name; otherwise return it as-is. Test both/api/studentsand/api/students?sort=nameand confirm the order changes.
Mini-Challenge · Redesign & Build
8 minA canteen API has grown messy. Here are four of its routes:
GET /listAllOrders
POST /addNewOrder
GET /findOrder/7
POST /removeOrder/7First, redesign all four as a clean RESTful table for the orders resource. Then implement two of them inside a routes/orders.js express.Router, mounted at /api/orders. This combines route design, a Router, and the CRUD verbs from earlier.
It works if: your table maps each old route to a method + plural-noun path, and opening /api/orders in the browser returns your orders as JSON.
Reveal a sample answer
The redesign table:
GET /orders // was /listAllOrders
POST /orders // was /addNewOrder
GET /orders/:id // was /findOrder/7
DELETE /orders/:id // was /removeOrder/7Two of them implemented in a Router:
// routes/orders.js
const express = require('express')
const router = express.Router()
const orders = [{ id: 7, item: 'Nasi lemak', price: 5.5 }]
router.get('/', (req, res) => res.json(orders)) // GET /orders
router.get('/:id', (req, res) => { // GET /orders/:id
const id = Number(req.params.id)
const order = orders.find((o) => o.id === id)
if (!order) return res.status(404).json({ error: 'Not found' })
res.json(order)
})
module.exports = routerMount it with app.use('/api/orders', ordersRouter). Opening /api/orders then shows:
[{"id":7,"item":"Nasi lemak","price":5.5}]Recap
3 min- Use nouns, not verbs in routes — the HTTP method is already the verb.
- Name resources with plural nouns:
/students, and/students/:idfor one item. - A nested resource lives inside its parent:
/students/:id/grades. - Use a query param like
?sort=namefor filtering and sorting, not a new route. - express.Router gathers one resource's routes into a module, mounted with
app.use.
New words
- Resource naming — using the thing's name (a noun) as the route, never an action.
- Plural nouns — the convention of naming a collection route in the plural (
/students). - Nested resource — a route placed inside another to show ownership (
/students/:id/grades). - Query param — a
?key=valueoption after the path, read fromreq.query. - express.Router — a mini-app that holds one resource's routes and is mounted with
app.use.
Homework
4 min to brief · ~20 min to doRequired
- Take an API you have built this level (or the students API from class). Redesign its routes to be RESTful — plural nouns, no verbs in the path — and split one resource into its own
routes/<name>.jsexpress.Router mounted withapp.use. Open one route in the browser, take a screenshot of the JSON, and bring it next lesson.
Optional stretch
- Read about API versioning — why big APIs put
/api/v1/...in front of every route. Write one sentence on what breaks if you change a route with no version. - Add a second nested route to your Router, for example
/students/:id/grades, returning a short array of grades as JSON.