Skip to main content

Command Palette

Search for a command to run...

Creating Routes and Handling Requests with Express

Published
7 min read
Creating Routes and Handling Requests with Express

Node.js has a built-in HTTP module, but writing servers with it is verbose. Express.js simplifies everything. It handles routing, request parsing, and response sending with clean, simple code.

What is Express?

Express is a lightweight web framework for Node.js. It makes building web applications easier by providing helpful features for routing, middleware, and responses.

Without Express, here's a basic Node.js server:

import http from 'http';

const server = http.createServer((req, res) => {
  if (req.url === '/' && req.method === 'GET') {
    res.statusCode = 200;
    res.setHeader('Content-Type', 'text/plain');
    res.end('Home page');
  } else if (req.url === '/about' && req.method === 'GET') {
    res.statusCode = 200;
    res.setHeader('Content-Type', 'text/plain');
    res.end('About page');
  } else {
    res.statusCode = 404;
    res.end('Not found');
  }
});

server.listen(3000);

This gets messy quickly. With multiple routes, you'd have many if-else statements.

With Express, the same thing is much simpler:

import express from 'express';

const app = express();

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

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

app.listen(3000);

Much cleaner. Express handles the complexity.

Setting Up Express

First, create a project and install Express:

npm init -y
npm install express

Create a file called server.js:

import express from 'express';

const app = express();

app.listen(3000, () => {
  console.log('Server running on http://localhost:3000');
});

Run it:

node server.js

You have a running server. It doesn't do anything yet, but it's listening on port 3000.

Understanding Routes

A route is a combination of:

  • HTTP method (GET, POST, PUT, DELETE, etc.)

  • URL path (/home, /users, /posts/123, etc.)

  • Handler function (what to do when this route is called)

The basic structure is:

app.METHOD(PATH, HANDLER);

For example:

app.get('/home', (req, res) => {
  res.send('This is the home page');
});

This means: "When someone sends a GET request to /home, execute this function."

GET Requests

GET requests fetch data. They're read-only and don't change anything.

import express from 'express';

const app = express();

app.get('/', (req, res) => {
  res.send('Welcome to the home page');
});

app.get('/about', (req, res) => {
  res.send('This is the about page');
});

app.get('/contact', (req, res) => {
  res.send('Contact us at info@example.com');
});

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

Now visit:

  • http://localhost:3000/ → "Welcome to the home page"

  • http://localhost:3000/about → "This is the about page"

  • http://localhost:3000/contact → "Contact us at..."

POST Requests

POST requests send data to the server. They typically create new resources.

For this, you need to parse incoming data. Express provides middleware for this:

import express from 'express';

const app = express();

// Parse JSON data
app.use(express.json());

app.get('/', (req, res) => {
  res.send('GET request received');
});

app.post('/submit', (req, res) => {
  const { name, email } = req.body;
  res.send(`Thank you, \({name}. We'll email \){email}`);
});

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

When someone sends a POST request to /submit with JSON data, Express parses it and stores it in req.body.

To test this, you'd need a tool like Postman or curl:

curl -X POST http://localhost:3000/submit \
  -H "Content-Type: application/json" \
  -d '{"name":"John","email":"john@example.com"}'

Response: "Thank you, John. We'll email john@example.com"

URL Parameters

Routes can have dynamic parts. Parameters start with a colon:

import express from 'express';

const app = express();

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

app.get('/posts/:postId/comments/:commentId', (req, res) => {
  const { postId, commentId } = req.params;
  res.send(`Comment \({commentId} on post \){postId}`);
});

app.listen(3000);

Examples:

  • GET /users/123 → "Getting user 123"

  • GET /posts/456/comments/789 → "Comment 789 on post 456"

Query Parameters

Query parameters come after a question mark and are optional:

import express from 'express';

const app = express();

app.get('/search', (req, res) => {
  const query = req.query.q;
  const limit = req.query.limit;
  res.send(`Searching for "\({query}", limit \){limit}`);
});

