77-Nodejs Course 2023: Resources Output
Tags: node, typescript, mongodb, fastify
Published: 2022-12-01 08:14:25

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

So we saw in our previous chapter the concept of resources, we stopped at defining the output property, now let's start defining it.

## Output property

This property `output` will be an object, that object's key will be both the key that will be returned and also will be used to get the value from the resource as well.

On the other hand, the value of the key will define how the key will be returned in the response.

Let's take an example to make everything clear, let's say we have a `user` model that has the following data:

```ts
export default class Resource {
  /**
   * Constructor
   */
  public constructor(protected resource: any = {}) {
    //
  }

  /**
   * Output shape
   */
  protected output: any = {};
}
```

Now let's create our first resource, `user-resource class`

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


export default class UserResource extends Resource {
  /**
   * Output shape
   */
  protected output: any = {
    id: 'number',
    name: 'string',
    email: 'string',
    age: 'number',
  };
}
```

So we just defined here our output property, we defined the key `id` and its type is `number`, the key `name` and its type is `string`, the key `email` and its type is `string`, and the key `age` and its type is `number`.

Now let's use this resource in our `get-user.ts`

```ts
// src/app/users/controllers/get-user.ts
import UserResource from "../resources/user-resource";

export default function getUser() {
  return {
    user: new UserResource({
      id: 12,
      name: "Hasan",
      email: "hassanzohdy@gmail.com",
      age: 33,
    }),
  };
}
```

Now let's open our request in the browser or postman, you will see the same exact data as we passed in the controller, why? because we didn't do anything yet 😂

Now let's start updating our `toJSON` method in our `resource` class

```ts
// src/core/resources/resource.ts
import { get } from "@mongez/reinforcements";

// this will be used to skip the output property if it is missing from the given resource
const missingKey = Symbol("missing");

export default class Resource {
  /**
   * Constructor
   */
  public constructor(protected resource: any = {}) {
    //
  }

  /**
   * Output shape
   */
  protected output: any = {};

  /**
   * {@inheritDoc}
   */
  public toJSON() {
    // final output
    const data: Record<string, any> = {};

    // loop through the output property
    for (const key in this.output) {
      // get the value type
      const valueType = this.output[key];
      // get the value, and also make sure to skip the output property if it is missing from the given resource
      let value = get(this.resource, key, missingKey);

      // skip the output property if it is missing from the given resource
      if (value === missingKey) {
        continue;
      }

      // just for now sett the output value to the data
      data[key] = value;
    }

    return data;
  }
}
```

We updated our `toJSON` method as we defined the final data that will be used to return the response, then we looped over the output object.

First thing to do is to get the value type, then we got the value itself from our passed resource.

> Note that i defined a missingKey symbol, this is used to make sure that if the key is missing from the resource, we will skip it.

Later i just added it to our defined data object and returned it.

For now you you will not see anything new as i just returned what is exactly sent, now let's add some logic to our code.

```ts
// src/core/resources/resource.ts
// ...
  /**
   * Builtin casts
   */
  protected cast(value: any, type: string) {
    switch (type) {
      case "number":
        return Number(value);
      case "float":
      case "double":
        return parseFloat(value);
      case "int":
      case "integer":
        return parseInt(value);
      case "string":
        return String(value);
      case "boolean":
        return Boolean(value);
      default:
        return value;
    }
  }
```

We added here our cast method that receives the value that we want to cast and its type that will be casted into, now let's use it in our code.

But before that, i assume that you're already familiar with that switch statement, i made just a few checks over the type and returned the value after casting it.

```ts
// src/core/resources/resource.ts
import { get } from "@mongez/reinforcements";

// this will be used to skip the output property if it is missing from the given resource
const missingKey = Symbol("missing");

export default class Resource {
  /**
   * Constructor
   */
  public constructor(protected resource: any = {}) {
    //
  }

  /**
   * Output shape
   */
  protected output: any = {};

  /**
   * {@inheritDoc}
   */
  public toJSON() {
    // final output
    const data: Record<string, any> = {};

    // loop through the output property
    for (const key in this.output) {
      // get the value type
      const valueType = this.output[key];
      // get the value, and also make sure to skip the output property if it is missing from the given resource
      let value = get(this.resource, key, missingKey);

      // skip the output property if it is missing from the given resource
      if (value === missingKey) {
        continue;
      }

      if (typeof valueType === "string") {
        // cast the value
        value = this.cast(value, valueType);
      }

      // just for now sett the output value to the data
      data[key] = value;
    }

    return data;
  }
  
  /**
   * Builtin casts
   */
  protected cast(value: any, type: string) {
    switch (type) {
      case "number":
        return Number(value);
      case "float":
      case "double":
        return parseFloat(value);
      case "int":
      case "integer":
        return parseInt(value);
      case "string":
        return String(value);
      case "boolean":
        return Boolean(value);
      default:
        return value;
    }
  }
}
```

Now to test our code, just update the `get-user.ts` controller

```ts
// src/app/users/controllers/get-user.ts
import UserResource from "../resources/user-resource";

export default function getUser() {
  return {
    user: new UserResource({
      id: "12",
      name: "Hasan",
      email: "hassanzohdy@gmail.com",
      age: "33",
    }),
  };
}
```

Now if you open your request in the browser or postman, you will see that the `id` and `age` are now numbers, and the `name` and `email` are strings.

## 🎨 Conclusion

We got introduced to our new property `output`, this was just the beginning, in our next article we'll see how to use custom functions for custom casting.

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