Skip to main content

MutationContext

Struct MutationContext 

Source
#[non_exhaustive]
pub struct MutationContext { pub auth: AuthContext, pub request: RequestMetadata, /* private fields */ }
Expand description

Context for mutation functions (transactional database access).

Fields (Non-exhaustive)§

This struct is marked as non-exhaustive
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.
§auth: AuthContext§request: RequestMetadata

Implementations§

Source§

impl MutationContext

Source

pub fn new(db_pool: PgPool, auth: AuthContext, request: RequestMetadata) -> Self

Create a new mutation context.

Source

pub fn with_dispatch( db_pool: PgPool, auth: AuthContext, request: RequestMetadata, http_client: CircuitBreakerClient, job_dispatch: Option<Arc<dyn JobDispatch>>, workflow_dispatch: Option<Arc<dyn WorkflowDispatch>>, ) -> Self

Create a mutation context with dispatch capabilities.

Source

pub fn with_env( db_pool: PgPool, auth: AuthContext, request: RequestMetadata, http_client: CircuitBreakerClient, job_dispatch: Option<Arc<dyn JobDispatch>>, workflow_dispatch: Option<Arc<dyn WorkflowDispatch>>, env_provider: Arc<dyn EnvProvider>, ) -> Self

Create a mutation context with a custom environment provider.

Source

pub fn with_transaction( db_pool: PgPool, tx: Transaction<'static, Postgres>, auth: AuthContext, request: RequestMetadata, http_client: CircuitBreakerClient, job_dispatch: Option<Arc<dyn JobDispatch>>, workflow_dispatch: Option<Arc<dyn WorkflowDispatch>>, ) -> (Self, Arc<AsyncMutex<Option<Transaction<'static, Postgres>>>>)

Build a transactional mutation context.

Jobs/workflows dispatched through the returned context insert their rows directly on tx, so they commit atomically with the mutation and are rolled back on failure.

The caller retains ownership of the transaction via the returned handle; commit it after the handler returns successfully.

Source

pub fn set_kv(&mut self, kv: Arc<dyn KvHandle>)

Attach a KV store handle. Called by the runtime before handing the context to the handler.

Source

pub fn kv(&self) -> Result<&dyn KvHandle>

Access the KV store.

Returns an error if the runtime did not supply a KV handle (this should not happen in production; it can only occur in unit tests that construct a bare MutationContext without going through the runtime).

Source

pub fn set_email(&mut self, sender: Arc<dyn EmailSender>)

Attach an email sender.

Source

pub fn email(&self) -> Result<&dyn EmailSender>

Access the email sender.

Source

pub fn is_transactional(&self) -> bool

Source

pub async fn conn(&self) -> Result<ForgeConn<'_>>

Acquire a connection compatible with sqlx compile-time checked macros.

In transactional mode, returns a guard over the active transaction. Otherwise acquires a fresh connection from the pool.

let mut conn = ctx.conn().await?;
sqlx::query_as!(User, "INSERT INTO users ... RETURNING *", ...)
    .fetch_one(&mut *conn)
    .await?
Source

pub fn bypass_pool(&self) -> &PgPool

Direct pool access that bypasses the active transaction.

In a transactional mutation, this returns the raw sqlx::PgPool and any queries run on it execute outside the transaction — so they will not see uncommitted writes and will not be rolled back if the mutation fails. Prefer MutationContext::conn or [MutationContext::db] for anything that should participate in the transaction. Reach for this only for operations that fundamentally cannot run inside a transaction (e.g. LISTEN/NOTIFY, advisory locks, or background pool work).

Source

pub fn tx(&self) -> DbConn<'_>

Get a DbConn for use in shared helper functions.

In transactional mode, returns a transaction-backed DbConn. Otherwise returns a pool-backed DbConn.

pub async fn list_items(db: DbConn<'_>) -> Result<Vec<Item>> { ... }

#[forge::mutation]
pub async fn items_snapshot(ctx: &MutationContext, input: Input) -> Result<Vec<Item>> {
    list_items(ctx.tx()).await
}
Source

pub fn db_conn(&self) -> DbConn<'_>

Get a DbConn for use in shared helper functions (alias for tx()).

Source

pub fn http(&self) -> HttpClient

Get the HTTP client for external requests.

Requests go through the circuit breaker automatically. When the handler declared an explicit timeout, that timeout is also applied to outbound HTTP requests unless the request overrides it.

Source

pub fn raw_http(&self) -> &Client

Get the raw reqwest client, bypassing circuit breaker execution.

Source

pub fn set_http_timeout(&mut self, timeout: Option<Duration>)

Set the default outbound HTTP request timeout for this context.

Source

pub fn user_id(&self) -> Result<Uuid>

Get the authenticated user’s UUID. Returns 401 if not authenticated.

Source

pub fn tenant_id(&self) -> Option<Uuid>

Get the tenant ID from JWT claims, if present.

Source

pub fn claim(&self, key: &str) -> Option<&Value>

Look up a custom JWT claim by name. Reserved JWT claims (iss, aud, nbf, jti, sub, iat, exp, roles) are filtered out. Shortcut for self.auth.claim(key).

Source

pub fn set_token_issuer(&mut self, issuer: Arc<dyn TokenIssuer>)

Set the token issuer for this context.

Source

pub fn set_token_ttl(&mut self, ttl: AuthTokenTtl)

Set the token TTL configuration (from forge.toml [auth]).

Source

pub fn set_max_jobs_per_request(&mut self, limit: usize)

Set the maximum number of background jobs this mutation may dispatch. A value of 0 disables the limit.

Source

pub fn issue_token(&self, claims: &Claims) -> Result<String>

Issue a signed JWT from the given claims.

Only available when HMAC auth is configured in forge.toml. Returns an error if auth is not configured or uses an external provider (RSA/JWKS).

let claims = Claims::builder()
    .user_id(user.id)
    .duration_secs(7 * 24 * 3600)
    .build()
    .map_err(ForgeError::internal)?;

let token = ctx.issue_token(&claims)?;
Source

pub async fn issue_token_pair( &self, user_id: Uuid, roles: &[&str], ) -> Result<TokenPair>

Issue an access + refresh token pair for the given user.

Stores the refresh token hash in forge_refresh_tokens and returns both tokens. Use rotate_refresh_token() to exchange a refresh token for a new pair, and revoke_refresh_token() to invalidate one.

TTLs come from [auth] in forge.toml:

  • access_token_ttl (default “1h”)
  • refresh_token_ttl (default “30d”)
Source

pub async fn rotate_refresh_token( &self, old_refresh_token: &str, ) -> Result<TokenPair>

Rotate a refresh token: validate the old one, issue a new pair.

The old token is atomically deleted and a new access + refresh pair is returned. Fails if the token is invalid or expired.

Source

pub async fn revoke_refresh_token(&self, refresh_token: &str) -> Result<()>

Revoke a specific refresh token (e.g., on logout).

Source

pub async fn revoke_all_refresh_tokens(&self, user_id: Uuid) -> Result<()>

Revoke all refresh tokens for a user (e.g., on password change or account deletion).

Source

pub async fn dispatch_job<T: Serialize>( &self, job_type: &str, args: T, ) -> Result<Uuid>

Dispatch a background job.

In transactional mutations the job row is inserted on the active transaction, so it only becomes visible to workers after commit. Outside a transaction the dispatcher writes through the pool directly.

Returns ForgeError::Validation when the call would exceed the per-request job dispatch cap configured via max_jobs_per_request.

Source

pub async fn dispatch_job_at<T: Serialize>( &self, job_type: &str, args: T, scheduled_at: DateTime<Utc>, ) -> Result<Uuid>

Dispatch a background job at a specific time.

The job row is inserted immediately but workers will not pick it up until scheduled_at is reached. In transactional mutations the insert participates in the active transaction, so the job is only committed (and becomes schedulable) once the mutation succeeds.

Returns ForgeError::Validation when the call would exceed the per-request job dispatch cap.

Source

pub async fn dispatch_job_after<T: Serialize>( &self, job_type: &str, args: T, delay: Duration, ) -> Result<Uuid>

Dispatch a background job after a delay.

Equivalent to dispatch_job_at(job_type, args, Utc::now() + delay). The delay is computed at call time.

Returns ForgeError::Validation when the call would exceed the per-request job dispatch cap, or when delay is too large to represent as a chrono duration.

Source

pub async fn dispatch<J: ForgeJob>(&self, args: J::Args) -> Result<Uuid>

Type-safe dispatch: resolves the job name from the type’s ForgeJob impl and serializes the args at the call site.

Source

pub async fn dispatch_at<J: ForgeJob>( &self, args: J::Args, scheduled_at: DateTime<Utc>, ) -> Result<Uuid>

Type-safe dispatch at a specific time.

Source

pub async fn dispatch_after<J: ForgeJob>( &self, args: J::Args, delay: Duration, ) -> Result<Uuid>

Type-safe dispatch after a delay.

Source

pub async fn cancel_job( &self, job_id: Uuid, reason: Option<String>, ) -> Result<bool>

Request cancellation for a job.

Source

pub async fn start_workflow<T: Serialize>( &self, workflow_name: &str, input: T, ) -> Result<Uuid>

Start a durable workflow.

In transactional mutations the run row and its $workflow_resume job are written on the active transaction, so the worker only picks the run up after commit. Outside a transaction the dispatcher writes through the pool directly.

Source

pub async fn start<W: ForgeWorkflow>(&self, input: W::Input) -> Result<Uuid>

Type-safe workflow start: resolves the workflow name from the type’s ForgeWorkflow impl.

Trait Implementations§

Source§

impl AuthenticatedContext for MutationContext

Source§

fn user_id(&self) -> Result<Uuid>

Returns the authenticated user’s UUID, or Unauthorized if the request is not authenticated or the subject is not a UUID.
Source§

fn tenant_id(&self) -> Option<Uuid>

Returns the tenant ID from the tenant_id JWT claim, if present.
Source§

impl EnvAccess for MutationContext

Source§

fn env_provider(&self) -> &dyn EnvProvider

Source§

fn env(&self, key: &str) -> Option<String>

Source§

fn env_or(&self, key: &str, default: &str) -> String

Source§

fn env_require(&self, key: &str) -> Result<String>

Source§

fn env_parse<T: FromStr>(&self, key: &str) -> Result<T>
where T::Err: Display,

Source§

fn env_parse_or<T: FromStr>(&self, key: &str, default: T) -> Result<T>
where T::Err: Display,

Returns the default when unset; errors only if the variable is set but unparseable.
Source§

fn env_contains(&self, key: &str) -> bool

Source§

impl HandlerContext for MutationContext

Source§

fn db(&self) -> ForgeDb

Database handle with automatic db.query tracing spans. Read more
Source§

fn db_conn(&self) -> DbConn<'_>

Unified connection handle for shared helper functions. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more