85-Nodejs Course 2023: Response: Async Body Parser
Tags: node, typescript, mongodb, fastify
Published: 2022-12-04 08:46:00

---
title: 85-Nodejs Course 2023: Response: Async Body Parser
published: true
description: 
series: Nodejs Course 2023
tags: nodejs, typescript, mongodb, fastify
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wpkzwdzkx16z8anpt6gg.png
# Use a ratio of 100:42 for best results.
---

In our previous chapter, we saw how to use our resource and added couple features `boot` and `extend` which led us to make our `toJSON` method to be `async`, that's where We got a serious issue, as `JSON.stringify` doesn't support `async` functions, so we need to find a way to make it work.

## Async Json Parser

Remember our `response` class? yes that one that we didn't use it yet, it's time to use it now, we'll use it to parse the data before sending it to the response, but before updating it, we need to update our `request` class to pass the output of the handler to the response class.

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

// ...

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

    if (middlewareOutput !== undefined) {
      // 👇🏻 send the response
      return this.response.send(middlewareOutput);
    }

    const handler = this.route.handler;

    // 👇🏻 check for validation using validateAll helper function
    const validationOutput = await validateAll(
      handler.validation,
      this,
      this.response,
    );

    if (validationOutput !== undefined) {
      // 👇🏻 send the response
      return this.response.send(validationOutput);
    }

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

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

    // 👇🏻 send the response
    await this.response.send(output);
  }
```

So instead of returned the output from middleware, validation or the handler, we are going to pass that output to the response's `send` method so we can handle that output freely.

Now let's update our `response` class to handle the output.

```ts
// src/core/http/response.ts
// ...

  /**
   * Send the response
   */
  public async send(data?: any, statusCode?: number) {
    if (data) {
      this.currentBody = data;
    }

    if (statusCode) {
      this.currentStatusCode = statusCode;
    }

    if (!this.currentStatusCode) {
      this.currentStatusCode = 200;
    }

    // trigger the sending event
    this.trigger("sending", this.currentStatusCode, data);

    this.baseResponse.status(this.currentStatusCode).send(data);

    // trigger the sent event
    this.trigger("sent", this.currentStatusCode, data);

    // trigger the success event if the status code is 2xx
    if (this.currentStatusCode >= 200 && this.currentStatusCode < 300) {
      this.trigger("success", data, this.currentStatusCode, this.route);
    }

    // trigger the successCreate event if the status code is 201
    if (this.currentStatusCode === 201) {
      this.trigger("successCreate", data, this.currentStatusCode, this.route);
    }

    // trigger the badRequest event if the status code is 400
    if (this.currentStatusCode === 400) {
      this.trigger("badRequest", data, this.currentStatusCode, this.route);
    }

    // trigger the unauthorized event if the status code is 401
    if (this.currentStatusCode === 401) {
      this.trigger("unauthorized", data, this.currentStatusCode, this.route);
    }

    // trigger the forbidden event if the status code is 403
    if (this.currentStatusCode === 403) {
      this.trigger("forbidden", data, this.currentStatusCode, this.route);
    }

    // trigger the notFound event if the status code is 404
    if (this.currentStatusCode === 404) {
      this.trigger("notFound", data, this.currentStatusCode, this.route);
    }

    // trigger the throttled event if the status code is 429
    if (this.currentStatusCode === 429) {
      this.trigger("throttled", data, this.currentStatusCode, this.route);
    }

    // trigger the serverError event if the status code is 500
    if (this.currentStatusCode === 500) {
      this.trigger("serverError", data, this.currentStatusCode, this.route);
    }

    // trigger the error event if the status code is 4xx or 5xx
    if (this.currentStatusCode >= 400) {
      this.trigger("error", data, this.currentStatusCode, this.route);
    }

    return this;
  }
```

This is our previous send method, we're going to add a new line after that first check.

```ts
// src/core/http/response.ts

