Module 10 — Node.js & Express
Write server-side JavaScript and build your first REST API.
Overview 📋
Node.js is a JavaScript runtime that runs outside the browser — on servers, in your terminal, anywhere you need to execute JS. Express is a minimal web framework for Node.js that makes it easy to define routes and handle HTTP requests. Together, they are the foundation of most JavaScript backends. This module takes you from "run a JS file" to "serve a working API."
Why This Matters 💡
Up until now, your JavaScript has lived in the browser. Node.js opens up the server side: handling database queries, authenticating users, protecting sensitive keys, and sending custom responses. Express is the most widely used Node.js framework and the basis for the backend of your capstone project.
Learning Goals 🎯
By the end of this module you should be able to:
- Run JavaScript files with Node.js
- Use
npmto install and manage packages - Understand how modules work with
requireandimport - Set up an Express application
- Define routes for GET, POST, PUT, and DELETE
- Use route parameters and query strings
- Return JSON responses
- Test your API with Postman
Vocabulary 📖
| Term | Definition |
|---|---|
| Node.js | A JavaScript runtime built on Chrome's V8 engine |
| Express | A minimal Node.js web framework for building APIs |
| Route | A combination of HTTP method + path that handles a request |
| Request (req) | The incoming HTTP request object |
| Response (res) | The outgoing HTTP response object |
| Middleware | A function that runs between a request and a route handler |
| Route parameter | A dynamic segment in a route path (e.g., /users/:id) |
dotenv | A package for loading environment variables from a .env file |
Core Concepts 🧠
Running a JS file with Node
node index.js
Setting up Express
npm init -y
npm install express
// index.js
const express = require('express')
const app = express()
const PORT = 3000
// parse incoming json bodies
app.use(express.json())
app.get('/', (req, res) => {
res.json({ message: 'server is running' })
})
app.listen(PORT, () => {
console.log(`server running on port ${PORT}`)
})
CRUD routes
// get all items
app.get('/students', (req, res) => {
res.json(students)
})
// get one item by id
app.get('/students/:id', (req, res) => {
const student = students.find((s) => s.id === Number(req.params.id))
if (!student) return res.status(404).json({ error: 'not found' })
res.json(student)
})
// post — create
app.post('/students', (req, res) => {
const newStudent = { id: students.length + 1, ...req.body }
students.push(newStudent)
res.status(201).json(newStudent)
})
// put — update
app.put('/students/:id', (req, res) => {
const index = students.findIndex((s) => s.id === Number(req.params.id))
if (index === -1) return res.status(404).json({ error: 'not found' })
students[index] = { ...students[index], ...req.body }
res.json(students[index])
})
// delete
app.delete('/students/:id', (req, res) => {
const index = students.findIndex((s) => s.id === Number(req.params.id))
if (index === -1) return res.status(404).json({ error: 'not found' })
students.splice(index, 1)
res.status(204).send()
})
Using environment variables
npm install dotenv
// at the top of index.js
require('dotenv').config()
const PORT = process.env.PORT || 3000
const DB_URL = process.env.DATABASE_URL
# .env (never commit this file)
PORT=3001
DATABASE_URL=postgres://localhost/mydb
Examples 💻
Adding CORS for a React frontend:
npm install cors
const cors = require('cors')
app.use(cors()) // allow all origins (fine for development)
Using query strings:
// get /students?cohort=spring2026
app.get('/students', (req, res) => {
const { cohort } = req.query
const results = cohort
? students.filter((s) => s.cohort === cohort)
: students
res.json(results)
})
Organizing routes with Express Router:
// routes/students.js
const router = require('express').Router()
router.get('/', (req, res) => { /* ... */ })
router.post('/', (req, res) => { /* ... */ })
module.exports = router
// index.js
const studentRoutes = require('./routes/students')
app.use('/students', studentRoutes)
Common Mistakes ⚠️
- Forgetting
express.json()middleware. Without it,req.bodyisundefinedon POST and PUT requests. - Not sending a response. Every route handler must call
res.json(),res.send(), orres.status()...— otherwise the request hangs. - Committing
.envto GitHub. Your.envfile contains secrets. It must be in.gitignore. Always. - Using hardcoded ports or URLs. Use environment variables for anything that might change between development and production.
- Not handling the 404 case. If a resource is not found, return
res.status(404).json({ error: 'not found' }), not an emptyres.json({}).
Debugging Tips 🔍
- Test routes in Postman before connecting a frontend — it lets you control the request precisely.
console.log(req.body)to see exactly what the client sent.console.log(req.params)to inspect URL parameters.- If you get
Cannot GET /route, the route is not defined or the path is wrong. - Use
nodemonso your server restarts automatically on file changes:npx nodemon index.js.
Exercise 🏋️
The exercise for this module is in the class repository:
ttpr-lagcc-spring-2026 → Module 10 Exercise (opens in new tab)
Build a REST API for a student directory. Implement all five CRUD routes (get all, get one, create, update, delete) using an in-memory array. Test every route with Postman. Add a query string filter for cohort.
Additional Resources 📚
- Node.js docs (opens in new tab) — official Node documentation
- Express.js docs (opens in new tab) — official Express documentation
- Express.js — Routing (opens in new tab) — routing in depth
- MDN — HTTP response status codes (opens in new tab) — know your status codes
- Postman (opens in new tab) — test your API endpoints
- The Odin Project — NodeJS (opens in new tab) — full structured backend curriculum
Recap Checklist ✔️
- I can run a JavaScript file with Node.js
- I have an Express server running on a local port
- I can define GET, POST, PUT, and DELETE routes
- I use
express.json()middleware to parse request bodies - I read route parameters with
req.paramsand query strings withreq.query - I return proper status codes (200, 201, 404) in my responses
- I store secrets in
.envand have.envin my.gitignore - I tested all my routes with Postman