Skip to main content

Command Palette

Search for a command to run...

Creating Routes and Handling Requests with Express

Build APIs faster with Express: routes, requests, and responses made simple.

Updated
8 min read
Creating Routes and Handling Requests with Express
A

Hi, I’m Abdul Samad. A web development learner and tech enthusiast. I write about what I learn, share practical coding tips, and publish in-depth blogs on programming and modern web development.

Check out my full collection of blogs on Hashnode: https://abdulsamad30.hashnode.dev/

Connect with me on X for quick updates and insights: @abdul_sama60108

If you've ever tried building a web server using just Node.js, you know it can get messy pretty quickly. You're manually parsing URLs, figuring out request methods, and writing a lot of boilerplate code just to handle a simple request. That's where Express.js comes in and honestly, once you use it, going back feels painful.

In this post, we'll walk through what Express.js is, why it makes your life easier, and how to actually use it to create routes and handle requests. By the end, you'll have a solid understanding of how requests flow through an Express app and how to send proper responses back to the client.


What is Express.js?

Express.js is a minimal and flexible web framework built on top of Node.js. It gives you a clean, straightforward way to handle HTTP requests, define routes, and build web applications or APIs without reinventing the wheel every time.

Think of Node.js as the raw engine of a car, and Express.js as the steering wheel, dashboard, and controls that make the engine actually usable for driving. The engine is still doing the heavy lifting, but Express gives you the interface to control it comfortably.

It's part of what's often called the MERN or MEAN stack, and it's one of the most downloaded packages on npm for a reason it's simple, stable, and widely supported.


Why Express Simplifies Node.js Development

To really appreciate Express, it helps to see what building a server looks like without it.

Raw Node.js HTTP Server

const http = require('http');

const server = http.createServer((req, res) => {
  if (req.url === '/about' && req.method === 'GET') {
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('About Page');
  } else if (req.url === '/contact' && req.method === 'GET') {
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('Contact Page');
  } else {
    res.writeHead(404, { 'Content-Type': 'text/plain' });
    res.end('Not Found');
  }
});

server.listen(3000, () => {
  console.log('Server running on port 3000');
});

Output (visiting /about in browser or via curl):

About Page

This works, but notice how you're manually checking req.url and req.method for every single route. As your app grows, this becomes a tangled mess.

The Same Thing with Express

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

app.get('/about', (req, res) => {
  res.send('About Page');
});

app.get('/contact', (req, res) => {
  res.send('Contact Page');
});

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

Output (visiting /about):

About Page

Same result. But the Express version is cleaner, more readable, and scales much better. Each route is clearly defined, and you're not writing manual conditionals everywhere. That's the core value Express brings to the table.


Creating Your First Express Server

Let's start from scratch and build a basic Express server.

First, make sure you have Node.js installed. Then set up your project:

mkdir my-express-app
cd my-express-app
npm init -y
npm install express

Now create a file called index.js and add the following:

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

const PORT = 3000;

app.get('/', (req, res) => {
  res.send('Welcome to my Express server!');
});

app.listen(PORT, () => {
  console.log(`Server is running on <http://localhost>:${PORT}`);
});

Run it with:

node index.js

Output in terminal:

Server is running on <http://localhost:3000>

Output in browser at http://localhost:3000:

Welcome to my Express server!

Here's what's happening step by step:

  • require('express') imports the Express module

  • express() creates an application instance

  • app.get('/', ...) defines a route for GET requests at the root path

  • app.listen(PORT, ...) starts the server on port 3000

Simple, clean, and ready to build on.


Understanding the Request → Response Flow

Before we go further into routes, it's worth visualizing how a request actually travels through an Express app.

When a client (browser, mobile app, or tool like Postman) sends a request:

  1. The request hits your Express server

  2. Express looks through the defined routes to find a match (method + path)

  3. The matching route handler runs

  4. The handler sends back a response to the client

If no route matches, Express returns a default 404 response unless you've defined a custom one.


Express Routing Structure

Routing in Express is the way you define how your app responds to different URLs and HTTP methods. Each route has a path and a handler function.

The general pattern looks like this:

app.METHOD(PATH, HANDLER);
  • METHOD the HTTP method (get, post, put, delete, etc.)

  • PATH the URL path ('/', '/users', '/products/1')

  • HANDLER a function that receives req (request) and res (response)

This structure is what makes Express so intuitive. You can glance at a route and immediately understand what it does and when it runs.


Handling GET Requests

GET requests are used to retrieve data. When someone visits a URL in their browser, they're making a GET request.

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

app.get('/users', (req, res) => {
  const users = [
    { id: 1, name: 'Alice' },
    { id: 2, name: 'Bob' },
  ];
  res.json(users);
});

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

Output (visiting /users):

[
  { "id": 1, "name": "Alice" },
  { "id": 2, "name": "Bob" }
]

Here, res.json() automatically sets the Content-Type header to application/json and sends the data as a JSON response which is exactly what you'd want when building an API.

You can also use route parameters to make routes dynamic:

app.get('/users/:id', (req, res) => {
  const userId = req.params.id;
  res.send(`Fetching user with ID: ${userId}`);
});

Output (visiting /users/42):

Fetching user with ID: 42

req.params gives you access to the dynamic parts of the URL. Clean and straightforward.


Handling POST Requests

POST requests are used to send data to the server like submitting a form or creating a new resource.

To read the data sent in a POST request body, you need to add a middleware that parses JSON. Express has this built in:

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

app.use(express.json()); // Middleware to parse JSON request bodies

app.post('/users', (req, res) => {
  const newUser = req.body;
  console.log('Received user data:', newUser);
  res.status(201).json({
    message: 'User created successfully',
    user: newUser,
  });
});

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

To test this, you can use Postman or curl:

curl -X POST <http://localhost:3000/users> \\
  -H "Content-Type: application/json" \\
  -d '{"name": "Charlie", "email": "charlie@example.com"}'

Output in terminal:

Received user data: { name: 'Charlie', email: 'charlie@example.com' }

Response sent back to client:

{
  "message": "User created successfully",
  "user": {
    "name": "Charlie",
    "email": "charlie@example.com"
  }
}

app.use(express.json()) is the line that does the heavy lifting it parses incoming JSON payloads and makes the data available at req.body. Without it, req.body would be undefined.


Sending Responses

Express gives you several ways to send responses depending on what you need to return.

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

// Send plain text
app.get('/text', (req, res) => {
  res.send('This is plain text');
});

// Send JSON
app.get('/json', (req, res) => {
  res.json({ message: 'This is JSON' });
});

// Send with a specific status code
app.get('/not-found', (req, res) => {
  res.status(404).send('This page does not exist');
});

// Send with status 201 (Created)
app.get('/created', (req, res) => {
  res.status(201).json({ message: 'Resource created' });
});

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

Outputs:

  • GET /textThis is plain text

  • GET /json{ "message": "This is JSON" }

  • GET /not-found → Status 404, This page does not exist

  • GET /created → Status 201, { "message": "Resource created" }

Here's a quick reference for the most commonly used response methods:

Method Use Case
res.send() Send text or HTML
res.json() Send JSON data
res.status() Set HTTP status code
res.sendFile() Send a file

Always pair your status codes with your responses it helps the client understand what happened. 200 means OK, 201 means something was created, 400 is a bad request, and 404 means not found.


Wrapping Up

Express takes the complexity out of building HTTP servers with Node.js. Instead of juggling raw request parsing and manual conditionals, you get a clean routing system where each path and method has its own dedicated handler.

Here's a quick recap of what we covered:

  • Express.js is a lightweight framework built on top of Node.js

  • It's significantly cleaner than raw Node.js HTTP servers

  • You create a server with express() and start it with app.listen()

  • GET routes handle data retrieval, and you can use req.params for dynamic paths

  • POST routes handle incoming data, and express.json() middleware makes req.body available

  • Responses can be plain text, JSON, or status-coded replies depending on your needs

From here, a natural next step would be exploring Express middleware more deeply, or organizing routes into separate files using express.Router() but that's a topic for another post.

For now, experiment with what you've learned here. Build a small API with a few routes, test it with Postman, and get comfortable with the request-response cycle. That foundation will take you a long way.

FIN ✌️