Lecture_Notes/Non-DSA Notes/FullStack-LLD/LLD-4 (MERN)/FullStack-2: Introduction to Express.md
mrinal1224 98aa698dd9 push
2024-05-07 12:57:28 +05:30

18 KiB

Agenda

Topics to cover in Express:

We will try to cover most of these topics in today's sessions and the remaining in the next.

  • What is express
  • How to use express with node
  • Http methods-get,post,put,delete, patch
  • Postman-to test your api endpoints
  • Middlewares It is going to be a bit challenging, advanced, but very interesting session covering topics that are asked very frequently in interviews.

So let's start.

Express Module

In Node.js, the Express module is a popular framework for building web applications and APIs. It simplifies the process of handling HTTP requests and responses and provides a structured way to create routes and middleware. Let's explore the Express module with some examples.

Example 1: Setting Up an Express Application

First, you need to install Express in your project:

npm install express --save

Now, let's create a simple Express application:

// Import the Express module
const express = require('express');

// Create an Express application
const app = express();

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

// Start the server
const port = 3000;
app.listen(port, () => {
  console.log(`Server is running on port ${port}`);
});

In this example:

  • We import the express module and create an Express application instance.
  • We define a route for the root URL ("/") using app.get(), which responds with "Hello, Express!" when accessed via a GET request.
  • We start the server on port 3000.

Example 2: Express Routes

Express allows you to define multiple routes for different URL paths and HTTP methods. Here's an example:

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

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

app.post('/data', (req, res) => {
  res.send('Received a POST request.');
});

In this example:

  • We define a route for the root URL ("/") and an "/about" page that responds to GET requests.
  • We also define a route for "/data" that responds to POST requests.

Postman

Postman is a popular and powerful tool used by developers and testers to simplify the process of testing APIs (Application Programming Interfaces). It provides an intuitive graphical user interface (GUI) that allows you to send HTTP requests to your API endpoints, inspect responses, and automate API testing. Here are some key features and uses of Postman:

  1. Creating a Request:

    • Open Postman and click the "New" button to create a new request.
    • Choose the HTTP method (e.g., GET, POST) for your request.
  2. Request URL:

    • Enter the URL of the API endpoint you want to test in the request URL field.
  3. Headers:

    • You can set request headers by clicking on the "Headers" tab.
    • Headers are used to pass additional information to the server, such as authentication tokens or content type.
  4. Request Body:

    • If your request requires a request body (e.g., for POST or PUT requests), you can define it in the "Body" tab.
    • You can send data in various formats like JSON, form-data, x-www-form-urlencoded, etc.
  5. Sending a Request:

    • Click the "Send" button to send the request to the specified endpoint.
    • Postman will display the response in the lower part of the window.
  6. Response:

    • You can view the response status code, headers, and body in the response section.
    • You can also format and highlight the response body using the options provided.
  7. Collections:

    • Organize your requests into collections for better management.
    • Create a new collection by clicking the "New" button under "Collections."
  8. Variables:

    • Use environment and global variables to store values that can be reused across requests.
    • Variables are helpful for managing different environments (e.g., development, production) or dynamic data.
  9. Tests and Assertions:

    • Write test scripts to validate the response data.
    • You can use JavaScript to write custom tests.
    • Use the "Tests" tab in the request to add test scripts.

These are some of the basic concepts and features of Postman. It's a versatile tool with many capabilities that can greatly simplify the process of working with APIs, testing, and collaborating with your team. As you become more familiar with Postman, you can explore its advanced features and customization options to suit your specific needs.

Here are simple examples of how to implement POST and DELETE requests in an Express.js application.

POST Request Example:

In this example, we'll create a basic Express application that handles a POST request to add a new user to a list of users.

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

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

// Sample user data (in-memory storage)
let users = [
  { id: 1, name: 'User 1' },
  { id: 2, name: 'User 2' }
];

// POST endpoint to add a new user
app.post('/users', (req, res) => {
  const newUser = req.body;

  // Assign a unique ID to the new user (in a real app, you'd typically use a database)
  const userId = users.length + 1;
  newUser.id = userId;

  // Add the new user to the list
  users.push(newUser);

  res.status(201).json({ message: 'User created', user: newUser });
});

app.listen(port, () => {
  console.log(`Server is running on port ${port}`);
});

To test this, send a POST request to http://localhost:3000/users with a JSON body, for example:

{
  "name": "New User"
}

The server will respond with a JSON message confirming that the user has been created.

DELETE Request Example:

In this example, we'll create an Express application that handles a DELETE request to remove a user from the list.

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

// Sample user data (in-memory storage)
let users = [
  { id: 1, name: 'User 1' },
  { id: 2, name: 'User 2' }
];

