55-Nodejs Course 2023: Request Events
Tags: node, typescript, mongodb, fastify
Published: 2022-11-10 09:38:32

---
title: 55-Nodejs Course 2023: Request Events
published: true
description: 
series: Nodejs Course 2023
tags: nodejs, typescript, mongodb, fastify
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/scv4losedwy0e8uez8uv.png
# Use a ratio of 100:42 for best results.
---

We've implemented the middleware concept in our previous article, now le'ts go back to our main topic, the events.

In this article, we'll implement the request events, but let's first review the http events.

## Http Events

As i mentioned earlier, there are multiple events that can be fired during the http request, let's review them:

* `request.executingMiddleware`: This event is fired before calling all the middlewares.
* `request.executedMiddleware`: This event is fired after calling all the middlewares.
* `request.executingAction`: This event is fired before the action is called.
* `request.executedAction`: This event is fired after the action is called and before.

Let's implement them now in our `Request` class.

> If you've a good observation, you'll notice that i renamed the events.

## Trigger And On Methods

As usual, we'll encapsulate the request events to be called in the `Request` class, so we'll add two methods to the `Request` class:

* `trigger`: This method will be used to trigger the event.
* `on`: This method will be used to listen to the event.

But let's first define the `RequestEvent` type that will be used to define the events.

```ts
// src/core/http/types.ts

export type RequestEvent = 'executingMiddleware' | 'executedMiddleware' | 'executingAction' | 'executedAction';
```

Now let's implement the `trigger` and `on` methods.

```ts
// src/core/http/request.ts
import { RequestEvent } from "./types";

export class Request {
//...

  /**
   * Trigger an http event
   */
  protected trigger(eventName: RequestEvent, ...args: any[]) {
    return events.trigger(`request.${eventName}`, ...args, this);
  }

  /**
   * Listen to the given event
   */
  public on(eventName: RequestEvent, callback: any) {
    return this.trigger(eventName, callback);
  }
}
```

Here we defined two methods, the `trigger` method will be used to trigger the event this method receives the event name and the arguments that will be passed to the event listeners, and the `on` method will be used to listen to the event, this method receives the event name and the callback that will be called when the event is fired.

## Middleware events

We'll start with the middleware events, so we'll implement the `callingMiddleware` and `calledMiddleware` events.

We already added our middleware in the `execute` method, but now we're going to increase our code to handle the events.

So let's move the middleware code to a new method called `callMiddleware` and call it from the `execute` method.

```ts
// src/core/http/request.ts

  /**
   * Execute the request
   */
  public async execute() {
    // check for middleware first
    const middlewareOutput = await this.executeMiddleware();

    if (middlewareOutput !== undefined) {
      return middlewareOutput;
    }

    const handler = this.route.handler;

    // check for validation
    if (handler.validation) {
      if (handler.validation.rules) {
        const validator = new Validator(this, handler.validation.rules);

        try {
          await validator.scan(); // start scanning the rules
        } catch (error) {
          console.log(error);
        }

        if (validator.fails()) {
          const responseErrorsKey = config.get(
            "validation.keys.response",
            "errors",
          );
          const responseStatus = config.get("validation.responseStatus", 400);

          return this.response.status(responseStatus).send({
            [responseErrorsKey]: validator.errors(),
          });
        }
      }

      if (handler.validation.validate) {
        const result = await handler.validation.validate(this, this.response);

        if (result) {
          return result;
        }
      }
    }

    return await handler(this, this.response);
  }

  /**
   * Execute middleware list of current route
   */
  protected async executeMiddleware() {
    if (!this.route.middleware) return;

    for (const middleware of this.route.middleware) {
      const output = await middleware(this, this.response);

      if (output !== undefined) {
        return output;
      }
    }
  }
```

We moved the middleware code to a new method called `executeMiddleware` then we called it from the `execute` method.

If it returns a value, then we'll return it from the `execute` method.

Now let's fire up some events.

```ts
// src/core/http/request.ts

  /**
   * Execute middleware list of current route
   */
  protected async executeMiddleware() {
    if (!this.route.middleware || this.route.middleware.length === 0) return;

    // trigger the executingMiddleware event
    this.trigger('executingMiddleware', this.route.middleware, this.route);

    for (const middleware of this.route.middleware) {
      const output = await middleware(this, this.response);

      if (output !== undefined) {
        this.trigger('executedMiddleware');
        return output;
      }
    }

    // trigger the executedMiddleware event
    this.trigger('executedMiddleware', this.route.middleware, this.route);
  }
```

Here we fired up the `executingMiddleware` event before calling the middleware, and the `executedMiddleware` event after calling the middleware.

Any of both events will send the middleware list that will be executed and the route object as well.

Also notice that if a middleware is returning a value, we need to fire up the `executedMiddleware` event, so we'll fire it up before returning the value.

Now let's implement the `executingAction` and `executedAction` events.

They will be called just before and after calling the action.

