pub struct RuntimeConfig {Show 14 fields
pub cache_query_plans: bool,
pub max_query_depth: usize,
pub max_query_complexity: usize,
pub max_page_size: Option<u32>,
pub enable_tracing: bool,
pub field_filter: Option<FieldFilter>,
pub rls_policy: Option<Arc<dyn RLSPolicy>>,
pub field_authorizer: Option<Arc<dyn FieldAuthorizer>>,
pub authorizer: Option<Arc<dyn Authorizer>>,
pub query_timeout_ms: u64,
pub jsonb_optimization: JsonbOptimizationOptions,
pub query_validation: Option<QueryValidatorConfig>,
pub audit_mutations: bool,
pub changelog_enabled: bool,
}Expand description
Runtime configuration for the FraiseQL query executor.
Controls safety limits, security policies, and performance tuning. All settings have production-safe defaults and can be overridden via the builder-style methods.
§Defaults
| Field | Default | Notes |
|---|---|---|
cache_query_plans | true | Caches parsed query plans for repeated queries |
max_query_depth | 10 | Prevents stack overflow on recursive GraphQL |
max_query_complexity | 1000 | Rough cost model; tune per workload |
enable_tracing | false | Emit OpenTelemetry spans for each query |
query_timeout_ms | 30 000 | Hard limit; 0 disables the timeout |
field_filter | None | No field-level access control |
rls_policy | None | No row-level security |
authorizer | None | No operation-level authorization |
§Example
use fraiseql_core::runtime::RuntimeConfig;
use fraiseql_core::security::FieldFilterConfig;
let config = RuntimeConfig {
max_query_depth: 5,
max_query_complexity: 500,
enable_tracing: true,
query_timeout_ms: 5_000,
..RuntimeConfig::default()
}
.with_field_filter(
FieldFilterConfig::new()
.protect_field("User", "salary")
.protect_field("User", "ssn"),
);Fields§
§cache_query_plans: boolEnable query plan caching.
max_query_depth: usizeMaximum query depth (prevents deeply nested queries).
max_query_complexity: usizeMaximum query complexity score.
max_page_size: Option<u32>Maximum number of rows a top-level first/last/limit argument may
request, guarding against unbounded-pagination denial of service (#421):
the top-level row count is the one knob that sizes the database result set
and the serialized response. A request exceeding this is rejected with a
crate::FraiseQLError::Validation. None disables the ceiling. Default
Some(1000).
enable_tracing: boolEnable performance tracing.
field_filter: Option<FieldFilter>Optional field filter for access control. When set, validates that users have required scopes to access fields.
rls_policy: Option<Arc<dyn RLSPolicy>>Optional row-level security (RLS) policy.
When set, evaluates access rules based on SecurityContext to determine
what rows a user can access (e.g., tenant isolation, owner-based access).
Optional dynamic field-level authorizer.
When set, fields marked policy-gated in the compiled schema
(FieldDefinition::authorize) are passed to
this authorizer per row, which returns an allow/deny decision based on the
principal, the parent row, and the field arguments. Composes as a logical AND
with the static requires_scope gate and is fail-closed (any error denies).
See FieldAuthorizer.
Optional dynamic operation-level authorizer.
When set, every operation (query, mutation, subscription) is passed to this
authorizer before dispatch, which returns an allow/deny decision based on the
principal (or None when anonymous), the operation kind and name, and the
request input. Composes as a logical AND with the static requires_role gate
and is fail-closed (any error or raise denies with HTTP 403 / FORBIDDEN).
See Authorizer.
query_timeout_ms: u64Query timeout in milliseconds (0 = no timeout).
jsonb_optimization: JsonbOptimizationOptionsJSONB field optimization strategy options
query_validation: Option<QueryValidatorConfig>Optional query validation config.
When Some, QueryValidator::validate() runs at the start of every
Executor::execute() call, before any parsing or SQL dispatch.
This provides DoS protection for direct fraiseql-core embedders that
do not route through fraiseql-server (which already runs RequestValidator
at the HTTP layer). Enforces: query size, depth, complexity, and alias count
(alias amplification protection).
Set None to disable (default) — useful when the caller applies
validation at a higher layer, or when fraiseql-server is in use.
audit_mutations: boolEmit structured tracing events for every successfully-executed mutation.
When true, a tracing::info! event with target "fraiseql::mutation_audit" is
emitted at the end of every successful execute_mutation_query_with_security() call.
The event carries fields: mutation_name, entity_type, operation, tenant_id.
Zero-cost when disabled: the guard if !self.config.audit_mutations { return }
short-circuits before any string formatting or allocation occurs.
Set to true when audit_logging_enabled = true in the compiled schema’s
[security.enterprise] section (threaded through Server::new() at startup).
changelog_enabled: boolGlobal switch for the Change-Spine change-log outbox write (default true).
When true, every successful state-changing mutation writes one
core.tb_entity_change_log row in-transaction (the framework owns the
write). Set false to disable the outbox globally — e.g. for an
application that does not consume the Change Spine — so no mutation pays
the write. The per-mutation
MutationDefinition.changelog flag
composes as a logical AND on top of this: a row is written only when the
global switch is on and the mutation is not individually opted out.
Sourced from [changelog] enabled in fraiseql.toml, overridable at
runtime by FRAISEQL_CHANGELOG_ENABLED.
Implementations§
Source§impl RuntimeConfig
impl RuntimeConfig
Sourcepub fn with_field_filter(self, config: FieldFilterConfig) -> Self
pub fn with_field_filter(self, config: FieldFilterConfig) -> Self
Create a new runtime config with a field filter.
§Example
use fraiseql_core::runtime::RuntimeConfig;
use fraiseql_core::security::FieldFilterConfig;
let config = RuntimeConfig::default()
.with_field_filter(
FieldFilterConfig::new()
.protect_field("User", "salary")
.protect_field("User", "ssn")
);Sourcepub fn with_rls_policy(self, policy: Arc<dyn RLSPolicy>) -> Self
pub fn with_rls_policy(self, policy: Arc<dyn RLSPolicy>) -> Self
Configure row-level security (RLS) policy for access control.
When set, the executor will evaluate the RLS policy before executing queries,
applying WHERE clause filters based on the user’s SecurityContext.
§Example
use fraiseql_core::runtime::RuntimeConfig;
use fraiseql_core::security::DefaultRLSPolicy;
use std::sync::Arc;
let config = RuntimeConfig::default()
.with_rls_policy(Arc::new(DefaultRLSPolicy::new()));Configure a dynamic field-level authorizer.
When set, fields marked policy-gated in the compiled schema
(FieldDefinition::authorize) are evaluated
per row by this authorizer. The decision composes as a logical AND with the
static requires_scope gate and is fail-closed (any error or raise denies
with HTTP 403 / FORBIDDEN). Parallel to with_rls_policy.
§Example
use fraiseql_core::runtime::RuntimeConfig;
use fraiseql_core::security::{
FieldAuthorizer, FieldAuthzRequest, FieldAuthzDecision,
};
use fraiseql_core::error::Result;
use std::sync::Arc;
struct AllowAll;
impl FieldAuthorizer for AllowAll {
fn authorize_field(&self, _req: &FieldAuthzRequest<'_>) -> Result<FieldAuthzDecision> {
Ok(FieldAuthzDecision::Allow)
}
}
let config = RuntimeConfig::default().with_field_authorizer(Arc::new(AllowAll));Configure a dynamic operation-level authorizer.
When set, every operation (query, mutation, subscription) is passed to this
authorizer before dispatch. The decision composes as a logical AND with the
static requires_role gate and is fail-closed (any error or raise denies with
HTTP 403 / FORBIDDEN). Parallel to
with_field_authorizer and
with_rls_policy.
§Example
use fraiseql_core::runtime::RuntimeConfig;
use fraiseql_core::security::{Authorizer, AuthzRequest, AuthzDecision};
use fraiseql_core::error::Result;
use std::sync::Arc;
struct AllowAll;
impl Authorizer for AllowAll {
fn authorize(&self, _req: &AuthzRequest<'_>) -> Result<AuthzDecision> {
Ok(AuthzDecision::Allow)
}
}
let config = RuntimeConfig::default().with_authorizer(Arc::new(AllowAll));Sourcepub fn from_compiled_schema(schema: &CompiledSchema) -> Result<Self, String>
pub fn from_compiled_schema(schema: &CompiledSchema) -> Result<Self, String>
Build a RuntimeConfig from a compiled schema, applying every
schema-derived runtime setting that an executor must honor.
This is the single seam every server entry point routes through so
the config can never drift by constructor (H16): Server::new,
with_relay_pagination, and with_flight_service previously built the
executor with RuntimeConfig::default, silently dropping the
compiled audit-logging flag, the #421 page-size ceiling, and the
change-log toggle, and skipping the schema-format-version check.
Applied in order:
- Schema-format-version validation — a legacy schema (no version) warns; an incompatible future version is rejected. Coupling the check into the constructor means a caller cannot obtain a config while skipping the validation.
- Audit logging —
audit_mutationsfrom the compiled[security.enterprise] audit_logging_enabled. - Page-size ceiling (#421) —
FRAISEQL_MAX_PAGE_SIZEoverrides the compiled[validation] max_page_size, which overrides the default. - Change-log outbox toggle —
FRAISEQL_CHANGELOG_ENABLEDoverrides the compiled[changelog] write_enabled(defaulttrue).
§Errors
Returns the validation message when the schema’s schema_format_version
is incompatible with this runtime.