Creating Your First Express.js Server

Write your first Express.js application and understand the basic structure of an Express.js server. Learn about routing and handling HTTP requests.


Mastering Express.js: Routing and Handling HTTP Requests

Introduction

This section delves into the crucial concepts of routing and handling HTTP requests within Express.js. Understanding these concepts is fundamental for building robust and scalable web applications. We'll cover how Express.js provides mechanisms to define routes that respond to different HTTP methods and URLs, and how to effectively handle the data sent in those requests.

Routing in Express.js

Routing refers to determining how an application responds to a client request to a specific endpoint, which is a URI (Uniform Resource Identifier) and a specific HTTP request method (GET, POST, PUT, DELETE, etc.). Express.js provides a clean and organized way to define these routes.

Defining Routes

Routes are defined using the app object and HTTP method functions like app.get(), app.post(), app.put(), and app.delete().

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

// GET route for the homepage
app.get('/', (req, res) => {
  res.send('Hello World!');
});

// POST route to create a new user
app.post('/users', (req, res) => {
  // Process the user data here
  res.send('User created successfully!');
});

app.listen(port, () => {
  console.log(`Example app listening at http://localhost:${port}`);
}); 

In the above example:

  • app.get('/', (req, res) => { ... }); defines a route that responds to GET requests to the root URL ("/").
  • app.post('/users', (req, res) => { ... }); defines a route that responds to POST requests to the "/users" URL. This is typically used for creating new resources.
  • The req object represents the HTTP request and contains information about the request, such as headers, query parameters, and body data.
  • The res object represents the HTTP response that the Express app sends when it receives a request.

Route Parameters

Routes can also include parameters, allowing you to capture dynamic values from the URL. These parameters are accessible through req.params.

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

In this example, visiting /users/123 would result in userId being set to "123" within the route handler.

Route Middleware

Middleware functions can be added to routes to perform tasks before or after the route handler. This can be used for authentication, logging, or data validation.

 function authenticateUser(req, res, next) {
  // Authentication logic here
  if (/* User is authenticated */ true) { //Replace with actual authentication check
    next(); // Proceed to the next middleware or the route handler
  } else {
    res.status(401).send('Unauthorized');
  }
}

app.get('/protected', authenticateUser, (req, res) => {
  res.send('Protected resource accessed!');
}); 

In this example, `authenticateUser` middleware will run *before* the route handler for `/protected`. Only authenticated users can proceed to access the protected resource.

Handling HTTP Requests

Handling HTTP requests involves processing the data sent by the client (request) and constructing an appropriate response.

Request Object (req)

The req object provides access to the request's headers, query parameters, body, and more.

  • req.params: Contains route parameters (e.g., /users/:userId).
  • req.query: Contains query string parameters (e.g., /search?q=keyword).
  • req.body: Contains the request body, typically used for POST and PUT requests. You'll need middleware like express.json() or express.urlencoded() to parse the request body.
  • req.headers: Contains the request headers.
  • req.method: Contains the HTTP method used for the request (GET, POST, etc.).

Response Object (res)

The res object allows you to send data back to the client.

  • res.send(body): Sends the HTTP response. The body parameter can be a string, an object (which Express will automatically convert to JSON), or a buffer.
  • res.json(obj): Sends a JSON response.
  • res.status(statusCode): Sets the HTTP status code (e.g., 200 for OK, 404 for Not Found, 500 for Internal Server Error).
  • res.redirect(url): Redirects the client to a different URL.
  • res.render(view, locals): Renders a view template (e.g., using EJS or Pug) and sends the resulting HTML to the client. This is useful for dynamic web pages.
  • res.sendFile(path): Sends a file as the response.
  • res.download(path, [filename], [options], [callback]): Prompts the client to download a file.

Example: Handling a POST Request with Request Body

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

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

app.post('/api/items', (req, res) => {
  const newItem = req.body;

  // Validate the data
  if (!newItem.name || !newItem.description) {
    return res.status(400).json({ error: 'Name and description are required.' });
  }

  // Process the new item (e.g., save it to a database)
  console.log('Received item:', newItem);

  // Send a success response
  res.status(201).json({ message: 'Item created successfully!', item: newItem });
});

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

This example demonstrates how to use express.json() middleware to parse JSON request bodies and access the data through req.body. It also includes basic data validation and sends an appropriate response based on the outcome.

Error Handling

Proper error handling is crucial for building reliable applications. Express.js provides several mechanisms for handling errors.

Middleware Error Handlers

You can define middleware functions that specifically handle errors. These functions have four arguments: err, req, res, and next. The presence of the err argument signifies that the middleware is an error handler.

 app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).send('Something broke!');
}); 

This error handler will catch any errors that occur in preceding middleware or route handlers. It logs the error stack trace and sends a generic error message to the client.

Using next(err)

Within a route handler or middleware, you can pass an error to the next() function to pass control to the next error-handling middleware.

 app.get('/data', (req, res, next) => {
  try {
    // Some code that might throw an error
    throw new Error('Something went wrong!');
  } catch (error) {
    next(error); // Pass the error to the error-handling middleware
  }
}); 

In the case of an uncaught exception within a route handler, wrapping your code in a try/catch and passing the error to `next()` is recommended for proper error management.

Summary

Routing and handling HTTP requests are core concepts in Express.js. By understanding how to define routes, access request data, and send appropriate responses, you can build powerful and well-structured web applications. Remember to implement robust error handling to ensure application stability.