Built-in and Third-Party Middleware
Every Express API uses the same set of core middleware. This lesson covers what each one does and how to configure it for the Bulletin API.
express.json() — body parsing
Section titled “express.json() — body parsing”Parses JSON request bodies and makes the result available as req.body:
app.use(express.json())
// Now req.body is parsed in POST/PUT/PATCH handlersapp.post('/posts', (req, res) => { const { title, body } = req.body // ✅ works})Without this, req.body is undefined.
express.urlencoded() — form data
Section titled “express.urlencoded() — form data”Parses URL-encoded form data (the kind sent by HTML <form> tags):
app.use(express.urlencoded({ extended: true }))The Bulletin API is a JSON API, so this isn’t needed — but it’s good to know it exists.
cors — Cross-Origin Resource Sharing
Section titled “cors — Cross-Origin Resource Sharing”When your React frontend (on https://your-app.github.io) makes a request to your API (on https://bulletin-api.railway.app), browsers block it by default. This is the Same-Origin Policy.
CORS headers tell the browser “this cross-origin request is allowed”:
npm install corsnpm install --save-dev @types/corsimport cors from 'cors'
// Allow requests from your frontendapp.use(cors({ origin: process.env.FRONTEND_URL || 'http://localhost:5173', credentials: true,}))In development, your React Vite app runs on http://localhost:5173. In production, it’s your GitHub Pages URL. The origin option whitelists requests from that origin.
morgan — HTTP request logging
Section titled “morgan — HTTP request logging”Morgan logs every request: method, path, status code, and response time:
npm install morgannpm install --save-dev @types/morganimport morgan from 'morgan'
// 'dev' format: colorized, conciseapp.use(morgan('dev'))
// Output: GET /posts 200 12.345 ms - 240Formats:
'dev'— colorized, for development'combined'— Apache-style, for production logs'tiny'— minimal
Full middleware setup for Bulletin
Section titled “Full middleware setup for Bulletin”import 'dotenv/config'import express from 'express'import cors from 'cors'import morgan from 'morgan'import { config } from './config.js'
const app = express()
// Middleware — order mattersapp.use(morgan('dev')) // logging firstapp.use(cors({ origin: config.frontendUrl })) // CORS secondapp.use(express.json()) // body parsing third
// Routes go after middlewareapp.get('/health', (req, res) => res.json({ status: 'ok' }))
// 404 catch-all lastapp.use((req, res) => res.status(404).json({ error: 'Not found' }))
app.listen(config.port, () => { console.log(`Bulletin API running on port ${config.port}`)})Middleware order
Section titled “Middleware order”The order in which you register middleware matters:
- Logging — log every request, including ones that fail
- CORS — set headers before any response is sent
- Body parsing — make
req.bodyavailable for route handlers - Authentication — verify tokens (later, in Module 06)
- Routes — your route handlers
- 404 handler — catch unmatched routes
- Error handler — catch errors from routes (next lesson)
Exercise
Section titled “Exercise”- Install
corsandmorgan. - Add both to your
src/index.tsin the correct order. - Make a request and observe the morgan log output.
- Remove
express.json()temporarily — confirmreq.bodyis undefined in a POST request.
express.json()— parses JSON request bodies intoreq.body.cors({ origin })— enables cross-origin requests from your frontend URL.morgan('dev')— logs all requests with method, path, status, and timing.- Register middleware in order: logging → CORS → body parsing → routes → error handling.