Sometimes, we can use the resource in multiple apis, for example we may use the resource for admin api and for the front office (Website or mobile app) api, therefore some keys can be displayed in the admin but not in the front office.
For example, the published
key probably won't be needed in the front office, but it will be needed in the admin thus we need to disable it if it was sent to the resource so it doesn't get displayed in the front office.
Allowed And Disabled Outputs
To achieve this, we need to define two static properties, disabledKeys
and allowedKeys
, these properties will be used to filter the data before sending it to the response.
Defining Disabled Keys
Now let's update our base resource class and add a new static method disable
this method receives multiple keys that are going to be used to filter the data before sending it to the response.
// src/core/resources/resource.ts
// ...
export default class Resource {
// ...
/**
* Disabled keys from being returned in the final output
*/
protected static disabledKeys: string[] = [];
/**
* Disable the given keys
*/
public static disable(...keys: string[]) {
this.disabledKeys.push(...keys);
return this;
}
// ...
}
We defined here out disable
method that allows us to add multiple keys to the disabledKeys
array.
Defining Allowed Keys
Now let's do the same with the other case, we'll define a new static method allow
this method receives multiple keys that are going to be used to filter the data before sending it to the response.
// src/core/resources/resource.ts
// ...
export default class Resource {
// ...
/**
* Allowed keys to be returned in the final output
*/
protected static allowedKeys: string[] = [];
/**
* Allow the given keys
*/
public static allow(...keys: string[]) {
this.allowedKeys.push(...keys);
return this;
}
// ...
}
Of course as usual, these methods have no effect until we use them in our toJSON
method, so let's update it.
Updating toJSON
To do so, we need to define also two internal methods one to check for the given key that will be in the output object if it is disabled, if so then skip the transformation step and go to the next key.
And also we need to check if the given key is not in the allowed list
, if so then skip the transformation step and go to the next key.
But note that the allowed list must have at least one key, otherwise, it will be ignored and all keys will be allowed.
// src/core/resources/resource.ts
// ...
export default class Resource {
// ...
/**
* Check if the given key is disabled
*/
protected isDisabledKey(key: string) {
return (this.constructor as typeof Resource).disabledKeys.includes(key);
}
/**
* Check if the given key is allowed
*/
protected isAllowedKey(key: string) {
const allowedKeys = (this.constructor as typeof Resource).allowedKeys;
return allowedKeys.length === 0 || allowedKeys.includes(key);
}
// ...
}
You may get confused with the syntax this.constructor
, why? because we are trying to access a static member from non static member, and also we need to access it from the child resource class not the base class, otherwise we could just used Resource.disabledKeys
and Resource.allowedKeys
.
In the first method isDisabledKey
we check if the given key is in the disabledKeys
array, if so then we return true
otherwise false
.
In the second method isAllowedKey
we check if the allowedKeys
array is empty, if so then we return true
otherwise we check if the given key is in the allowedKeys
array, if so then we return true
otherwise false
.
Now let's use these methods in our toJSON
method.
// src/core/resources/resource.ts
// ...
export default class Resource {
// ...
/**
* Final data output
*/
protected data: Record<string, any> = {};
/**
* Transform resource to object, that's going to be used as the final output
*/
public toJSON() {
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(item =>
this.transformValue(item, valueType),
);
} else {
this.data[key] = this.transformValue(value, valueType);
}
}
return this.data;
}
// ...
}
We made two checks here before even getting the value type, the first one is to check if the given key is disabled, if so then we skip the transformation step and go to the next key.
The second one is to check if the given key is allowed, if so then we skip the transformation step and go to the next key.
Please note that i added a new property called
data
, this is going to be used later to add more features like customizing the final output and adding more complex outputs.
Using Disabled And Allowed Keys
Now let's open our users-list.ts
file and we can add the following code
// src/app/users/controllers/users-list.ts
import User from "../models/user";
import UserResource from "../resources/user-resource";
export default async function usersList() {
const users = await User.list();
UserResource.disable("isPhoneVerified", "isActive");
return {
users: UserResource.collect(users),
};
}
This will disable the isPhoneVerified
and isActive
keys from being returned in the final output.
🎨 Conclusion
In this article, we learned how to disable and allow keys from being returned in the final output.
☕♨️ 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)