Page Content

Tutorials

What is Express Routing in Node.js? With Code Examples

Express Routing in Node.js

Routing is the process of figuring out how an application reacts to a client request sent to a certain endpoint, which comprises a specific HTTP request type (such as GET, POST, PUT, or DELETE) and a URI (or path). By letting you create handlers for various URL and HTTP method combinations, Express simplifies this process.

Previously, managing alternative pathways with simply the native http module required using if statements to verify request.url, which might result in a “giant mess” and a “massive callback function” as the program expanded. To manage these routing patterns, Express offers a more streamlined and manageable solution.

Code Example: Basic Express “Hello World” with Routing

const express = require('express'); // Import the express module 
const app = express(); // Create an Express application instance 
const port = process.env.PORT || 3000; // Define the port 

// Route for the home page (HTTP GET request to '/')
app.get('/', (req, res) => { // Handles GET requests to the root path 
    res.send('Hello, World!'); // Sends "Hello, World!" as the response 
});

// Route for a 'wiki' page
app.get('/wiki', (req, res) => { // Another GET route 
    res.send('This is wiki page.');
});

// Start the server to listen on the specified port
app.listen(port, () => { // Make the app listen on port 3000 
    console.log(`Server listening on http://localhost:${port}`); // Log server status 
});

// Catch-all route for any unlisted URLs (404 Not Found) - placed last 
app.use((req, res) => { // `app.use` can also be used for routing 
    res.send('404-Page Not Found'); // Send a 404 message 
});

Code Output Example (after running node app.js and visiting http://localhost:3000):

Server listening on http://localhost:3000

(In browser at http://localhost:3000): Hello, World!

Defining Routes for Different HTTP Methods

You can use methods that match HTTP verbs to set up route handlers in Express.

  1. app.get(): Data can be retrieved from the server using the app.get() method. The app.get() callback function is called when a client sends an HTTP GET request to a certain path.
  2. For instance, if you go to http://localhost:3000/ in a browser, “Hello, World!” appears. The same would be true for http://localhost:3000/anime, which would yield a JSON array of shows. You can send back different kinds of material, like as text, HTML, or JSON, using the res.send() method.
  3. app.post(): Form data and other data are sent to the server using this function. The request body must frequently be parsed into a useable format, usually an object accessible via req.body, using middleware like as body-parser before handling POST requests. Additionally, Express has express.json() for parsing incoming JSON.
  4. This configuration enables the processing of data sent by a client (for example, through an API request or form submission) by your Express application.
  5. Other HTTP Methods: App.put() and app.delete() are two more HTTP methods that Express supports. They are used for updating and deleting, respectively. These techniques follow the guidelines for designing RESTful APIs. It is possible to use app.all() or app.use() for operations that apply to all HTTP methods for a given path. To apply different handlers for various HTTP methods, you can also chain route declarations for a single path.

Code Example: Basic CRUD API with Express HTTP Methods

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

// Example user data (in a real app, this would be a database)
let users = [ // Dummy data 
    { id: 1, name: "John Doe", age: 23, email: "john@doe.com" },
    { id: 2, name: "Jane Smith", age: 28, email: "jane@smith.com" }
];

// GET all users
app.get('/api/users', (req, res) => { // Handles GET request to /api/users 
    res.json(users); // Sends users array as JSON response 
});

// GET a specific user by ID (using a route parameter)
app.get('/api/users/:id', (req, res) => { // ':id' is a route parameter 
    const userId = parseInt(req.params.id); // Access parameter value via req.params 
    const user = users.find(u => u.id === userId);
    if (user) {
        res.json(user);
    } else {
        res.status(404).send('User not found');
    }
});

// POST to create a new user
app.post('/api/users', (req, res) => { // Handles POST request to /api/users 
    // In a real application, you'd use middleware like body-parser to access req.body 
    // For this example, let's assume req.body.user is available
    const newUser = req.body.user || { id: users.length + 1, name: "New User", age: 30, email: "new@user.com" };
    users.push(newUser);
    res.status(201).send('User has been added successfully'); // 201 Created status 
});

// PUT to update a user by ID
app.put('/api/users/:id', (req, res) => { // Handles PUT request to /api/users/:id 
    const userId = parseInt(req.params.id);
    const userIndex = users.findIndex(u => u.id === userId);
    if (userIndex !== -1) {
        // Assume req.body contains updated user data
        users[userIndex] = { ...users[userIndex], ...req.body.user };
        res.send('User updated successfully');
    } else {
        res.status(404).send('User not found');
    }
});

// DELETE a user by ID
app.delete('/api/users/:id', (req, res) => { // Handles DELETE request to /api/users/:id 
    const userId = parseInt(req.params.id);
    const initialLength = users.length;
    users = users.filter(u => u.id !== userId);
    if (users.length < initialLength) {
        res.send('User deleted successfully');
    } else {
        res.status(404).send('User not found');
    }
});

app.listen(port, () => {
    console.log(`API server listening on port ${port}`);
});

When a client sends a request to /api/users/10req.params.id would contain the string “10”. The req.params object holds all the named route parameters sent with the URL.

Route Parameters ()

In order to retrieve dynamic data from URLs, route parameters are an essential component. Route parameters are placeholders in the URL path that can be used in place of specifying a distinct route for each potential ID or name.

  • Defining Routes with Parameters: In the route path, you create a route parameter by placing a colon (:) before its name (e.g., '/profile/:id'). Express is instructed to collect any value that comes in that URL segment.
  • Extracting Data with req.params: The req.params object then provides access to the values of these parameters. An object called req.params holds the additional parameters that were delivered with the URL. Req.params would have properties like userId and bookId for a route specified as '/users/:userId/books/:bookId'.
  • The server will display “Displaying profile for user with ID: 123” in response to a browser query that goes to http://localhost:3000/profile/123. This indicates that req.params.id was successful in capturing 123 from the URL. This would also retrieve “electronics” and “laptop-x” from http://localhost:3000/products/electronics/laptop-x.

Express is a potent tool for creating scalable and stable web apps and APIs because of its systematic approach to routing, which makes URLs dynamic and easy to understand.

Modular Express Applications

For larger applications, defining all routes in a single file can become unwieldy. Express allows you to organize your endpoints into separate route files using express.Router().

Code Example: Modular Express Routing routes/users.js

const express = require('express');
const router = express.Router(); // Create a new router instance 

// Define routes on the router object
router.get('/', (req, res) => { // Handles GET to /api/users
    res.json({ message: 'Get all users from user router' });
});

router.post('/', (req, res) => { // Handles POST to /api/users
    res.json({ message: 'Create a new user from user router' });
});

module.exports = router; // Export the router

app.js

const express = require('express');
const app = express();
const usersRouter = require('./routes/users'); // Import the user router 

app.use('/api/users', usersRouter); // Mount the router at a specific base path 

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

This structure makes your application more modular, customizable, and reusable.

Serving Static Files

Express can easily serve static assets like HTML, CSS, JavaScript, and images from a designated directory. This is done using express.static() middleware.

Code Example: Serving Static Files

const express = require('express');
const path = require('path'); // Node's built-in path module 
const app = express();
const port = 3000;

// Define the path to your static files (e.g., a 'public' folder)
const publicDirectoryPath = path.join(__dirname, 'public'); // Joins path segments 

// Serve static files from the 'public' directory
app.use(express.static(publicDirectoryPath)); // Middleware to serve static files 

// Example: If you have an index.html in 'public', it will be served at '/'
// Any other files like 'styles.css', 'app.js', 'image.png' will be available at /styles.css, /app.js, /image.png

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

With this setup, any file placed in the public directory (e.g., public/index.htmlpublic/css/styles.css) can be accessed directly in the browser via http://localhost:3000/index.html or http://localhost:3000/css/styles.css.

Templating Engines with Routing

Express supports various templating engines (like EJS, Handlebars, Pug/Jade, Mustache) to render dynamic HTML pages. This allows you to embed data from your server-side code directly into HTML templates.

Code Example: Using EJS Template Engine with Express First, install EJS: npm install ejs. app.js

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

app.set('view engine', 'ejs'); // Set EJS as the view engine 
app.set('views', path.join(__dirname, 'views')); // Set the directory for view files 

app.get('/', (req, res) => {
    res.render('index', { // Render the 'index.ejs' file 
        title: 'My Dynamic Page',
        message: 'Welcome to the world of EJS!'
    });
});

app.listen(3000, () => {
    console.log('Server listening on port 3000 for templating example.');
});

views/index.ejs

<!DOCTYPE html>
<html>
<head>
    <title><%= title %></title> <!-- Access variables passed to render [80] -->
</head>
<body>
    <h1><%= message %></h1>
</body>
</html>

When you access http://localhost:3000/, Express will render index.ejs, replacing <%= title %> and <%= message %> with the values provided in the res.render() call.

Middleware and Error Handling

Express is built on the concept of middleware: a stack of functions that have access to the req (request), res (response), and the next middleware function in the application’s request-response cycle. Middleware can execute any code, modify the request and response objects, end the request-response cycle, and call the next middleware function.

The next() callback is crucial; calling it without arguments passes control to the next matching route or middleware. Calling next(err) passes an error to error-handling middleware.

Code Example: Middleware and Error Handling

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

// Custom logging middleware
app.use((req, res, next) => { // `app.use` registers middleware 
    console.log(`New request: ${req.method} ${req.url} at ${new Date().toISOString()}`); // Log request details 
    next(); // Pass control to the next middleware/route handler 
});

// Route with a simulated error
app.get('/error-test', (req, res, next) => {
    // Simulate an error
    const error = new Error('Something went wrong on this route!'); // Create an error object 
    error.status = 500;
    next(error); // Pass the error to the error handling middleware 
});

// Default route
app.get('/', (req, res) => {
    res.send('Welcome to the homepage!');
});

// Error handling middleware - must have 4 arguments (err, req, res, next) 
app.use((err, req, res, next) => { // Defined after all other routes and logic 
    console.error(err.stack); // Log the error stack for debugging 
    res.status(err.status || 500).send(`Error: ${err.message || 'Internal Server Error'}`); // Send error response 
});

// 404 Not Found handler - placed after all other routes and middleware
app.use((req, res, next) => { // This will only be reached if no other route or middleware handled the request
    res.status(404).send('Resource not found'); // Send 404 for unhandled routes 
});

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

Express routing provides a robust and flexible way to build web applications, moving beyond simple request-URL comparisons to a powerful system of HTTP method handling, dynamic parameters, modular organization, static file serving, templating, and middleware integration. It acts like a sophisticated air traffic controller for your web server, directing incoming requests to the correct functions based on their destination, type, and even their contents, ensuring smooth and efficient operation.

Index