Skip to main content

Command Palette

Search for a command to run...

Creating Routes and Handling Requests with Express.js

Updated
6 min read
Creating Routes and Handling Requests with Express.js

What is Express.js?

Express.js is a minimal, unopinionated web framework for Node.js. It wraps Node's built-in http module with a clean, developer-friendly API — giving you routing, middleware, and response helpers without locking you into a rigid structure.

Think of it this way: Node's http module hands you the raw HTTP pipe. Express gives you a well-designed faucet to control it.

Quick fact: Express is the E in the MERN/MEAN stack, and one of the most downloaded npm packages ever — with over 30 million weekly downloads.

Why Express Simplifies Node.js Development

Raw Node.js forces you to manually parse URLs, method names, request bodies, and headers for every single route. As your app grows, that becomes a maintenance nightmare.

Here's an honest side-by-side:

Raw Node.js HTTP

const http = require('http');

const server = http.createServer((req, res) => {
  if (req.url === '/' && req.method === 'GET') {
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('Hello World');
  } else if (req.url === '/users') {
    // more manual checks...
  }
});

server.listen(3000);

Express.js

const express = require('express');
const app = express();

app.get('/', (req, res) => {
  res.send('Hello World');
});

app.get('/users', (req, res) => {
  res.json({ users: [] });
});

app.listen(3000);

Express handles all the tedious boilerplate — content-type headers, URL parsing, method routing — so you can focus on your application logic.

Creating Your First Express Server

Step 1 — Install Express

# Initialize a new project
mkdir my-express-app && cd my-express-app
npm init -y

# Install Express
npm install express

Step 2 — Write the minimal server

// server.js
const express = require('express');

const app = express();   // create the app instance
const PORT = 3000;

// A single route — responds to GET /
app.get('/', (req, res) => {
  res.send('🚀 Express server is live!');
});

// Start listening
app.listen(PORT, () => {
  console.log(`Server running at http://localhost:${PORT}`);
});

Run it with node server.js and open http://localhost:3000 in your browser. That's your first Express server.

💡 Pro tip: Install nodemon (npm i -g nodemon) and run nodemon server.js — it auto-restarts your server on every file change during development.

Request → Route Handler → Response Flow

Before diving into routes, it helps to understand what actually happens when a request hits your server:

Every HTTP request follows this exact path. Express's job is to make steps 2–4 as clean as possible.

Handling GET Requests

GET requests are the most common — they ask the server to retrieve data. Express handles them with app.get(path, handler).

const express = require('express');
const app = express();

// 1. Static route
app.get('/about', (req, res) => {
  res.send('About page');
});

// 2. Route with URL parameter  →  /users/42
app.get('/users/:id', (req, res) => {
  const { id } = req.params;          // grab :id from URL
  res.json({ message: `User ID: ${id}` });
});

// 3. Route with query string  →  /search?q=express
app.get('/search', (req, res) => {
  const { q } = req.query;            // grab ?q= from URL
  res.json({ query: q, results: [] });
});

app.listen(3000);

Key objects in a GET handler

Object What it contains
req.params Values from dynamic route segments (:id)
req.query Values from query strings (?key=value)
req.headers HTTP headers sent by the client

Handling POST Requests

POST requests send data to the server — creating resources, submitting forms, logging in. The request body carries the data. Express needs a built-in middleware to parse it.

const express = require('express');
const app = express();

// ✅ Required: parse incoming JSON bodies
app.use(express.json());

// In-memory "database" for the demo
const users = [];

// GET all users
app.get('/users', (req, res) => {
  res.json(users);
});

// POST create a new user
app.post('/users', (req, res) => {
  const { name, email } = req.body;    // parsed from JSON body

  if (!name || !email) {
    return res
      .status(400)
      .json({ error: 'name and email are required' });
  }

  const newUser = {
    id: users.length + 1,
    name,
    email,
  };

  users.push(newUser);
  res.status(201).json(newUser);       // 201 = Created
});

app.listen(3000, () => console.log('Server on port 3000'));

Test it with curl

# Create a user
curl -X POST http://localhost:3000/users \
  -H "Content-Type: application/json" \
  -d '{"name":"Arjun","email":"arjun@example.com"}'

# Get all users
curl http://localhost:3000/users

Common mistake: Always send exactly one response per request. Calling res.send() twice throws "Cannot set headers after they are sent to the client". Use return res.json(...) inside conditionals to stop execution early.

Sending Responses

The res object is your toolkit for sending data back to the client.

// 1. Plain text
res.send('Hello!');

// 2. JSON  (auto-sets Content-Type: application/json)
res.json({ status: 'ok', data: [] });

// 3. Custom HTTP status code
res.status(404).json({ error: 'Not found' });

// 4. Redirect
res.redirect('/new-url');

// 5. Set a custom header, then respond
res.set('X-Powered-By', 'My API').json({ ok: true });

Common HTTP status codes to know

Code Meaning When to use
200 OK Successful GET
201 Created Successful POST
400 Bad Request Missing/invalid input
404 Not Found Resource doesn't exist
500 Server Error Something broke on your end

Putting It All Together

Here's a complete, minimal Express API with GET and POST routes, proper status codes, and JSON responses:

// server.js
const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;

// ── Middleware ──────────────────────────────────────
app.use(express.json());

// ── Data store (in-memory for demo) ────────────────
let products = [
  { id: 1, name: 'Keyboard', price: 2499 },
  { id: 2, name: 'Mouse',    price: 999  },
];

// ── Routes ─────────────────────────────────────────

// GET all products
app.get('/products', (req, res) => {
  res.json({ count: products.length, products });
});

// GET single product by ID
app.get('/products/:id', (req, res) => {
  const product = products.find(
    (p) => p.id === parseInt(req.params.id)
  );
  if (!product) {
    return res.status(404).json({ error: 'Product not found' });
  }
  res.json(product);
});

// POST create a new product
app.post('/products', (req, res) => {
  const { name, price } = req.body;
  if (!name || !price) {
    return res
      .status(400)
      .json({ error: 'name and price are required' });
  }
  const newProduct = { id: products.length + 1, name, price };
  products.push(newProduct);
  res.status(201).json(newProduct);
});

// 404 fallback for unknown routes
app.use((req, res) => {
  res.status(404).json({ error: 'Route not found' });
});

// ── Start server ────────────────────────────────────
app.listen(PORT, () => {
  console.log(`✅ Server running → http://localhost:${PORT}`);
});