DEV Community

Cover image for Creating Routes and Handling Requests with Express
SATYA SOOTAR
SATYA SOOTAR

Posted on

Creating Routes and Handling Requests with Express

Hello readers 👋, welcome to the 11th blog in our Node.js series!

We've come a long way. We started by setting up Node.js, understood its event loop, explored blocking vs non‑blocking code, handled async operations with promises, secured routes with JWT, designed a REST API, and even dove into middleware. But to build those APIs and middleware, we used Express.js. Now it's time to shine a focused light on exactly how Express helps us create routes and handle requests so effortlessly.

In this post, we'll take a step back and talk about what Express.js is, why it makes Node.js development so much smoother, and how to create clean, readable routes for GET and POST requests. By the end, you'll see why Express has become the go‑to framework for building web servers with Node.js.

Let's jump in.

What is Express.js?

Express.js is a minimal and flexible web application framework for Node.js. It provides a thin layer of fundamental web application features, without obscuring Node.js features that you know and love. In simpler words, it's a set of tools that makes it ridiculously easy to handle HTTP requests, define routes, work with middleware, and send responses.

At its core, Express is just a function that returns an application object. You attach route handlers to it, and it efficiently maps incoming requests to the right handlers.

Why Express simplifies Node.js development

To appreciate Express, let's quickly compare it with building an HTTP server using Node.js's built‑in http module.

Raw Node.js HTTP server example:

const http = require('http');

const server = http.createServer((req, res) => {
  if (req.method === 'GET' && req.url === '/users') {
    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify([{ id: 1, name: 'Satya' }]));
  } else if (req.method === 'GET' && req.url.startsWith('/users/')) {
    const id = req.url.split('/')[2];
    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({ id, name: 'Satya' }));
  } else if (req.method === 'POST' && req.url === '/users') {
    let body = '';
    req.on('data', chunk => body += chunk);
    req.on('end', () => {
      const user = JSON.parse(body);
      res.writeHead(201, { 'Content-Type': 'application/json' });
      res.end(JSON.stringify(user));
    });
  } else {
    res.writeHead(404);
    res.end('Not found');
  }
});

server.listen(3000);
Enter fullscreen mode Exit fullscreen mode

That works, but as your application grows, the code becomes a mess of if‑else statements, manual body parsing, and repetitive header writing.

Now look at the same logic with Express:

const express = require('express');
const app = express();
app.use(express.json());

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

app.get('/users/:id', (req, res) => {
  const id = req.params.id;
  res.json({ id, name: 'Satya' });
});

app.post('/users', (req, res) => {
  const user = req.body;
  res.status(201).json(user);
});

app.listen(3000);
Enter fullscreen mode Exit fullscreen mode

Here's what Express gives you:

  • Clean routing: You directly map HTTP methods and URL patterns to handler functions. No need to manually parse req.url or check req.method.
  • Automatic body parsing: With express.json() middleware, the body is parsed and available at req.body.
  • Easy response helpers: res.json() automatically sets the content type and stringifies the object.
  • Route parameters: Express extracts :id from the URL and places it in req.params.
  • Middleware support: You can chain functions (like we explored in the previous blog) to handle logging, auth, validation.

Express takes away the boilerplate so you can focus on your application logic.

Creating your first Express server

Let's start from zero. First, initialize a project and install Express:

npm init -y
npm install express
Enter fullscreen mode Exit fullscreen mode

Then create a file, say server.js:

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

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

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

Run it with node server.js, and open http://localhost:3000. You'll see the greeting. This is the absolute minimal Express server.

Handling GET requests

GET requests are used to fetch data. Express makes it very intuitive.

1. Simple GET route

app.get('/hello', (req, res) => {
  res.send('Hello World');
});
Enter fullscreen mode Exit fullscreen mode

2. Using route parameters

app.get('/users/:id', (req, res) => {
  const userId = req.params.id;
  res.send(`User ID: ${userId}`);
});
Enter fullscreen mode Exit fullscreen mode

A request to /users/42 returns User ID: 42. Express automatically parses the :id segment.

3. Query string parameters

app.get('/search', (req, res) => {
  const query = req.query.q;
  const page = req.query.page || 1;
  res.json({ query, page });
});
Enter fullscreen mode Exit fullscreen mode

A request to /search?q=express&page=2 gives { "query": "express", "page": "2" }.

4. Returning JSON data

app.get('/api/users', (req, res) => {
  const users = [
    { id: 1, name: 'Satya' },
    { id: 2, name: 'Priya' }
  ];
  res.json(users); // sets Content-Type to application/json automatically
});
Enter fullscreen mode Exit fullscreen mode

res.json() is the go‑to for API responses.

Handling POST requests

POST requests typically create new resources. To process them, you need to read the request body. Express's built-in middleware express.json() does exactly that.

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

app.post('/api/users', (req, res) => {
  const { name, email } = req.body;
  // In a real app, you'd save to a database
  const newUser = { id: Date.now(), name, email };
  res.status(201).json(newUser);
});
Enter fullscreen mode Exit fullscreen mode

Test it with a client that sends JSON:

{
  "name": "Rahul",
  "email": "rahul@example.com"
}
Enter fullscreen mode Exit fullscreen mode

The response will be 201 Created with the new user object.

You can also handle form submissions using express.urlencoded({ extended: true }) if you're dealing with traditional HTML forms.

Sending responses

Express provides multiple methods to send responses, each suitable for different scenarios:

  • res.send(string) – sends a plain text or HTML string; sets Content-Type accordingly.
  • res.json(object) – sends a JSON object with proper header.
  • res.status(code).json(object) – sets the status code and sends JSON.
  • res.sendFile(path) – streams a file from disk.
  • res.redirect(url) – redirects to another URL.
  • res.download(path) – prompts the browser to download a file.

You can also chain status codes: res.status(404).send('Not Found').

Example:

app.get('/file', (req, res) => {
  res.sendFile(__dirname + '/sample.pdf');
});
Enter fullscreen mode Exit fullscreen mode

Express is flexible and doesn't force one way of responding.

Conclusion

Express.js strips away the repetitive plumbing of raw Node.js HTTP servers and lets you define routes declaratively, making your code cleaner and faster to write. With just a few lines, you can set up a server, handle GET and POST requests, parse inputs, and send proper responses. This is the foundation of every Node.js backend I've built.

Let's recap:

  • Express is a minimal framework that simplifies creating HTTP servers in Node.js.
  • It offers clean routing with methods like app.get, app.post, app.put, etc.
  • Route parameters (req.params), query strings (req.query), and body parsing (req.body) are built in.
  • Response helpers (res.json, res.send, res.status) make sending data back straightforward.
  • Compared to raw Node, Express eliminates boilerplate and reduces developer friction.

Now that you're comfortable with routes and request handling, the next step is to build more complex applications, connect to databases, and structure larger codebases. We'll continue that journey in the next post.


Hope you found this helpful! If you spot any mistakes or have suggestions, let me know. You can find me on LinkedIn and X, where I post more about web development.

Top comments (0)