app.listen(3000);

Examples:

  • GET /search?q=javascript → "Searching for 'javascript', limit undefined"

  • GET /search?q=node&limit=10 → "Searching for 'node', limit 10"

Response Methods

Express provides several ways to send responses:

import express from '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', status: 'success' });
});

// Send HTML
app.get('/html', (req, res) => {
  res.send('<h1>This is HTML</h1>');
});

// Send with status code
app.get('/error', (req, res) => {
  res.status(404).send('Not found');
});

// Redirect
app.get('/old-path', (req, res) => {
  res.redirect('/new-path');
});

app.listen(3000);

Handling All HTTP Methods

Express supports all standard HTTP methods:

import express from 'express';

const app = express();

app.use(express.json());

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

app.post('/users', (req, res) => {
  res.json({ message: 'Create new user' });
});

app.put('/users/:id', (req, res) => {
  res.json({ message: `Update user ${req.params.id}` });
});

app.delete('/users/:id', (req, res) => {
  res.json({ message: `Delete user ${req.params.id}` });
});

app.listen(3000);

Handling 404 Errors

When a route doesn't exist, Express returns a 404 error by default. You can customize this:

import express from 'express';

const app = express();

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

// Catch all unmatched routes
app.use((req, res) => {
  res.status(404).send('Page not found');
});

app.listen(3000);

The order matters. This "catch-all" route must be last, otherwise it will intercept all requests.

A Complete Example

Let's build a simple blog API:

import express from 'express';

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

// Sample data
let posts = [
  { id: 1, title: 'First Post', content: 'Hello world' },
  { id: 2, title: 'Second Post', content: 'Another post' }
];

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

// GET single post
app.get('/posts/:id', (req, res) => {
  const post = posts.find(p => p.id === parseInt(req.params.id));
  if (!post) {
    return res.status(404).send('Post not found');
  }
  res.json(post);
});

// CREATE new post
app.post('/posts', (req, res) => {
  const newPost = {
    id: posts.length + 1,
    title: req.body.title,
    content: req.body.content
  };
  posts.push(newPost);
  res.status(201).json(newPost);
});

// UPDATE post
app.put('/posts/:id', (req, res) => {
  const post = posts.find(p => p.id === parseInt(req.params.id));
  if (!post) {
    return res.status(404).send('Post not found');
  }
  post.title = req.body.title || post.title;
  post.content = req.body.content || post.content;
  res.json(post);
});

// DELETE post
app.delete('/posts/:id', (req, res) => {
  const index = posts.findIndex(p => p.id === parseInt(req.params.id));
  if (index === -1) {
    return res.status(404).send('Post not found');
  }
  const deleted = posts.splice(index, 1);
  res.json(deleted);
});

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

This simple API lets you create, read, update, and delete posts.

Common Mistakes

Mistake 1: Forgetting middleware

// Wrong - won't parse JSON
app.post('/data', (req, res) => {
  console.log(req.body); // undefined
});

// Correct
app.use(express.json());
app.post('/data', (req, res) => {
  console.log(req.body); // has data
});

Mistake 2: Forgetting to return after sending response

app.get('/users/:id', (req, res) => {
  if (!id) {
    res.status(400).send('Invalid ID');
    // BUG: continues executing below
  }
  res.send('User data');
});

// Better
app.get('/users/:id', (req, res) => {
  if (!id) {
    return res.status(400).send('Invalid ID');
  }
  res.send('User data');
});

Key Takeaways

  • Express simplifies building web servers with Node.js

  • Routes combine HTTP method, path, and handler function

  • GET requests fetch data, POST requests send data

  • URL parameters (:id) capture dynamic values

  • Query parameters (?key=value) pass optional filters

  • Middleware processes requests before they reach handlers

  • Always return after sending a response

  • Use appropriate HTTP status codes

Express makes building web applications enjoyable. These fundamentals are the foundation for all Express applications.