Hooks
Mercury.js: Defining Hook Execution for Models and Actions
Mercury.js provides an advanced mechanism for adding before and after hooks to actions on your application's models. Hooks allow you to execute custom logic before or after certain operations, giving you granular control over the behavior of your data models.
Hook Levels
Mercury.js hooks can be executed at different stages:
- Before Hooks: Run before an action occurs on a model.
- After Hooks: Run after an action occurs on a model.
Each model can have hooks attached to specific actions:
- Create: Executed before or after creating a new record.
- Update: Executed before or after modifying an existing record.
- Delete: Executed before or after removing an existing record.
- Get: Executed before or after retrieving a record.
- Paginate: Executed before or after paginating records.
Hook Syntax
To define hooks, use the mercury.hook.before
and mercury.hook.after
methods.
Rule Structure
// Define a before hook
mercury.hook.before(action: string, handler: Function);
// Define an after hook
mercury.hook.after(action: string, handler: Function);
mercury.hook.before
and mercury.hook.after
are methods which allow you to specify custom logic for actions on a model.
The action
parameter is a string that represents the specific operation on a model to which the hook is attached.
Hook Actions
Example
Let's assume USER
would be the model. Each hook can be tied to different actions such as "CREATE", "UPDATE", or "DELETE" depending on the custom logic to run.
1. Create Hook
In this example, we add custom logic before and after creating a new user in the User model.
Before Create Hook
import mercury from "@mercury-js/core";
// Define a before hook for the "create" action
mercury.hook.before("CREATE_USER_RECORD", (this: any) => {
console.log("Before Creating User: ", this);
});
The before
hook for the "CREATE_USER_RECORD" action using Mercury.js executes custom logic before the "user" record is created. It also prints the context (this
) available at the time of the hook's execution.
Explanation
-
import mercury from "@mercury-js/core"
The Mercury.js library provides a set of tools to manage actions and models in your application. By importing mercury, you gain access to the hook mechanism that lets you define custom logic for model actions like create.
-
mercury.hook.before("CREATE_USER_RECORD", function(this: any))
This line registers a before hook that listens for the CREATE_USER_RECORD event.
-
function(this: any)
The function is defined with
this
bound to the current context in which the action is executed. In TypeScript,this: any
indicates thatthis
can hold any type of value.
What this
Represents
The this
context in Mercury hooks usually provides detailed information about the current operation. Here's what you might see when printing this
in the console:
{
name: 'User',
data: { userName: "User" },
user: { id: '1', profile: 'ADMIN' },
options: {
root: undefined,
args: { input: [Object] },
ctx: {
user: [Object],
[Symbol(realm)]: [Object],
[Symbol(state)]: [Object],
[Symbol(signal)]: [AbortSignal],
[Symbol(abortController)]: [AbortController],
[Symbol(headers)]: [HeadersList],
[Symbol(internal request)]: [Object]
},
internal: false,
populate: [],
select: [ 'id' ]
}
}
name
: The name of the model being processed (e.g., User).data
: The data being sent for creating the User record.user
: Information about the user performing the action, including their id and profile (e.g., ADMIN).options
: Additional metadata and configuration options for the create action.root
: Root object, usually undefined for create operations.args
: Arguments passed to the action.ctx
: Context of the request, including user data, headers, and control symbols.user
: Represents additional details about the user making the request, typically used for authentication and authorization checks.
internal
: Boolean indicating whether this is an internal request.populate
: Specifies which related fields to populate.select
: Fields selected for the response (e.g., id).
Purpose of the Hook:
This before hook gives you an opportunity to execute custom logic before the model record is created. You can inspect the this
context to access important information, such as the data being used to create the record, the user performing the action, and the environment in which the action is happening.
After Create Hook
Rule Structure
import mercury from "@mercury-js/core";
// Define an after hook for the "create" action
mercury.hook.after("CREATE_USER_RECORD", (this: any) => {
console.log("After Creation Of User: ", this);
});
The after
hook for the "CREATE_USER_RECORD" action using Mercury.js runs a custom function after a user record is created.
Explanation
-
import mercury from "@mercury-js/core"
The Mercury.js library provides a set of tools to manage actions and models in your application. By importing mercury, you gain access to the hook mechanism that lets you define custom logic for model actions like create.
-
mercury.hook.after("CREATE_USER_RECORD", function(this: any))
This line registers an after hook that listens for the CREATE_USER_RECORD event.
-
CREATE_USER_RECORD
This string represents the specific action to which the hook is attached. In this case, it refers to the creation of a model record in a model, most likely a User model. The hook will be executed after a user record has been created successfully.
-
function (this: any)
The callback function is executed after the CREATE_USER_RECORD action is completed.
this
refers to the context or state in which the action was executed. This is important because it contains information about what happened during the user creation process.
What this
Represents
When printing this
in the console, the object will likely contain:
{
name: 'User',
record: {
_id: new ObjectId('123'),
UserName: 'User',
createdOn: 2024-09-22T14:50:46.367Z,
updatedOn: 2024-09-22T14:50:46.367Z,
id: '123'
},
user: { profile: 'ADMIN' },
options: {
root: { mercuryResolvers: [Object] },
args: { input: [Object] },
ctx: {
user: [Object],
[Symbol(kCapture)]: false,
[Symbol(kHeaders)]: [Object],
[Symbol(kHeadersCount)]: 32,
[Symbol(kTrailers)]: null,
[Symbol(kTrailersCount)]: 0
},
internal: false,
populate: [],
select: ['id', 'name']
}
}
name
: Specifies the name of the model being processed. In this case, it is set to 'User', indicating the operation involves a user-related entity in the system.record
: Stores all the details about the user. It represents the actual user being created or updated in the database.user
: Contains metadata about the user who is performing the operation, such as creating or updating a user.profile
: Represents the role or profile of the user making the request (e.g., 'ADMIN'). This field may be used to apply permissions or role-based access control during the operation.
options
: Contains additional metadata about the request, including resolver information, arguments, and selection options.root
: Connects to the database and handles how the model is processed based on the query or mutation.args
: Captures the input provided to the operation, such as form data or request parameters. In GraphQL operations, this includes the input fields for mutations or queries (like the userName).ctx
: Context of the request, including user data, headers, and control symbols.user
: Represents additional details about the user making the request, typically used for authentication and authorization checks.
internal
: Boolean indicating whether this is an internal request.populate
: Specifies which related fields to populate.select
: Fields selected for the response (e.g., id, name).
Purpose of the Hook: The after hook allows you to perform tasks or trigger logic after the user creation has been finalized. Some common use cases include:
- Logging information about the created user.
- Triggering additional workflows, such as sending notifications.
- Updating related data or records based on the newly created user.
- Auditing and tracking creation events for monitoring or debugging purposes.
2.Update Hook
This hook allows you to execute logic before and after updating a user record.
Before Update Hook
Rule Structure
import mercury from "@mercury-js/core";
// Define a before hook for the "update" action
mercury.hook.before("UPDATE_USER_RECORD", (this:any) => {
console.log("Before Updating User: ", this);
});
The before
hook for the "UPDATE_USER_RECORD" action using Mercury.js executes custom logic before the update action is performed on the user model.
Explanation
1. import mercury from "@mercury-js/core"
The Mercury.js library provides a set of tools to manage actions and models in your application. By importing mercury, you gain access to the hook mechanism that lets you define custom logic for model action like update.
2.mercury.hook.before("UPDATE_USER_RECORD", function(this: any))
This line registers a before hook that listens for the UPDATE_USER_RECORD event.
3.UPDATE_USER_RECORD
This string represents the specific action to which the hook is attached. In this case, it refers to the updation of a model record in a model, most likely a User model. The hook will be executed before a user record has been updating.
4.function (this: any)
The callback function is executed before the UPDATE_USER_RECORD action. this refers to the context or state in which the action is executing. This is important because it contains information about what happened during the user updation process. this is used to capture the context of the action being processed.
What this Represents
When printing this in the console, the object will likely contain:
{
name: 'User',
prevRecord: {
\_id: new ObjectId('123'),
userName: 'User',
createdOn: 2024-09-20T06:48:09.999Z,
updatedOn: 2024-09-20T07:07:12.799Z,
__v: 0,
id: '123'
},
record: {
_id: new ObjectId('123'),
name: 'User',
id: '123'
},
data: { id: '123', name: 'do' },
user: { profile: 'ADMIN' },
options: {
root: { mercuryResolvers: [Object] },
args: { input: [Object] },
ctx: {
_
user: [Object],
[Symbol(kCapture)]: false,
[Symbol(kHeaders)]: [Object],
[Symbol(kHeadersCount)]: 34,
[Symbol(kTrailers)]: null,
[Symbol(kTrailersCount)]: 0
},
internal: false,
populate: [],
select: [ 'id', 'userName']
}
}
-
name
: The name of the model being processed (e.g., User). -
prevRecord
: Holds details about the previous version of the user before the update (e.g., name,). -
record
: Represents the updated version of the user (e.g., the new name after the update). -
user
: Contains metadata about the user performing the operation (profile as 'ADMIN'). -
options
: Contains extra metadata about the request:-
root: Links to database query processing.
-
args: Captures input arguments for the update.
-
ctx: Provides the context of the request, including HTTP details and user info.
-
internal: Specifies whether the request is internal (set to false).
-
select: Defines which fields are selected for the response.
-
Purpose of the Hook:
-
The after hook allows performing tasks or triggering logic after the user update has been completed. Common use cases include:
-
Logging information about the updated user.
-
Triggering workflows, such as sending notifications related to the updated project.
-
Updating related data or records based on the newly updated user.
-
Auditing and tracking update events for monitoring or debugging purposes.
After Update Hook:
Rule Structure
import mercury from "@mercury-js/core";
// Define an after hook for the "update" action
mercury.hook.after("UPDATE_USER_RECORD", (this: any) => {
console.log("After User Updation: ", this);
});
The after
hook for the "UPDATE_USER_RECORD" action using Mercury.js runs a custom function after a user record is updated.
Explanation
1. import mercury from "@mercury-js/core"
Mercury.js provides the tools to handle actions and events in your application. By importing Mercury, you can define hooks for various model actions, such as after updating a user.
2. mercury.hook.after("UPDATE_USER_RECORD", function(this: any))
Registers an after hook that listens for the "UPDATE_USER_RECORD" event. The hook will trigger after a user record has been updated.
3. "UPDATE_USER_RECORD"
This string represents the specific action tied to the update of a user model record. The hook runs after the user update process is completed.
4. function (this: any)
The callback function is executed after the "UPDATE_USER_RECORD" action, providing access to the context and state at the time of the update.
What this Represents
After the user is updated, the this object will contain relevant information about the update process:
{
name: 'User',
prevRecord: {
_id: new ObjectId('123'),
userName: 'Old User Name',
createdOn: 2024-09-20T06:48:09.999Z,
updatedOn: 2024-09-22T07:07:12.799Z,
__v: 0,
id: '123'
},
record: {
_id: new ObjectId('123'),
userName: 'New User Name',
updatedOn: 2024-09-23T08:45:12.799Z,
id: '123'
},
data: { id: '123', userName: 'New User Name' },
user: { profile: 'ADMIN' },
options: {
root: { mercuryResolvers: [Object] },
args: { input: [Object] },
ctx: {
url: '/update',
method: 'POST',
body: [Object],
user: [Object],
res: [ServerResponse]
},
internal: false,
populate: [],
select: ['id', 'userName']
}
}
-
name
: Refers to the entity being updated (in this case, a "User"). -
prevRecord
: Contains the previous state of the user before the update. -
record
: Represents the new updated user data (e.g., updated name or timestamps). -
user
: Metadata about the user who performed the update (e.g., profile as 'ADMIN'). -
options
: Contains extra metadata about the request:-
root: Links to database query processing.
-
args: Captures input arguments for the update.
-
ctx: Provides the context of the request, including HTTP details and user info.
-
internal: Specifies whether the request is internal (set to false).
-
select: Defines which fields are selected for the response.
-
Purpose of the After Hook:
- Logging: Log the details of the updated user for auditing or monitoring.
- Notifications: Trigger notifications or alerts based on the user update.
- Data Synchronization: Synchronize the updated user data with other systems.
- Trigger Workflows: Start additional workflows or processes after the update is complete, such as recalculating dependent data or generating reports.
3.Delete Hook
This hook allows you to execute logic before and after deleting a user record.
Before Delete Hook
Rule Structure
import mercury from "@mercury-js/core";
mercury.hook.before("DELETE_USER_RECORD", (this:any) => {
console.log("Before Deleting User: ", this);
});
The before
hook for the "DELETE_USER_RECORD" action using Mercury.js executes custom logic before the delete action is performed on the user model.
Explination
1. import mercury from "@mercury-js/core"
The Mercury.js library provides a set of tools to manage actions and models in your application. By importing mercury, you gain access to the hook mechanism that lets you define custom logic for model action like delete.
2. mercury.hook.before("DELETE_USER_RECORD", function(this: any)):
This line registers a before hook that listens for the DELETE_USER_RECORD event.
3.DELETE_USER_RECORD
This string represents the specific action to which the hook is attached. In this case, it refers to the deletion of a model record in a model, most likely a User model. The hook will be executed before a user record has been deleting.
4.function (this: any)
The callback function is executed before the DELETE_USER_RECORD action. this refers to the context or state in which the action is executing. This is important because it contains information about what happened during the user deletion process. this is used to capture the context of the action being processed.
What this Represents
When printing this in the console, the object will likely contain:
name: 'UserName',
record: {
\_id: new ObjectId('123'),
name: 'User',
createdOn: 2024-09-22T14:50:46.367Z,
updatedOn: 2024-09-22T14:50:46.367Z,
id: '123'
},
user: { profile: 'ADMIN' },
options: {
root: { mercuryResolvers: [Object] },
args: { id: '123' },
ctx: {
user: [Object],
[Symbol(kCapture)]: false,
[Symbol(kHeaders)]: [Object],
[Symbol(kHeadersCount)]: 34,
[Symbol(kTrailers)]: null,
[Symbol(kTrailersCount)]: 0
},
internal: false
}
-
name
:This represents the type or name of the model or entity involved. In this case, it's a user that is about to be deleted. -
record
:This contains the details of the specific user that is about to be deleted. -
user
:Information about the user performing the action, including their id and profile (e.g., ADMIN). -
options
:Contains additional contextual information about the request and environment.-
root:Contains MercuryJS resolvers for handling the request. The details of the resolver are not fully visible here.
-
args:
{ id: '123' }
:Represents the arguments passed to the request. In this case, the id of the user to be deleted is passed as an argument. -
ctx:The request context (contains metadata from the HTTP request).
- user : Represents additional details about the user making the request, typically used for authentication and authorization checks.
-
-
internal
: false:This flag indicates whether the request is an internal system request. Here, false means it is a user-driven or external request.
Purpose of the before Hook This before hook provides an opportunity to execute custom logic before a model record is deleted. You can inspect the this context to access important information.
After Delete Hook:
Rule Structure
import mercury from "@mercury-js/core";
mercury.hook.after("DELETE_USER_RECORD", (this:any) => {
console.log("After Deleting User: ", this);
});
The after
hook for the "DELETE_USER_RECORD" action using Mercury.js runs a custom function after a user record is deleted.
Explination
1. import mercury from "@mercury-js/core"
The Mercury.js library provides a set of tools to manage actions and models in your application. By importing mercury, you gain access to the hook mechanism that lets you define custom logic for model action like delete.
2. mercury.hook.after("DELETE_USER_RECORD", function(this: any)):
This line registers a after hook that listens for the DELETE_USER_RECORD event.
3.DELETE_USER_RECORD
This string represents the specific action to which the hook is attached. In this case, it refers to the deletion of a model record in a model, most likely a User model. The hook will be executed after a user record has been deleted.
4.function (this: any)
The callback function is executed after the DELETE_USER_RECORD action. this refers to the context or state in which the action is executing. This is important because it contains information about what happened during the user deletion process. this is used to capture the context of the action being processed.
What this Represents
When printing this in the console, the object will likely contain:
{
name: 'User',
deletedRecord: {
\_id: new ObjectId('123'),
name: 'UserName',
createdOn: 2024-09-23T07:30:48.031Z,
updatedOn: 2024-09-23T07:30:48.031Z,
\_\_v: 0,
id: '123'
},
record: { acknowledged: true, deletedCount: 1 },
user: { profile: 'ADMIN' },
options: {
root: { mercuryResolvers: [Object] },
args: { id: '123' },
ctx: {
user: [Object],
[Symbol(kCapture)]: false,
[Symbol(kHeaders)]: [Object],
[Symbol(kHeadersCount)]: 34,
[Symbol(kTrailers)]: null,
[Symbol(kTrailersCount)]: 0
},
internal: false
}
}
-
name
:This represents the type or name of the model or entity involved. -
deletedRecord
:Contains details of the user that has just been deleted. -
record
: Contains the result of the delete operation.- acknowledged: Indicates that the delete operation was acknowledged by the database (true).
- deletedCount: The number of users deleted as a result of the operation (1).
-
user
: Information about the user who initiated the deletion. In this case, the user has an "ADMIN" profile. -
options
:Additional contextual information about the request and environment. -
root:Contains MercuryJS resolvers for handling the request.
-
args:
{ id: '123' }
: The arguments passed to the request, specifically the id of the user that was deleted. -
user: The current user making the request (this object is used in authorization logic).
Purpose of the before Hook
-
Logging: Track what was deleted and by whom.
-
Auditing: Keep records for compliance or debugging.
-
Triggering post-deletion actions: Notify related users, update other dependent records, or clean up related data.
4. Get Hook
In this example, we add custom logic before and after retrieving a user in the User model.
Before Get Hook
Rule Structure
import mercury from "@mercury-js/core";
// Define a before hook for the "get" action
mercury.hook.before("GET_USER_RECORD", (this: any) => {
console.log("Before Retrieving User: ", this);
});
The before
hook for the "GET_USER_RECORD" action using Mercury.js executes custom logic before the "user" record is retrieved.
Explanation
1. import mercury from "@mercury-js/core"
The Mercury.js library provides a set of tools to manage actions and models in your application. By importing mercury, you gain access to the hook mechanism that lets you define custom logic for model actions like get.
2. mercury.hook.before("GET_USER_RECORD", function(this: any))
This line registers a before hook that listens for the GET_USER_RECORD event.
3. function(this: any)
The function is defined with this
bound to the current context in which the action is executed. In TypeScript, this: any
indicates that this
can hold any type of value.
What this Represents
The this
context in Mercury hooks usually provides detailed information about the current operation. Here's what you might see when printing this
in the console:
{
name: 'User',
query: { id: '123' },
user: { id: '1', profile: 'ADMIN' },
options: {
root: undefined,
args: { input: [Object] },
ctx: {
user: [Object],
[Symbol(realm)]: [Object],
[Symbol(state)]: [Object],
[Symbol(signal)]: [AbortSignal],
[Symbol(abortController)]: [AbortController],
[Symbol(headers)]: [HeadersList],
[Symbol(internal request)]: [Object]
},
internal: false,
populate: [],
select: [ 'id', 'name' ]
}
}
name
: The name of the model being processed (e.g., User).query
: The query parameters used to retrieve the User record.user
: Information about the user performing the action, including their id and profile (e.g., ADMIN).options
: Additional metadata and configuration options for the get action.
Purpose of the Hook:
This before hook gives you an opportunity to execute custom logic before the model record is retrieved. You can inspect the this
context to access important information, such as the query parameters, the user performing the action, and the environment in which the action is happening.
After Get Hook
Rule Structure
import mercury from "@mercury-js/core";
// Define an after hook for the "get" action
mercury.hook.after("GET_USER_RECORD", (this: any) => {
console.log("After Retrieving User: ", this);
});
The after
hook for the "GET_USER_RECORD" action using Mercury.js runs a custom function after a user record is retrieved.
Explanation
1. import mercury from "@mercury-js/core"
The Mercury.js library provides a set of tools to manage actions and models in your application. By importing mercury, you gain access to the hook mechanism that lets you define custom logic for model actions like get.
2. mercury.hook.after("GET_USER_RECORD", function(this: any))
This line registers an after hook that listens for the GET_USER_RECORD event.
3. GET_USER_RECORD
This string represents the specific action to which the hook is attached. In this case, it refers to the retrieval of a model record in a model, most likely a User model. The hook will be executed after a user record has been retrieved successfully.
4. function (this: any)
The callback function is executed after the GET_USER_RECORD action is completed. this
refers to the context or state in which the action was executed. This is important because it contains information about what happened during the user retrieval process.
What this Represents
When printing this
in the console, the object will likely contain:
{
name: 'User',
record: {
_id: new ObjectId('123'),
userName: 'User',
createdOn: 2024-09-22T14:50:46.367Z,
updatedOn: 2024-09-22T14:50:46.367Z,
id: '123'
},
user: { profile: 'ADMIN' },
options: {
root: { mercuryResolvers: [Object] },
args: { input: [Object] },
ctx: {
// context of the user using mercuryjs
},
internal: false,
populate: [],
select: [ 'id', 'name' ]
}
}
name
: Specifies the name of the model being processed. In this case, it is set to 'User', indicating the operation involves a user-related entity in the system.record
: Stores all the details about the user. It represents the actual user being retrieved from the database.user
: Contains metadata about the user who is performing the operation, such as retrieving a user.profile
: Represents the role or profile of the user making the request (e.g., 'ADMIN'). This field may be used to apply permissions or role-based access control during the operation.
options
: Contains additional metadata about the request, including resolver information, arguments, and selection options.root
: Connects to the database and handles how the model is processed based on the query or mutation.args
: Captures the input provided to the operation, such as form data or request parameters. In GraphQL operations, this includes the input fields for mutations or queries (like the userName).ctx
: Context of the request, including user data, headers, and control symbols.- user: Represents additional details about the user making the request, typically used for authentication and authorization checks.
- internal: Boolean indicating whether this is an internal request.
- populate: Specifies which related fields to populate.
- select: Fields selected for the response (e.g., id, name).
Purpose of the Hook: The after hook allows you to perform tasks or trigger logic after the user retrieval has been finalized. Some common use cases include:
-
Logging information about the retrieved user.
-
Triggering additional workflows, such as sending notifications.
-
Updating related data or records based on the retrieved user.
-
Auditing and tracking retrieval events for monitoring or debugging purposes.
5.Paginate Hook
This hook allows you to execute logic before and after listing or paginating a user record.
Before Paginate Hook
Rule Structure
import mercury from "@mercury-js/core";
// Define a before hook for the "paginate" action
mercury.hook.before("PAGINATE_USER_RECORD", (this:any) => {
console.log("Before Listing User: ", this);
});
The before
hook for the "PAGINATE_USER_RECORD" action using Mercury.js executes custom logic before the user records are paginated or listed.
Explanation
1. import mercury from "@mercury-js/core"
Mercury.js provides the tools to handle actions and events in your application. By importing Mercury, you can define hooks for various model actions, such as before paginate or list a user.
2. mercury.hook.before("PAGINATE_USER_RECORD", function(this: any))
Registers an before hook that listens for the "PAGINATE_USER_RECORD" event. The hook will trigger before a user record has been paginate or listed.
3. "PAGINATE_USER_RECORD"
This string represents the specific action tied to the paginate or list of a user model record. The hook runs before the user paginate or list process is completed.
4. function (this: any)
The callback function is executed before the "PAGINATE_USER_RECORD" action, providing access to the context and state at the time of the paginate or list.
What this Represents
before the user is paginated or updated, the this object will contain relevant information about the paginate or list process: