79-Nodejs Course 2023: Resources: Recursive Resources
Hasan Zohdy
Posted on December 3, 2022
In this article, we'll see how to use resource as an output type and how to implement it.
Purpose
Why would we need to assign to an output key a resource
type? well, because we're using MongoDB, which means a document(s) can be stored in a single key in another document, for example we can store createdBy
object in literally any document (which we'll do later), and so on.
So we need to map that createdBy
object to use its proper resource to make a proper response output.
We are going to learn how to use two things, checking wether if a variable is a typeof class, and also a partial recursion, which means a class/function that can call itself (in somehow).
Before implementing
We're going to add a new feature in our Resource
class, which is allowing outputs to be resources, this requires us to add a check for the valueType
to check if it is a Resource
type, so i want to show you how to check if a variable is a type of class, (The class itself not the object is instance of it).
// some-file.ts
class MyClass {
}
const A = MyClass;
// check if A is a `MyClass` type
if (A.prototype instanceof MyClass) {
// A is a MyClass type
}
We used here prototype
to check if the class is an instance of another class, so we can use this to check if the valueType
is a Resource
type.
So we can say if we want to check if variable is a class
type, we use prototype
, if we want to check if variable is an object/instance of class we use instanceof
directly.
Implementation
Now let's see how we can do this.
It's very simple, we just assign to the output key, let's say createdBy
the value of UserResource
type and in our toJSON
we'll add another check if the given value type is typeof a Resource
, then we'll pass the data to it, otherwise we'll skip the key entirely.
// 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);
} else if (valueType.prototype instanceOf Resource) {
// if the value type is a resource, then pass the value to it
value = new valueType(value).toJSON();
} else if (typeof valueType === "function") {
// call the custom output handler
value = valueType(value);
}
// 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;
}
}
}
Here we just added a little if statement, which is checking if the value type is a Resource
type, if so, then we'll pass the value to it, otherwise we'll skip the key entirely.
Now we can update our UserResource
class
// src/app/users/resources/user-resource.ts
import Resource from 'core/resources/resource';
import { uploadsUrl } from 'core/utils/urls';
export default class UserResource extends Resource {
/**
* Output shape
*/
protected output: any = {
id: 'number',
name: 'string',
email: 'string',
age: 'number',
avatar: uploadsUrl,
createdBy: UserResource,
};
}
This should be working just fine with you, pass to the data a key named createdBy
and pass to it the object.
The point here is to map that data into a proper output using the UserResource
class.
This is also called Recursive Resource
because we're using the same resource to map the data to the output.
🎨 Conclusion
We just learnt how to implement a recursive call for resources inside each other, also how to check if a variable is a type of class.
☕♨️ 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)
Posted on December 3, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.