DEV Community

Cover image for 87-Nodejs Course 2023: Resource: Resources In Models
Hasan Zohdy
Hasan Zohdy

Posted on

87-Nodejs Course 2023: Resource: Resources In Models

Let's update our codebase and go to the next level, let's embed our resources in our models!

The idea

Let's take a look at the following code:

// src/users/controllers/users-list.ts

import User from "../models/user";

export default async function usersList() {
  const users = await User.list();

  return {
    users,
  };
}
Enter fullscreen mode Exit fullscreen mode

This will basically return an array of models and will return the entire properties listed in each model, that's ugly too way ugly, in that sense, we need to link our model with our resource, if the model does not have a resource, then we'll just return only the data not the entire model.

toJSON method

Let's implement the easy part first, let's create a toJSON method in our base-model file, remember it? that model that we buried in our models folder, let's add a new method to it:

// src/core/database/model/base-model.ts

// ...

export default abstract class BaseModel {

  /**
   * Prepare model for response
   */
  public async toJSON() {
    return (this as any).data;
  }
}
Enter fullscreen mode Exit fullscreen mode

Why we added any because CrudModel does not anything about it, we added that property in our Model class so we need to tell typescript that it's okay to use it.

And also we made it async because of the next step, adding the resource.

Adding Resource

The resource will be added as a static property, why? well because we may access the Models' resource without the need to create an instance of the model, for example:

const UserResource = User.resource;

return {
    user: new UserResource({
        id: 1,
        name: 'Hasan',
    })
};
Enter fullscreen mode Exit fullscreen mode

I know that's sounds weird, why not just call the UserResource class directly, well you're right, but let's stick with that idea, i like it more.

Returning back to our code, let's add the resource property to our BaseModel class:

// src/core/database/model/base-model.ts
import Resource from 'core/resources/resource';
// ...

export default abstract class BaseModel {

  /**
   * Resource class
   */
  public static resource: typeof Resource;

  // ...
}
Enter fullscreen mode Exit fullscreen mode

Now Let's update the toJSON method to use the resource:

// src/core/database/model/base-model.ts
import Resource from 'core/resources/resource';
// ...

export default abstract class BaseModel {

  /**
   * Resource class
   */
  public static resource: typeof Resource;

  /**
   * Prepare model for response
   */
  public async toJSON() {
    // get static resource class
    const resource = this.getStaticProperty("resource");

    // if the model has a resource class
    if (resource) {
      // then return the resource instance and call `toJSON` method
      return await new resource(this).toJSON();
    }

    // otherwise return the data object
    return (this as any).data;
  }
}
Enter fullscreen mode Exit fullscreen mode

And that's it! now we can try this with our User model

// src/app/users/models/user.ts

import Auth from "core/auth/models/auth";
import castPassword from "core/database/casts/cast-password";
import { Casts, Document } from "core/database/model/types";
import UserResource from "../resources/user-resource";

export default class User extends Auth {
  /**
   * Collection name
   */
  public static collectionName = "users";

  /**
   * Resource
   */
  public static resource = UserResource;

  /**
   * Get user type
   */
  public get userType(): string {
    return "user";
  }

  /**
   * {@inheritDoc}
   */
  public defaultValue: Document = {
    isActive: true,
    isEmailVerified: false,
    isPhoneVerified: false,
  };

  protected casts: Casts = {
    isActive: "boolean",
    isPhoneVerified: "boolean",
    joinDate: "date",
    password: castPassword,
  };
}
Enter fullscreen mode Exit fullscreen mode

Now our user model is linked automatically with the UserResource class.

Usage

Now let's see the magic happens, let's update our users-list controller:

// src/users/controllers/users-list.ts

import User from "../models/user";

export default async function usersList() {
  const users = await User.list();

  return {
    users,
  };
}
Enter fullscreen mode Exit fullscreen mode

Same exact code, but this time you'll see in your response that it is returned from the resource not the model directly, and to make sure it works fine, let's add boot method in our user resource and add custom property in the final output.

// src/app/users/resources/user-resource.ts

import Resource from "core/resources/resource";

export default class UserResource extends Resource {
  /**
   * {@inheritDoc}
   */
  protected async boot() {
    this.set('outputFromUserResource', true);s
  }
}
Enter fullscreen mode Exit fullscreen mode

Now you should see something like this:


{
  "users": [
    {
      "outputFromUserResource": true,
      "isActive": true,
      "isPhoneVerified": false,
      "name": "John Doe",
      "email": "hassanzohdy@gmail.com",
      "image": "http://127.0.0.1:3000/uploads/users/my-image.jpg"
    },
    {
      "outputFromUserResource": true,
      "isActive": true,
      "isPhoneVerified": false,
      "name": "John Doe",
      "email": "hassanzohdy@gmail.com",
      "image": "http://127.0.0.1:3000/uploads/users/my-image.jpg"
    },
    {
      "outputFromUserResource": true,
      "isActive": true,
      "isPhoneVerified": false,
      "name": "John Doe",
      "email": "hassanzohdy@gmail.com",
      "image": "http://127.0.0.1:3000/uploads/users/my-image.jpg"
    },
    {
      "outputFromUserResource": true,
      "isActive": true,
      "isPhoneVerified": false,
      "name": "John Doe",
      "email": "hassanzohdy@gmail.com",
      "image": "http://127.0.0.1:3000/uploads/users/my-image.jpg"
    },
    {
      "outputFromUserResource": true,
      "isActive": true,
      "isPhoneVerified": false,
      "name": "John Doe",
      "email": "hassanzohdy@gmail.com",
      "image": "http://127.0.0.1:3000/uploads/users/my-image.jpg"
    },
    {
      "outputFromUserResource": true,
      "isActive": true,
      "isPhoneVerified": false,
      "name": "John Doe",
      "email": "hassanzohdy@gmail.com",
      "image": "http://127.0.0.1:3000/uploads/users/my-image.jpg"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

And that's it!

🎨 Conclusion

We saw in this tutorial how to manipulate the model when is being returned in response and also how to link a resource to model.

☕♨️ Buy me a Coffee ♨️☕

If you enjoy my articles and see it useful to you, you may buy me a coffee, 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

😍 Join our community

Join our community on Discord 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, 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

Packages & Libraries

React Js Packages

Courses (Articles)

Top comments (0)