```ts
// src/core/http/request.ts
  /**
   * Execute the request
   */
  public async execute() {
    // check for middleware first
    const middlewareOutput = await this.executeMiddleware();

    if (middlewareOutput !== undefined) {
      return middlewareOutput;
    }

    const handler = this.route.handler;

    // check for validation
    if (handler.validation) {
      if (handler.validation.rules) {
        const validator = new Validator(this, handler.validation.rules);

        try {
          await validator.scan(); // start scanning the rules
        } catch (error) {
          console.log(error);
        }

        if (validator.fails()) {
          const responseErrorsKey = config.get(
            "validation.keys.response",
            "errors",
          );
          const responseStatus = config.get("validation.responseStatus", 400);

          return this.response.status(responseStatus).send({
            [responseErrorsKey]: validator.errors(),
          });
        }
      }

      if (handler.validation.validate) {
        const result = await handler.validation.validate(this, this.response);

        if (result) {
          return result;
        }
      }
    }

    // call executingAction event
    this.trigger("executingAction", this.route);
    const output = await handler(this, this.response);

    // call executedAction event
    this.trigger("executedAction", this.route);

    return output;
  }
```

We replaced the last returned line by calling the `executingAction` event before calling the action, and the `executedAction` event after calling the action then we returned the output.

You know what, the validation step is too long to be in the same method, so let's move it to a new method called `validate` and call it from the `execute` method.

```ts
// src/core/http/request.ts

  /**
   * Execute the request
   */
  public async execute() {
    // check for middleware first
    const middlewareOutput = await this.executeMiddleware();

    if (middlewareOutput !== undefined) {
      return middlewareOutput;
    }

    const handler = this.route.handler;

    // check for validation
    const validationOutput = await this.validate(handler.validation);

    if (validationOutput !== undefined) {
      return validationOutput;
    }

    // call executingAction event
    this.trigger("executingAction", this.route);
    const output = await handler(this, this.response);

    // call executedAction event
    this.trigger("executedAction", this.route);

    return output;
  }

  /**
   * Validate the request
   */
  protected async validate(validation: any) {
    if (!validation) return;

    if (validation.rules) {
      const validator = new Validator(this, validation.rules);

      try {
        await validator.scan(); // start scanning the rules
      } catch (error) {
        console.log(error);
      }

      if (validator.fails()) {
        const responseErrorsKey = config.get(
          "validation.keys.response",
          "errors",
        );
        const responseStatus = config.get("validation.responseStatus", 400);

        return this.response.status(responseStatus).send({
          [responseErrorsKey]: validator.errors(),
        });
      }
    }

    if (validation.validate) {
      const result = await validation.validate(this, this.response);

      if (result) {
        return result;
      }
    }
  }
```

Pretty much cleaner now.

Now as long as the middleware and validation does not return a value that is undefined, the handler itself will be called.

And that's it!, we're done with the request events.

## 🎨 Conclusion

In this article, we learned how to implement events in our request class and reorganized it to be much readable and maintainable.

In our next article we'll start handle the response class and make our own one, then we'll implement events in it.

## 🚀 Project Repository

You can find the latest updates of this project on [Github](https://github.com/hassanzohdy/nodejs-2023)

## 😍 Join our community

Join our community on [Discord](https://discord.gg/pb2vmdfhGf) to get help and support (Node Js 2023 Channel).

## 🎞️ Video Course (Arabic Voice)

If you want to learn this course in video format, you can find it on [Youtube](https://www.youtube.com/playlist?list=PLGO8ntvxgiZMJc7RN2lIq9WmMOlWZGzmz), the course is in Arabic language.

## 📚 Bonus Content 📚

You may have a look at these articles, it will definitely boost your knowledge and productivity.

General Topics

* [Event Driven Architecture: A Practical Guide in Javascript](https://dev.to/hassanzohdy/event-driven-architecture-the-best-paradigm-that-i-love-to-work-with-in-javascript-and-node-js-1gnk)
* [Best Practices For Case Styles: Camel, Pascal, Snake, and Kebab Case In Node And Javascript](https://dev.to/hassanzohdy/best-practices-for-case-styles-camel-pascal-snake-and-kebab-case-in-node-and-javascript-55oi)
* [After 6 years of practicing MongoDB, Here are my thoughts on MongoDB vs MySQL
](https://dev.to/hassanzohdy/after-6-years-of-practicing-mongodb-here-are-my-thoughts-on-mongodb-vs-mysql-574b)

Packages & Libraries

* [Collections: Your ultimate Javascript Arrays Manager](https://dev.to/hassanzohdy/collections-your-ultimate-javascript-array-handler-3o15)
* [Supportive Is: an elegant utility to check types of values in JavaScript](https://dev.to/hassanzohdy/supportive-is-an-elegant-utility-to-check-types-of-values-in-javascript-1b3e)
* [Localization: An agnostic i18n package to manage localization in your project](https://dev.to/hassanzohdy/mongez-localization-the-simplest-way-to-translate-your-website-regardless-your-favorite-framework-4gi3)

 React Js Packages

* [useFetcher: easiest way to fetch data in React Js](https://dev.to/hassanzohdy/usefetcher-the-easiest-way-to-fetch-data-in-react-45o9)

Courses (Articles)

* [React Js: Let"s Create File Manager With React Js and Node Js](https://dev.to/hassanzohdy/lets-create-a-file-manager-from-scratch-with-react-and-typescript-chapter-i-a-good-way-to-expand-your-experience-5g4k)