Primate Controller
A Flexible and Dynamic CRUD Controller
The PrimateController class is a key component in the Primate framework, designed for handling generic CRUD (Create, Read, Update, Delete) operations in web applications built with Node.js and Express.js. This controller is highly flexible, dynamic, and reusable, enabling rapid API prototyping with minimal code duplication across different models.
Imports
createError: Used for creating HTTP errors.
PrimateService: A generic service to handle CRUD operations if no custom service is defined.
fs: Used for reading files, checking if a service file exists.
chalk: Provides colored output for logging warnings and info.
primate: Some application-wide object that might contain hooks or configurations.
pluralize: Converts singular words to their plural form.
Example Flow
If you instantiate the controller like this:
const userController = new PrimateController('User');Model Name Transformation:
modelName:"User"serviceFileName:"user"this.singular:"user"this.plural:"users"
If the file ./entities/users/user.service.js exists, it will be dynamically imported as the service for the User model.
If no service is found or passed, the controller logs a warning and will default to using the generic PrimateService for CRUD operations.
Options
In the PrimateController class, the options parameter plays a crucial role in allowing for flexibility and customization in how CRUD operations are performed. These options are passed during the instantiation of the controller and are used in various parts of the class to adjust the behavior of the methods according to specific needs.
Let's break down where and how the options are used throughout the PrimateController class:
1. During Controller Initialization (Constructor)
In the constructor, options are assigned to the controller instance as this.options:
this.options = options;These options can contain custom configurations such as:
Custom services (e.g.,
options.service)Authenticated user information (e.g.,
options.user)Additional settings or hooks to modify the behavior of CRUD operations (e.g., logging, additional query constraints)
2. Inside the all() Method
all() MethodIn the all() method, the options object is used to:
Pass user information: If the request contains a user (
req.user), this information is added tooptions. For example, theuser.idis attached to the options:if (req.user) this.options.user = req.user.payload;Hooks and global modifications: Before fetching data, the method checks if there are any global hooks defined in
primate.hooks.all. If so, it calls this hook with theoptionsobject:if (primate.hooks?.all) { primate.hooks.all(req, res, next, this.options); }This allows for global behaviors, like logging or modifying the request before processing, to be added via the
options.Service usage: The
optionsare also passed when calling the service (either custom or genericPrimateService):const { count, data } = await this.service.all(req.query, this.options);
3. Inside the create() Method
create() MethodIn the create() method, the options are used for:
Attaching the current user: Just like in the
all()method, if the request contains a user, their information is added to the options:if (req.user) options.idUser = req.user.payload.id;Service usage: When creating a new record, the
optionsare passed to the custom service or the genericPrimateService:const record = await service.create(req.body, this.entity, options);
This allows for custom logic during the creation process, such as associating the new record with a specific user or applying additional constraints.
4. Inside the get() Method
get() MethodThe options are passed when fetching a single record in the get() method:
Passing options to the service: Similar to other methods, the
optionsobject is passed along with the query to the service orPrimateService:const record = await service.get(req.params.id, this.entity, req.query, this.options);
This can help in scenarios where additional filters, restrictions, or logging need to be applied to single record retrieval.
5. Inside the update() Method
update() MethodIn the update() method, the options are used in the following ways:
Adding user information: If the user is present, their ID is added to the options to ensure updates are linked to the appropriate user:
if (user) { options.idUser = user.payload.id; }Passing options to the service: The options are passed both when fetching the old record and when updating the record:
oldRecord = await PrimateService.get(id, entity, req.query, options); updatedRecord = await PrimateService.update(id, data, entity, options);
This is useful if certain update conditions depend on the user, such as only allowing the creator of a record to update it.
6. Inside the delete() Method
delete() MethodIn the delete() method:
Passing options to the service: The options are passed when deleting a record:
record = await this.service.delete(id, this.options);
Again, this allows for custom behaviors during the deletion process, such as checking if the user has permission to delete the record.
7. Inside the serviceCall() Method
serviceCall() MethodIn this method, which dynamically calls a service function:
Options can be part of the request: Though the
optionsare not explicitly used here, they could be passed as part of the service function being called, depending on how that service is designed to work.
8. Inside the updateMetas() Method
updateMetas() MethodIn the updateMetas() method, the options object is not directly passed to PrimateService, but it could easily be modified to include it if needed for custom metadata handling.
Last updated