// ...

  /**
   * Send the response
   */
  public async send(data?: any, statusCode?: number) {
    if (data) {
      this.currentBody = data;
    }

    // parse the body and make sure it is transformed to sync data instead of async data
    data = await this.parseBody();
    // ...
  }
```

We added a new line here, which is going to parse our `currentBody` data, and make sure it's transformed to sync data instead of async data, so we can send it to the response.

## Body Parser

Now let's create our `parseBody` method, but before we go to the implementation let me tell you the workflow of this method.

Basically, this method will call another method called `parse` and pass the `currentBody` to that method.

Why would we do this? because we're going in some recursion here, so the `parse` method will receive any kind of data and handle it accordingly regardless its the main body or a nested object.

Now returning to the parse method workflow

1. If the given value is a `falsy` value, we'll return it as it is.
2. Check if the passed data has `toJSON` method, if so then `await` it and return the result.
3. Check if the passed data is an `array`, if so then loop over it and call the `parse` method on each item and return the result.
4. If it is not plain object and not an array and does not have `toJSON` then we're going to just return it.
5. If it is a plain object then we're going to loop over its keys and call the `parse` method on each value and return the result.

Actually we're going to change the point of `array`, we're going to check if the value is `iterable` instead of checking if it is an array, because we want to support any kind of iterable data, not just arrays.

Now let's go to the implementation.

```ts
// src/core/http/response.ts
// we'll need it here to make couple checks later
import Is from "@mongez/supportive-is";

// ...
export class Response {
    // ...
    /**
     * Parse body
     */
    protected async parseBody() {
        return await this.parse(this.currentBody);
    }
    // ...
}
```

Now let's create the `parse` method.

```ts
// src/core/http/response.ts
// ...


export class Response {
    // ...
    
  /**
   * Parse the given value
   */
  protected async parse(value: any): Promise<any> {
    // if it is a falsy value, return it
    if (!value) return value;

    // if it has a `toJSON` method, call it and await the result then return it
    if (value.toJSON) {
      return await value.toJSON();
    }

    // if it is iterable, an array or array-like object then parse each item
    if (Is.iterable(value)) {
      return await Promise.all(
        Array.from(value).map((item: any) => {
          return this.parse(item);
        }),
      );
    }

    // if not plain object, then return it
    if (!Is.plainObject(value)) {
      return value;
    }

    // loop over the object and check if the value and call `parse` on it
    for (const key in value) {
      const subValue = value[key];

      value[key] = await this.parse(subValue);
    }

    return value;
  }
}
```

The code is pretty much as the flow steps i wrote above, we checked if its a `falsy` value then we returned it, if it has a `toJSON` method then await the call of it and return it.

If it is `iterable` (**That's why we imported Supportive Is method**) then we're going to loop over it and call the `parse` method on each item and return the result.

If it is not `plainObject` then we're going to return it.

If it is a `plainObject` then we're going to loop over its keys and call the `parse` method on each value and return the result.

Regarding the `iterable` data, you might see the code a little weird, we used `Promise.all` to await all promises to be resolved, that `Promise.all` method receives an array, that's why we used `Array.from` to convert the iterable data (**arrays are also considered iterable**) to an array, then map each value to be called with the `parse` method.

Now if we tried again returning the resource, it will work just fine.

## 🎨 Conclusion

In this section, we customized the final output of the response, we make our own data parser to make every `async` data to be sent as `sync` data.

## ☕♨️ Buy me a Coffee ♨️☕

If you enjoy my articles and see it useful to you, you may [buy me a coffee](https://www.paypal.com/paypalme/hassanzohdy), it will help me to keep going and keep creating more content.

## 🚀 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)

* [OOP In JS And TS From The Very Beginning](https://dev.to/hassanzohdy/jsts-oop-course-1-introduction-2ld)
* [ES6++: The Ultimate Guide to ES6 and Beyond](https://dev.to/hassanzohdy/es6-1-introduction-h3e)
* [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)