// DELETE endpoint to delete a user by ID
app.delete('/users/:id', (req, res) => {
  const userId = parseInt(req.params.id);

  // Find the user index by ID
  const userIndex = users.findIndex(user => user.id === userId);

  if (userIndex === -1) {
    return res.status(404).json({ message: 'User not found' });
  }

  // Remove the user from the array
  users.splice(userIndex, 1);
  
  res.json({ message: 'User deleted' });
});

app.listen(port, () => {
  console.log(`Server is running on port ${port}`);
});

To test this, send a DELETE request to http://localhost:3000/users/1 to delete the user with ID 1.

These examples demonstrate how to implement POST and DELETE requests in an Express.js application. They focus on the core functionality without extensive error handling or database interactions. In practice, you would typically include more robust validation and potentially use a database for data storage.

Middleware

Middleware functions in Express.js are functions that have access to the request (req) and response (res) objects and can perform actions or transformations on them. They are used to handle tasks like parsing request data, authentication, logging, error handling, and more. Middleware functions can be added to your Express application using app.use() or applied to specific routes using app.use() or app.METHOD() (e.g., app.get(), app.post()).

Here's a simple example of how to create and use middleware in an Express.js application:

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

// Custom middleware function
const loggerMiddleware = (req, res, next) => {
  console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
  next(); // Call the next middleware in the chain
};

// Register the middleware globally for all routes
app.use(loggerMiddleware);

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

app.listen(port, () => {
  console.log(`Server is running on port ${port}`);
});

In this example:

  1. We define a custom middleware function called loggerMiddleware. It logs the request method and URL along with a timestamp.

  2. We use app.use() to register the loggerMiddleware, which means it will be executed for all incoming requests.

  3. We have a simple route handler for the root path ("/") that sends a "Hello, Express!" response.

When you run this Express application, every incoming request will trigger the loggerMiddleware to log information about the request. This is just one example of middleware; you can create and use middleware functions for various purposes, including authentication, request validation, error handling, and more.

Middleware functions are executed in the order they are registered with app.use(), so the order of middleware registration matters. You can also apply middleware to specific routes by using app.use() or app.METHOD() for those routes.

For example, if you wanted to apply loggerMiddleware only to a specific route, you could do the following:

app.get('/special', loggerMiddleware, (req, res) => {
  res.send('This route is special!');
});

In this case, the loggerMiddleware will only run for requests to the "/special" route.

Remember that middleware functions can perform various tasks, and you can create custom middleware to suit your application's needs.

Authentication middleware

Authentication middleware in an Express.js application is used to check if a user is authenticated before allowing access to certain routes or resources. Below is a simple example of how to create an authentication middleware to protect routes and ensure users are authenticated before accessing them. We'll use a basic username and password authentication mechanism for demonstration purposes.

Here's an example where we use app.use to protect all routes with the authentication middleware:

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

// Sample user data (in-memory storage)
const users = [
  { id: 1, username: 'user1', password: 'password1' },
  { id: 2, username: 'user2', password: 'password2' },
];

// Middleware for basic authentication
const authenticate = (req, res, next) => {
  const authHeader = req.headers.authorization;

  if (!authHeader) {
    return res.status(401).json({ message: 'Unauthorized' });
  }

  const [authType, authCredentials] = authHeader.split(' ');

  if (authType !== 'Basic') {
    return res.status(401).json({ message: 'Unauthorized' });
  }

  const credentials = Buffer.from(authCredentials, 'base64').toString('utf-8');
  const [username, password] = credentials.split(':');

  const user = users.find((u) => u.username === username && u.password === password);

  if (!user) {
    return res.status(401).json({ message: 'Unauthorized' });
  }

  req.user = user; // Attach the user object to the request for later use
  next();
};

// Apply the authentication middleware globally for all routes
app.use(authenticate);

// Protected route
app.get('/protected', (req, res) => {
  res.json({ message: 'You have access to the protected resource!', user: req.user });
});

app.listen(port, () => {
  console.log(`Server is running on port ${port}`);
});

In this updated example:

  • We use app.use(authenticate) to apply the authenticate middleware globally for all routes. This means that every route defined after app.use(authenticate) will require authentication.

  • The "/protected" route does not have the authenticate middleware applied directly to it. Instead, it inherits the authentication requirement from the global middleware.

Now, all routes are protected by the authentication middleware, and you don't need to manually apply the middleware to each individual route.

Route-level middleware

Route-level middleware in Express.js allows you to apply middleware functions to specific routes rather than applying them globally to all routes. This gives you more fine-grained control over which routes have specific middleware functions applied to them. Here's an example of how to use route-level middleware:

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

// Sample user data (in-memory storage)
const users = [
  { id: 1, username: 'user1', password: 'password1' },
  { id: 2, username: 'user2', password: 'password2' },
];

// Middleware for basic authentication
const authenticate = (req, res, next) => {
  const authHeader = req.headers.authorization;

  if (!authHeader) {
    return res.status(401).json({ message: 'Unauthorized' });
  }

  const [authType, authCredentials] = authHeader.split(' ');

  if (authType !== 'Basic') {
    return res.status(401).json({ message: 'Unauthorized' });
  }

  const credentials = Buffer.from(authCredentials, 'base64').toString('utf-8');
  const [username, password] = credentials.split(':');

  const user = users.find((u) => u.username === username && u.password === password);

  if (!user) {
    return res.status(401).json({ message: 'Unauthorized' });
  }

  req.user = user; // Attach the user object to the request for later use
  next();
};

// Apply the authentication middleware to a specific route
app.get('/protected', authenticate, (req, res) => {
  res.json({ message: 'You have access to the protected resource!', user: req.user });
});

// Another route without authentication middleware
app.get('/public', (req, res) => {
  res.json({ message: 'This is a public resource.' });
});

app.listen(port, () => {
  console.log(`Server is running on port ${port}`);
});

In this example:

  • We define the authenticate middleware as before.

  • We apply the authenticate middleware to the "/protected" route by including it as a second argument after the route path when defining the route.

  • The "/public" route does not have the authenticate middleware applied, so it does not require authentication.

With this setup, the "/protected" route requires authentication because the authenticate middleware is applied to it. The "/public" route, on the other hand, does not require authentication and is accessible without any additional middleware. Route-level middleware allows you to customize the middleware applied to each route in your Express.js application.

Built-in Middleware

Sure, let me explain these three commonly used built-in middleware functions in Express:

  1. express.static(root, [options]):

    • express.static is a middleware function that serves static files such as HTML, CSS, JavaScript, images, and more.
    • It is often used to serve client-side assets, making it easy to host static files like HTML, CSS, and JavaScript for your web application.
    • The root parameter specifies the root directory from which to serve static files.
    • The optional options object allows you to configure various settings, such as caching and file handling.

    Example:

    const express = require('express');
    const app = express();
    
    // Serve static files from the 'public' directory
    app.use(express.static('public'));
    

    In this example, if you have an "index.html" file in the "public" directory, you can access it in your browser by navigating to http://localhost:3000/index.html.

Static Hosting

Static hosting, also known as static web hosting or static file hosting, refers to the process of serving static files, such as HTML, CSS, JavaScript, images, and other assets, over the internet using a web server. Static files are files that do not change dynamically based on user interactions or database queries.

Static hosting is commonly used for websites, single-page applications (SPAs), documentation sites, and other web-based content that does not require server-side processing.

  1. express.json([options]):

    • express.json is a middleware function that parses incoming JSON requests and makes the parsed data available in the req.body property.
  2. express.urlencoded([options]):

    • express.urlencoded is a middleware function that parses incoming URL-encoded data from forms and makes it available in the req.body property.

These built-in middlewares in Express simplify common tasks like serving static files and parsing request data, making it easier to handle different types of requests in your web application or API.

Route Parameters and Query Parameters

In web development, route parameters and query parameters are mechanisms for passing data to a web server or an application, typically through a URL. They are commonly used to customize and control the behavior of a web application by providing information about the requested resource or specifying additional options.

Route Parameters:

Route parameters are part of the URL's path and are used to define variable parts of a route. They are typically denoted by placeholders in the route pattern, surrounded by colons (:). When a client makes a request with a URL that matches the route pattern, the values specified in the URL are extracted and made available to the server or application.

For example, in a RESTful API, you might have a route for retrieving a specific user's profile:

GET /users/:userId

In this URL, :userId is a route parameter. When a request is made to /users/123, the server can extract the value 123 from the URL and use it to retrieve the user with that ID.

Query Parameters:

Query parameters are part of the URL's query string and are used to provide additional information or data to the server. They are typically specified after the ? character in the URL and are in the form of key-value pairs.

For example, in a search feature, you might have a URL that includes query parameters to filter results:

GET /search?q=keyword&page=2&sort=desc

In this URL, q, page, and sort are query parameters. They allow the server to understand the search query, the desired page, and the sorting order.

In summary, route parameters and query parameters are essential for building dynamic web applications and APIs. They allow you to customize the behavior of your routes and pass data between clients and servers effectively. Route parameters are part of the URL's path and are extracted using placeholders in the route pattern, while query parameters are part of the URL's query string and are provided as key-value pairs after the ? character. Express.js simplifies the handling of both types of parameters in your server-side code.