# Hooks
Hooks made it possible to tune how models respond to how queries are built for CRUD operations and conditions for model lifecycles.
# CRUD Operation Hooks
CRUD operation hooks override the operation definition of the client adapter. For example, the Default Adapter interprets the create operation with something that looks like the below
const operation = "createOne" + upperFirst(model.entity);
query = query
.where("data", {
type: upperFirst(model.entity) + "CreateInput",
required: true,
value: data,
})
.mutation()
.operation(operation)
.parseWith((response) => response.data[operation]);
This means every create operation of models using this adapter will build and execute a createOne[ModelName] mutation with a required argument named data of type [ModelName]CreateInput.
class Post extends Model {
static entity = 'Post';
...
static hooks() {
return {
create(data: any, query: Query, model: ModelType): Query | void{
const operation = "create" + upperFirst(model.entity);
query.where('data', {
type: upperFirst(model.entity) + "CreateInput",
required: false,
value: data,
})
.mutation()
.operation(operation)
.parseWith((response) => response.data[operation]);
}
createMany(data: any, query: Query, model: ModelType): Query | void{
const operation = "create" + plural(upperFirst(model.entity));
// create and return a completely new query
return model.newQuery()
.select('*')
.where("data", {
type: "[" + upperFirst(model.entity) + "CreateInput]",
required: false,
value: data,
})
.mutation()
.operation(operation)
.parseWith((response) => response.data[operation]);
}
};
}
}
The above slightly changes the behaviour of the create and createMany operations only for the Post model.
Currently all adapter methods are overridable.
Note that hooks completely override adapter methods.
# Lifecycle Hooks
Lifecycle hooks are called before or after a create, update or delete operation. This gives you the opportunity to further modify mutation data or completely abort the operation.
Here is an example of a Location model that has an Address model. Changes might have been made to an instantiated location's address directly.
const location = await Location.with("address").findUnique({ id: 1 });
location.address.is_public = false;
location.$save();
In this case,
location.$isDirty(); // returns false
location.$isDeepDirty(); // return true
Now, to save the location and the changes made to its address, supply an $updating hook to make changes to the update data.
class Location extends Model {
static entity = 'Location';
static primaryKey = 'handle'
...
static hooks() {
return {
$updating(model: Location, data: Attributes) {
if (model.address && model.address.$isDirty()) {
data.address = {
update: model.address.$getAttributeChanges(),
}
}
},
}
}
# Available lifecycle hooks
class Location extends Model {
static entity = 'Location';
...
static hooks() {
return {
$creating(model: Model, data: Attributes): void | Attributes | false{
//
},
$created(model: Model): void{
//
},
$updating(model: Model, data: Attributes): void | Attributes | false{
//
},
$updated(model: Model): void{
//
},
$deleting(model: Model): void | false{
//
},
$deleted(model: Model): void{
//
},
}
}
When false is returned at $creating, $updating and $deleting, the operation is aborted.
Note that
$creatingand$createdhooks are not triggered when usingcreateMany.