A Practical Guide to Express Routing

Summary: in this tutorial, you’ll learn about Express routing, a process of defining endpoints (URIs) and responding when the client requests those endpoints.

Introduction to Express Routing

An endpoint is a specific URL (Uniform Resource Locator) or path and an HTTP method (GET, POST, PUT, …)

Typically, an endpoint represents a specific resource or function which a client can perform such as fetching data, modifying data, and deleting records.

For example, the following endpoint retrieves a list of todos:

GET /todos

In this endpoint, The path is /todos and the HTTP method is GET.

The following endpoint creates a new todo item:

POST /todos

The path is also /todos but the HTTP is POST.

In Express, routing is about setting up endpoints and defining how the app should respond when a client requests to those endpoints.

A route defines how your app handles requests to an endpoint. To define a route, you use a method of the Express object such as get, post, put, patch, and delete and a

Here’s the syntax for defining a route:

app.method(path, handler);Code language: JavaScript (javascript)

In this syntax:

  • app is an instance of the Express application.
  • method is an HTTP request method in lowercase e.g., get, post, put, patch, and delete.
  • path is the path on the server.
  • handler is a function executed when the route is matched. The route handler has access to the req and res objects, which correspond to HTTP request and response objects.

Basic route examples

The following defines a route for the GET requests to the root URL (‘/’):

app.get('/', (req, res) => {
   res.send('Hello World');
});Code language: PHP (php)

Similarly, the following defines a route for the GET request to '/about':

app.get('/about', (req, res) => {
   res.send('About Page');
});Code language: PHP (php)

The following example defines a route for POST requests to '/login':

app.post('/login', (req, res) => {
   res.send('Authenticated');
});Code language: PHP (php)

Express routing examples

Let’s create an Express app with a couple of routes.

Creating an Express app

Step 1. Open your terminal or command prompt and create a new directory such as express-routing:

mkdir express-routing
cd express-routingCode language: JavaScript (javascript)

Step 2. Run the following npm command to initialize the project:

npm init -yCode language: JavaScript (javascript)

This will create a package.json file that includes configurations, scripts, and dependencies.

Step 3. Install Express by running the following npm command from the terminal:

npm install expressCode language: JavaScript (javascript)

Step 4. Add the .env file to the project and set the PORT to 3000:

PORT=3000Code language: JavaScript (javascript)

Step 5. Add the following key/value pair to the package.json file to instruct Node.js to use ES modules:

"type": "module"Code language: JavaScript (javascript)

Also, change the scripts section to the following:

  "scripts": {
    "start": "node --watch --env-file=.env index.js"
  },Code language: JavaScript (javascript)

This allows you to run the npm start command to execute the index.js file with the --watch flag and load the .env file.

Step 6. Create an index.js file with the following code:

import express from 'express';

const app = express();
const PORT = process.env.PORT || 3000;


app.listen(PORT, () => {
  console.log(`Server is listening on port ${PORT}`);
});Code language: JavaScript (javascript)

Step 7. Start the server by running the following command from the terminal:

npm startCode language: JavaScript (javascript)

Step 8. Create an api.http file for making requests to routes using REST Client extension in VS Code.

Adding routes

Step 1. Create a new module todos.js module that exports an array of todo items and a function that returns the next todo id:

export const todos = [
  { id: 1, title: 'Learn Node.js', completed: true },
  { id: 2, title: 'Master Express', completed: false },
  { id: 3, title: 'Build an API Server', completed: false },
];

export const nextTodoId = () => {
  // get the next id for the todo
  let maxId = 1;
  todos.forEach((todo) => {
    if (todo.id > maxId) {
      maxId = todo.id;
    }
  });

  return maxId + 1;
};Code language: JavaScript (javascript)

Step 2. Import the todos array and nextTodoId function from the todo.js module into the index.js module:

import { todos, nextTodoId } from './todos.js';Code language: JavaScript (javascript)

Step 3. Define a route for a GET request to the '/api/todos/' endpoint:

import express from 'express';
import { todos, nextTodoId} from './todos.js';

const app = express();

const PORT = process.env.PORT || 3000;

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

app.listen(PORT, () => {
  console.log(`Server is listening on port ${PORT}`);
});Code language: JavaScript (javascript)

Step 4. Modify the api.http by adding the HTTP request to the /api/todos endpoint:

# Get all todos items
GET http://localhost:3000/api/todos/Code language: JavaScript (javascript)

When a GET request is made to the /api/todos/ endpoint, the route handler is executed, which responds with the todos array as JSON data:

[
  {
    "id": 1,
    "title": "Learn Node.js",
    "completed": true
  },
  {
    "id": 2,
    "title": "Master Express",
    "completed": false
  },
  {
    "id": 3,
    "title": "Build an API Server",
    "completed": false
  }
]Code language: JavaScript (javascript)

Adding a query string to a route

To retrieve a list of completed or incompleted todo items, you can append a query string to the route /api/todos like this:

