Lifecycle Hooks
Relevant source files
The following files were used as context for generating this wiki page:
Lifecycle Hooks in Seaography provide a mechanism for intercepting and customizing the behavior of GraphQL operations at various stages of their execution. They allow developers to implement cross-cutting concerns such as authorization, data filtering, and audit logging by plugging into the resolver logic for entities and fields.
The hook system is integrated into the BuilderContext, enabling fine-grained control over how SeaORM entities are exposed and manipulated through the GraphQL API. This includes the ability to block access to specific entities or fields, apply automatic query filters, and monitor mutations.
Sources: src/builder_context/hooks.rs:1-10, CHANGELOG.md:58-80, README.md:18-24
Architecture and Core Interfaces
The lifecycle hook system is built around the LifecycleHooksInterface trait. This trait defines the specific methods that can be implemented to intercept different stages of an operation. The LifecycleHooks struct acts as a container for any type implementing this interface, while MultiLifecycleHooks allows for the composition of multiple hook implementations that are executed sequentially.
Operation Types
Hooks are categorized by the type of operation being performed, represented by the OperationType enum:
| Operation | Description |
|---|---|
Read | Querying or fetching entity data. |
Create | Inserting new entity records. |
Update | Modifying existing entity records. |
Delete | Removing entity records. |
Sources: src/builder_context/hooks.rs:17-23, src/builder_context/hooks.rs:75-84
Hook Execution Flow
The following diagram illustrates how the MultiLifecycleHooks implementation handles multiple hooks, ensuring that any block action stops the chain.
Sources: src/builder_context/hooks.rs:86-135
Hook Methods
Entity and Field Guards
Guards are used to authorize or restrict access to specific parts of the schema. They return a GuardAction, which can either be Allow or Block(Option<String>).
entity_guard: Invoked before an entire entity is accessed.field_guard: Invoked for every individual field within an entity access.
Sources: src/builder_context/hooks.rs:27-46, examples/sea-draw/src/schema.rs:114-135
Entity Filtering
The entity_filter method allows for the injection of custom sea_orm::Condition objects into queries. This is commonly used for Row-Level Security (RLS), such as restricting users to only see data belonging to their own organization.
fn entity_filter(
&self,
_ctx: &ResolverContext,
entity: &str,
_action: OperationType,
) -> Option<Condition> {
match entity {
"Store" | "Stores" => Some(Condition::all().add(store::Column::StoreId.eq(2))),
_ => None,
}
}
Sources: CHANGELOG.md:83-99, examples/sqlite/tests/entity_filter_tests.rs:26-38
Mutation Hooks
Hooks also provide visibility into and control over the mutation lifecycle:
before_active_model_save: Allows inspection and modification of a SeaORMActiveModelbefore it is saved to the database (currently supports insert).entity_watch: An asynchronous hook called after a mutation has occurred, useful for logging or triggering side effects.
Sources: src/builder_context/hooks.rs:55-73
Integration with BuilderContext
Lifecycle hooks are registered by providing a LifecycleHooks instance to the BuilderContext. This context is then used by the Builder to construct the GraphQL schema.
Implementation Example: Authentication
In practical applications, hooks often retrieve security context from the GraphQL ResolverContext data, which may be populated by web framework middleware (e.g., Axum or Actix-web).
impl LifecycleHooksInterface for AuthHooks {
fn entity_filter(&self, ctx: &ResolverContext, entity: &str, action: OperationType) -> Option<Condition> {
if let Ok(access) = ctx.data::<Access>() {
if access.is_root { return None; }
// Apply project-based filtering
Some(Condition::all().add(Column::ProjectId.in_list(access.project_ids())))
} else {
Some(never_condition()) // Block unauthenticated
}
}
}
Sources: examples/sea-draw/src/schema.rs:146-173, examples/sea-draw/src/schema.rs:252-273
Summary
Lifecycle Hooks serve as the primary extension point for business logic and security in Seaography. By implementing the LifecycleHooksInterface, developers can intercept standard CRUD operations to enforce permissions via guards, automatically scope data via filters, and perform post-mutation actions. This system ensures that security and domain rules are applied consistently across the entire GraphQL API without duplicating logic in individual resolvers.
Sources: src/builder_context/hooks.rs:1-135, CHANGELOG.md:58-99