Skip to content

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.

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 handlers
app.post('/posts', (req, res) => {
const { title, body } = req.body // ✅ works
})

Without this, req.body is undefined.

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.

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”:

Terminal window
npm install cors
npm install --save-dev @types/cors
import cors from 'cors'
// Allow requests from your frontend
app.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 logs every request: method, path, status code, and response time:

Terminal window
npm install morgan
npm install --save-dev @types/morgan
import morgan from 'morgan'
// 'dev' format: colorized, concise
app.use(morgan('dev'))
// Output: GET /posts 200 12.345 ms - 240

Formats:

  • 'dev' — colorized, for development
  • 'combined' — Apache-style, for production logs
  • 'tiny' — minimal
src/index.ts
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 matters
app.use(morgan('dev')) // logging first
app.use(cors({ origin: config.frontendUrl })) // CORS second
app.use(express.json()) // body parsing third
// Routes go after middleware
app.get('/health', (req, res) => res.json({ status: 'ok' }))
// 404 catch-all last
app.use((req, res) => res.status(404).json({ error: 'Not found' }))
app.listen(config.port, () => {
console.log(`Bulletin API running on port ${config.port}`)
})

The order in which you register middleware matters:

  1. Logging — log every request, including ones that fail
  2. CORS — set headers before any response is sent
  3. Body parsing — make req.body available for route handlers
  4. Authentication — verify tokens (later, in Module 06)
  5. Routes — your route handlers
  6. 404 handler — catch unmatched routes
  7. Error handler — catch errors from routes (next lesson)
  1. Install cors and morgan.
  2. Add both to your src/index.ts in the correct order.
  3. Make a request and observe the morgan log output.
  4. Remove express.json() temporarily — confirm req.body is undefined in a POST request.
  • express.json() — parses JSON request bodies into req.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.