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,
};
}
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;
}
}
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',
})
};
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;
// ...
}
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;
}
}
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,
};
}
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,
};
}
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
}
}
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"
}
]
}
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
- Event Driven Architecture: A Practical Guide in Javascript
- Best Practices For Case Styles: Camel, Pascal, Snake, and Kebab Case In Node And Javascript
- After 6 years of practicing MongoDB, Here are my thoughts on MongoDB vs MySQL
Packages & Libraries
- Collections: Your ultimate Javascript Arrays Manager
- Supportive Is: an elegant utility to check types of values in JavaScript
- Localization: An agnostic i18n package to manage localization in your project
React Js Packages
Courses (Articles)
Top comments (0)