http://localhost:3000/api/todos/?completed=trueCode language: JavaScript (javascript)

or

http://localhost:3000/api/todos/?completed=falseCode language: JavaScript (javascript)

Everything after the question mark (?) in the URL is called a query string. In our example, the query string is the following:

completed=falseCode language: JavaScript (javascript)

A query string may include one or more parameters, where each parameter is represented by a unique key/value pair separated by an equals sign (=).

To access the query string from the URL in Express, you use the req.query object like this:

req.queryCode language: JavaScript (javascript)

For example:

// ... 

app.get('/api/todos/', (req, res) => {
  console.log(req.query);
});

// ...Code language: JavaScript (javascript)

If you make a GET request to the endpoint http://localhost:3000/api/todos/?completed=false, you’ll see the following query object in the console:

{ completed: 'false' }Code language: JavaScript (javascript)

To access the completed parameter in the query string, you can use the following:

app.get('/api/todos/', (req, res) => {
  console.log(req.query.completed);
});Code language: JavaScript (javascript)

The following modifies the /api/todos/ endpoint to return the incompleted/completed todo items if the completed query string is available:

app.get('/api/todos/', (req, res) => {
  if (req?.query?.completed) {
    const isCompleted = req.query.completed === 'true';
    const filteredTodos = todos.filter(
      (todo) => todo.completed === isCompleted
    );
    res.send(filteredTodos);
  }

  res.send(todos);
});Code language: JavaScript (javascript)

Here’s the updated version of the api.http file:

# Get all todos items
GET http://localhost:3000/api/todos/

### 

# Get incompleted todo items:
GET http://localhost:3000/api/todos/?completed=false

### 

# Get completed todo items:
GET http://localhost:3000/api/todos/?completed=trueCode language: JavaScript (javascript)

Handling route parameters

A route segment is a named URL segment used to capture the value specified at its position in the URL. For example:

http://localhost:3000/api/todos/1Code language: JavaScript (javascript)

In this URL, 1 is a route parameter representing the todo id.

In Express, you can set up the route with an id like this:

/api/todo/:idCode language: JavaScript (javascript)

To access route parameters, you use the req.params object:

req.paramsCode language: JavaScript (javascript)

In the route /api/todo/:id, the id is a key to the req.params object. So you can access it using the following:

req.params.idCode language: JavaScript (javascript)

Note that the values of route parameters are always strings. Therefore, you need to convert them into the values of desired types.

The following defines a route that returns a todo item by an id:

app.get('/api/todos/:id', (req, res) => {
  const { id } = req.params;

  const todoId = parseInt(id);
  if (isNaN(todoId)) {
    res.status(400).send(`Invalid id ${id}`);
  }

  const todo = todos.find((todo) => todo.id === todoId);
  if (todo) res.status(200).send(todo);

  res.status(404).send(`Todo with id ${id} not found`);
});Code language: JavaScript (javascript)

How it works.

First, get the id from the req.params object by destructing the object:

const { id } = req.params;Code language: JavaScript (javascript)

Second, convert the id value to an integer. If the id is not a number, respond with HTTP status code 400 with an error message:

const todoId = parseInt(id);
if (isNaN(todoId)) {
   res.status(400).send(`Invalid id ${id}`);
}Code language: JavaScript (javascript)

Third, find the todo with the id and return it as JSON to the client:

const todo = todos.find((todo) => todo.id === id);
if (todo) res.send(todo);Code language: JavaScript (javascript)

Finally, return the HTTP status code 404 if the todo with the id is not found:

res.status(404).send(`Todo with id ${id} not found`);Code language: JavaScript (javascript)

Putting it all together

The following shows the completed index.js module:

import express from 'express';
import { todos, nextTodoId } from './todos.js';

const app = express();

const PORT = process.env.PORT || 3000;

app.get('/api/todos/', (req, res) => {
  if (req?.query?.completed) {
    const isCompleted = req.query.completed === 'true';
    const filteredTodos = todos.filter(
      (todo) => todo.completed === isCompleted
    );
    res.send(filteredTodos);
  }

  res.send(todos);
});

app.get('/api/todos/:id', (req, res) => {
  const { id } = req.params;

  const todoId = parseInt(id);
  if (isNaN(todoId)) {
    res.status(400).send(`Invalid id ${id}`);
  }

  const todo = todos.find((todo) => todo.id === todoId);
  if (todo) res.status(200).send(todo);

  res.status(404).send(`Todo with id ${id} not found`);
});

app.listen(PORT, () => {
  console.log(`Server is listening on port ${PORT}`);
});
Code language: JavaScript (javascript)

Download the Express Routing example source code

Summary

  • An endpoint refers to a specific URL (or path) and an HTTP method that a client can use to access a resource or perform an operation function.
  • A route defines how the app responds to a particular endpoint.
  • Use methods of Express app object to define routes.
Was this tutorial helpful ?