NoNode.js · Lesson 3 of 7

HTTP Server

Node has a built-in HTTP module that can serve requests with no dependencies. Most real apps use Express on top of it, but understanding the raw module makes Express less magical.

JavaScript
import { createServer } from 'http'

const server = createServer((req, res) => {
  const url = new URL(req.url, `http://${req.headers.host}`)

  // Route by pathname
  if (req.method === "GET" && url.pathname === "/") {
    res.writeHead(200, { "Content-Type": "text/plain" })
    res.end("Hello, World!")
    return
  }

  if (req.method === "GET" && url.pathname === "/health") {
    res.writeHead(200, { "Content-Type": "application/json" })
    res.end(JSON.stringify({ ok: true, uptime: process.uptime() }))
    return
  }

  // Read request body for POST
  if (req.method === "POST" && url.pathname === "/echo") {
    let body = ""
    req.on("data", chunk => body += chunk)
    req.on("end", () => {
      res.writeHead(200, { "Content-Type": "application/json" })
      res.end(JSON.stringify({ received: body }))
    })
    return
  }

  res.writeHead(404, { "Content-Type": "text/plain" })
  res.end("Not Found")
})

const PORT = process.env.PORT ?? 3000
server.listen(PORT, () => console.log(`Server running on http://localhost:${PORT}`))
JavaScript
// Express — the most popular Node.js web framework
// npm install express
import express from 'express'

const app  = express()
const PORT = process.env.PORT ?? 3000

// Built-in middleware
app.use(express.json())              // parse JSON request bodies
app.use(express.urlencoded({ extended: true })) // parse form data

// In-memory "database" for demo
const users = new Map()
let nextId = 1

// Routes
app.get("/",           (req, res) => res.json({ message: "API running" }))
app.get("/users",      (req, res) => res.json([...users.values()]))
app.get("/users/:id",  (req, res) => {
  const user = users.get(Number(req.params.id))
  if (!user) return res.status(404).json({ error: "User not found" })
  res.json(user)
})

app.post("/users", (req, res) => {
  const { name, email } = req.body
  if (!name || !email) return res.status(400).json({ error: "name and email required" })
  const user = { id: nextId++, name, email }
  users.set(user.id, user)
  res.status(201).json(user)
})

app.delete("/users/:id", (req, res) => {
  const id = Number(req.params.id)
  if (!users.has(id)) return res.status(404).json({ error: "Not found" })
  users.delete(id)
  res.status(204).end()
})

app.listen(PORT, () => console.log(`http://localhost:${PORT}`))
✦ Tip
Add error handling middleware at the end of all your routes: `app.use((err, req, res, next) => { res.status(500).json({ error: err.message }) })`. Express recognizes 4-argument middleware as error handlers. Without it, unhandled errors crash the process or return HTML error pages.