84-Nodejs Course 2023: Resources: Boot And Extend
Tags: node, typescript, mongodb, fastify
Published: 2022-12-04 05:17:00

---
title: 84-Nodejs Course 2023: Resources: Boot And Extend
published: true
description: 
series: Nodejs Course 2023
tags: nodejs, typescript, mongodb, fastify
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x8a37ba6p4i1fvtho9g6.png
# Use a ratio of 100:42 for best results.
---

We have done a good job in our resources so far, let's go one more step, and define a way to customize our resource output using `boot` and `extend` methods.

## The need

Why would we need these methods? Well, sometimes we need to customize the output of our resource, for example, we may need to add a new key to the output, or we may need to remove a key from the output, or we may need to change the value of a key in the output.

These changes are mostly conditional, which means, for example, if current resource is `User Resource` and the current user is the same as the one in the resource, then we may need to add a new key to the output, for example, let's say we want to add a new key called `cart` to the output, but we only want to add this key if the current user is the same as the user in the resource.

Another use case, if you're working with courses api, and you're returning list of courses, or just a single course, we want to add a new key to the final output called `isSubscribed` which is a boolean value, and we want to set this value to `true` if the current user is subscribed to the course, otherwise the key will be set to `false` or may be not returned at all, so that's a `must` feature to be implemented in our resources.

## Boot And Extend

Basically, these are two methods, the first one `boot` will be called before transforming our `output` property to the final output, and the second one `extend` will be called after transforming our `output` property to the final output.

So you can use `boot` to add new keys to the output, replace or remove any keys from it and you can use `extend` to add new keys to the output, replace or remove any keys from it but after the output has been transformed.

## Implementation

Implementation is so simple, these are two methods (`non-static of course`), the first one will be called before transforming the output, and the second one will be called after transforming the output.

> Please note that we'll make these two methods async methods so you can perform any async operation inside them like fetching data from the database.

```ts
// core/resources/resource.ts

// ...

export default class Resource {

  /**
   * Boot method
   * Called before transforming the resource
   */
  public async boot() {
    //
  }

  /**
   * Extend the resource output
   * Called after transforming the resource
   */
  public async extend() {
    //
  }
  // ...
}
```

Simple two async empty methods, now we are going to update our `toJSON` function to be `async` method and call these two methods before and after transforming the output.

```ts
// core/resources/resource.ts

// ...

export default class Resource {

  // ...

  /**
   * Transform resource to object, that's going to be used as the final output
   */
  public async toJSON() {
    await this.boot();

    await this.transformOutput();

    await this.extend();

    return this.data;
  }
  // ...
}
```

You might be wondered, where is that giant code block that was in our `toJSON` method, well i just moved it to `transformOutput` method, that's much cleaner now.

We can also make any callable function that will be used in the transforming process to be `async` as well.

## Async transformation

Now let's update our `transformOutput` method to be `async` method as well.

Inside it, there will be a call to `transformValue` this method must be `awaited` so we can make it `async` as well.

```ts
// core/resources/resource.ts

// ...

export default class Resource {

  // ...

  /**
   * Transform final output
   */
  protected async transformOutput() {
    for (const key in this.output) {
      // first check if key is disabled
      if (this.isDisabledKey(key)) continue;

      if (!this.isAllowedKey(key)) continue;

      // get value type
      const valueType = this.output[key];

      // now get the value from the given resource data
      const value = get(
        this.resource,
        key,
        get(this.defaults, key, missingKey),
      );

      if (value === missingKey) {
        continue;
      }

      if (Is.empty(value)) continue;

      if (Array.isArray(value)) {
        this.data[key] = value.map(
          async item => await this.transformValue(item, valueType),
        );
      } else {
        this.data[key] = await this.transformValue(value, valueType);
      }
    }
  }

  // ...
}
```

I added `async` keyword to the `transformOutput` method, and i added `await` keyword to the `transformValue` method call.

> Note that we have to make the callback in the map function to be `async` as well, so we can `await` the `transformValue` method call.

Now let's update our `transformValue` method to be `async` method as well.

```ts
// core/resources/resource.ts

// ...


export default class Resource {

  // ...

  /**
   * Transform final output
   */
  protected async transformOutput() {
    for (const key in this.output) {
      // first check if key is disabled
      if (this.isDisabledKey(key)) continue;

      if (!this.isAllowedKey(key)) continue;

      // get value type
      const valueType = this.output[key];

      // now get the value from the given resource data
      const value = get(
        this.resource,
        key,
        get(this.defaults, key, missingKey),
      );

      if (value === missingKey) {
        continue;
      }

      if (Is.empty(value)) continue;

      if (Array.isArray(value)) {
        this.data[key] = value.map(
          async item => await this.transformValue(item, valueType),
        );
      } else {
        this.data[key] = await this.transformValue(value, valueType);
      }
    }
  }

  /**
   * Transform value
   */
  protected async transformValue(value: any, valueType: any) {
    if (typeof valueType === "string") {
      value = this.cast(value, valueType);
    } else if (valueType.prototype instanceof Resource) {
      if (!this.isValidResourceValue(value)) return null;
      value = new valueType(value);
    } else if (typeof valueType === "function") {
      value = await valueType.call(this, value);
    }

    return value;
  }
  // ...
}
```

I added `async` keyword to the `transformValue` method, and i added `await` keyword to the `valueType` function call.

Now let's give it a try in our `UserResource` class.

```ts
// src/app/users/resources/user-resource.ts
import Resource from "core/resources/resource";

export default class UserResource extends Resource {
  /**
   * {@inheritDoc}
   */
  public async boot(): Promise<void> {
    this.data.welcome = true;
  }

  /**
   * {@inheritDoc}
   */
  public async extend(): Promise<void> {
    this.data.goodbye = true;
  }
}
```

Now you should see something like this:

```json
  {
    "welcome": true,
    "isActive": true,
    "isPhoneVerified": false,
    "name": "John Doe",
    "email": "hassanzohdy@gmail.com",
    "image": "http://127.0.0.1:3000/uploads/users/my-image.jpg",
    "goodbye": true
  },
```

But this really is not going to work, why? because our method `toJSON` is basically `sync` method and now we changed it to be `async` method, thus this won't work anymore.

And that's going to be our next topic.

## 🎨 Conclusion

In this chapter, we talked about the importance of `booting` and `extending` resources and why it should be in our codebase.

In our next chapter, we're going to see how to make our `toJSON` method to be `async` method.

> That's where our powered Response class comes in.

## ☕♨️ 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)