| title | Migration Guide |
|---|---|
| kind | reference |
| status | active |
| owner | workspace-maintainers |
| last_reviewed | 2026-08-31 |
| review_by | 2027-02-01 |
| supersedes |
graphql-orm is distributed from GitHub only. Use a reviewed full 40-character commit in rev;
neither the runtime nor macros crate is published to crates.io.
Adopt runtime and macros 0.31.1 together from one reviewed workspace release. The MSSQL notification parser now uses quick-xml 0.41. Existing input-size, notification-shape and separately authorized Broker-connection requirements remain in place. No Rust call-site, GraphQL SDL, persisted-schema or database migration is required. This dependency update does not grant queue consumption, DML or schema-management authority to an existing read-only connection.
Adopt the runtime and macros 0.31.0 together from one reviewed full Git revision. The optional MSSQL notification API uses a separately authorized connection; existing read-only pools do not acquire queue-consumption or write capability. External database owners must provision Broker and its dedicated queue/service. See SQL Server notification setup. No managed schema migration is required or performed. Notifications are one-shot invalidation hints, not durable row-change events; applications own re-registration, catch-up, permissions, and reconnect/resynchronization behavior.
Adopt graphql-orm and graphql-orm-macros 0.30.0 together from one reviewed
full Git revision. Backend-neutral semantic consumers must adopt
graphql-orm-operation-catalog 0.4.0 from that revision. The AI tool-profile
package remains 0.10.2; rebuild its generated capabilities against the new
semantic catalogue.
Generated calendar predicates now use half-open ranges over the persisted
column: today is [today, tomorrow), future begins at tomorrow, the past ends
at today, RecentDays(N) includes exactly today plus N - 1 preceding dates,
and WithinDays(N) includes exactly N dates beginning today. LteRelative
is inclusive through its selected calendar date by rendering an exclusive
next-date upper bound. Ordinary Eq, Ne, Lt, Lte, Gt, and Gte
continue binding the supplied value without normalization.
DateRangeInput.start and DateRangeInput.end are now required String
fields. Update Rust struct literals that supplied Option<String>. Both bounds
must be parseable date/timestamp values and start must not follow end.
RecentDays and WithinDays accept 1 through 36,600; signed relative offsets
accept -36,600 through 36,600. Generated query execution validates recursively
and returns INVALID_INPUT before database work. Handwritten DatabaseFilter
implementations remain source compatible because validate has a permissive
default; implement it when a custom filter has structured invariants.
The meaning of today remains backend-owned and is now internally consistent:
PostgreSQL uses session CURRENT_DATE, SQL Server uses the server-local date
from GETDATE(), and SQLite uses UTC date('now'). SQLite's in-memory spatial
fallback captures one UTC date per filtering pass and does not push
clock-dependent predicates into its SQL prefilter. This release does not add a
cross-backend application-timezone setting or normalize exact input strings.
A future typed date/time input and context-bound calendar-clock design requires
separate compatibility review.
Date semantic metadata and catalogue fingerprints change. Rebuild reviewed catalogues, automatic query capabilities, and retained bindings. There is no database schema, stored-data, backup, or migration-history change.
Computed fields can join generated ordering without a handwritten query:
#[graphql_orm(order_expression(
name = "Duration",
expression = "COALESCE(finished_at, :as_of) - started_at",
parameters = "duration_order_parameters"
))]The expression is trusted, backend-specific server configuration. Requests
continue to supply only ASC or DESC. For :named binds, the configured
synchronous function receives &async_graphql::Context and returns
async_graphql::Result<OrderExpressionParameters>; use SqlValue variants for
the values. Raw ?, $n, and @Pn placeholders are rejected. Programmatic
queries using a contextual expression call EntityQuery::order_by_with_context.
Generated pagination now adds missing primary-key columns as ascending
tie-breakers. Review the resulting query plan and add an expression/index
strategy where the selected backend supports one.
SortExpression now carries bound values so query rendering can number
order-expression placeholders after filter placeholders on every backend.
Handwritten parameter-free sort construction should use
SortExpression::unbound("column ASC"); handwritten struct literals must add
values: Vec::new().
A readable relation can expose a server-generated count order without a projection:
#[relation(
target = "StaffAssignment",
from = "id",
to = "policy_id",
multiple,
order_aggregate(name = "AssignedStaffCount", aggregate = "count")
)]Requests again supply only ASC or DESC. The macro obtains the target table
from its entity type and generates the correlated count from the declared key
mapping. The initial contract supports count on unconditional, readable
relations; conditional relations fail compilation when combined with
order_aggregate.
For a relation entity that already has a handwritten #[ComplexObject] impl,
add #[graphql_orm(compose_complex_object)] to the entity and replace that
impl attribute with #[graphql_complex_object]. Handwritten methods remain
unchanged; generated relations retain their batching and arguments. Do not
leave both complex-object attributes on the impl.
There is no table, column, constraint, or stored-data migration. Regenerate and review GraphQL SDL and semantic/capability fingerprints when adding a new ordering field. Review the query plan for correlated relation counts and add indexes on the target key columns used by frequently sorted relations.
Adopt graphql-orm and graphql-orm-macros 0.27.0 together from one reviewed
full Git revision. Existing relation declarations need no change.
Externally managed tables that reuse one reference column for multiple target types can declare a fixed source discriminator:
#[relation(
target = "Document",
from = "reference_id",
to = "id",
source_condition(field = "reference_kind", equals = 1),
emit_fk = false
)]
pub document: Option<Document>,The inverse collection can constrain the target rows:
#[relation(
target = "Activity",
from = "id",
to = "reference_id",
target_condition(column = "reference_kind", equals = 1),
multiple,
emit_fk = false
)]
pub activity: Vec<Activity>,Condition values accept string, integer, float, and boolean literals. A source
condition must name a persisted scalar Rust field with a matching type; target
conditions name a physical target column and use the same trusted, bound-value
SQL path as generated filters. Conditional relationships always require
emit_fk = false because the discriminator makes the join conditional rather
than a physical foreign-key contract.
No database, table, column, constraint, or stored-data migration is required. After adding conditional relationships, regenerate the semantic catalogue and any derived capability fingerprints so the new selectable graph is reviewed and deployed atomically.
Git consumers using auth-agql must align direct agql-auth dependencies to
0.18.0 at merged revision 527d15d28e3c295a6f6b5e6d74559a7aecdc1322.
Adopt aligned graphql-orm and graphql-orm-macros 0.26.0 from the same
reviewed monorepo revision. The bridge remains projection-only. Hosts adopting
typed authorization-role grants follow the upstream issuer/resource-server
rollout; no ORM schema, migration, generated-code, database-policy, or
stored-data change is required.
Git consumers using auth-agql must align direct agql-auth dependencies to
0.17.1 at merged revision b792241b7d9bb46bad81dde4534ae5b39857f614.
Adopt aligned graphql-orm and graphql-orm-macros 0.25.1 from the same
reviewed monorepo revision. The upstream patch makes default session-context
serialization compact; no API, schema, migration, generated-code,
database-policy, or stored-data change is required in this workspace.
Git consumers using auth-agql must align direct agql-auth dependencies to
0.17.0 at merged revision 82650a447f3b6875160254fa1293b3a0e305c224.
The role-scope expansion contract is additive and opt-in; the ORM bridge does
not fetch catalogues or change authorization decisions. No schema, migration,
generated code, or database-policy change is required. Adopt aligned
graphql-orm and graphql-orm-macros 0.25.0 from the same reviewed revision.
Hosts enabling auth-agql or depending directly on agql-auth must align to
version 0.16.0 at merged revision
3bc38cd94794f1e868a9cc3a5551047b95a32105. Adopt graphql-orm and the
aligned graphql-orm-macros 0.24.0 from the same reviewed monorepo revision.
This retains one public auth type universe. Version 0.16 adds
consumer-supplied exact-only hierarchical matcher policy; the ORM bridge
remains projection-only and does not select or install that policy. No
database, schema, GraphQL SDL, generated-code, token, backup, or stored-data
migration is required.
Update graphql-orm, graphql-orm-macros, and
graphql-orm-operation-catalog together to 0.23.0 / 0.3.0 at one reviewed
full Git revision. No database, table, column, constraint, or AI
schema-module migration is required.
Regenerate semantic catalogues and automatic query capabilities. Many
relationships now require collection_bound. Old catalogue version 1
payloads are rejected. Server-fixed object lists no longer accept a
model-selected maximumItems and use the declared semantic ceiling in
disclosure.
Update graphql-orm and graphql-orm-macros together to 0.22.1 at one
reviewed full Git revision. No resolver SDL, database schema, table, column,
constraint, data, or AI schema-module migration is required.
The canonical semantic catalogue now describes a generated to-many
relationship's nullable ordering argument as ChildOrderByInput, matching the
resolver SDL. Root list queries continue to describe and accept
[ChildOrderByInput!] as before. Regenerate semantic catalogues and automatic
query capability fingerprints after adoption; do not preserve fingerprints
derived from the incorrect relationship argument shape.
AiGraphqlQueryCapabilityCatalog::compile remains strict. Any future
catalogue/SDL type, nullability, list-item, name, or argument-set mismatch
continues to fail readiness.
Update graphql-orm and graphql-orm-macros together to 0.22.0 at one
reviewed full Git revision. Backend-neutral consumers use
graphql-orm-operation-catalog 0.2.0.
Existing entity and operation declarations continue to compile. The former
minimal borrowed GraphqlEntitySemanticMetadata values are now owned,
serializable catalogue values with typed field capabilities. Code constructing
those structs directly must migrate to the expanded fields or, preferably,
consume Entity::graphql_semantic_metadata() and
graphql_orm_semantic_catalog().
Descriptions resolve from an explicit semantic description, then Rust doc
comments, then a stable humanized public-name fallback. Use the same doc text
when an entity also derives async_graphql::SimpleObject; that external derive
owns its SDL field documentation. Sensitive fields are now structurally
Secret and NeverExport, while private and read = false fields remain
absent.
Handwritten resolver roots can publish canonical metadata beside their
async-graphql impl:
#[derive(Default)]
struct HealthQueries;
#[graphql_orm_custom_operations(kind = "query", authorization = true)]
#[async_graphql::Object]
impl HealthQueries {
/// Returns current service health.
#[graphql_orm(
result_classification = "public",
result_export = "exportable"
)]
async fn health(&self) -> String {
"ok".to_owned()
}
}
schema_roots! {
entities: [Account],
described_query_types: [HealthQueries],
}The described_query_types, described_mutation_types, and
described_subscription_types lists replace the need to repeat one root in
both an extra_* list and semantic_custom_operations; direct
GraphQLSemanticObject results are collected as well. Legacy lists remain
supported, but the same root cannot use both forms.
Unclassified custom scalar/enum leaves now fail safe as
Secret/NeverExport and therefore do not become automatic AI capabilities.
Declare result_classification and result_export together only for a
reviewed exportable root. An exportable scalar/enum list additionally requires
positive result_maximum_items; custom enum/scalar wrappers may need the
explicit result_type_kind. Object results continue deriving authority-neutral
disclosure from selected fields, and a root declaration can only tighten it.
Generated aggregate operation fingerprints now include their owning public entity identity. Compiled aggregate plan, projection, and disclosure fingerprints also bind exact selected grouping fields and metric field/operator pairs. Refresh any stored host allowlists; a response with unselected or drifted aggregate identities is rejected before provider egress.
The catalogue and per-operation semantic fingerprints intentionally change
when public descriptions, fields, relationships, capabilities, classifications
or exposure change. They are drift evidence, never authority. There is no
database, stored-data, backup, migration-history, or AI schema-module migration.
Generated broadcast subscriptions are now described as best-effort and are not
eligible for a durable wait. A custom subscription may declare bounded
replay_then_live semantics only beside its resolver; downstream runtimes must
still register and verify the matching authoritative cursor/watermark source.
Public mutations now carry a closed AI execution classification in semantic
catalogue v1. Existing declarations remain Prohibited and therefore acquire
no AI capability. Opt in generated categories with ai_mutations(...), or add
ai_execution = "automatic" | "approval_required" | "prohibited" to an
existing mutation graphql_orm_custom_operations declaration. This changes
semantic fingerprints only. It does not change SDL, resolver behavior,
database schema, stored rows, or authorization.
Typed grouped aggregates are additive. Every GraphQLEntity now emits a
closed aggregate-field enum for repository use, while no GraphQL root is added
unless the entity declares aggregate = true. That opt-in changes the schema
and operation catalogue by adding the bounded aggregate query. Decimal fields
must declare #[graphql_orm(decimal(precision = P, scale = S))]; SQLite stores
their exact scaled integer representation while PostgreSQL and SQL Server use
native fixed-precision types. Adding a new Decimal column is an ordinary
application schema migration, but adopting this release without declaring one
requires no database or stored-data migration. Logical backups containing a
Decimal field use the new exact Decimal value kind and include precision/scale
in the schema hash. Produce and restore those backups with 0.22.0 or later;
string-typed Decimal backup values are rejected instead of being coerced.
SQL Server compatibility constructors remain physically read-only, so existing MSSQL applications acquire no write authority after upgrading. To adopt DML against an externally managed schema, change both boundaries explicitly:
#[repository_entity(
backend = "mssql",
table = "dbo.WorkItems",
schema_policy = "external_writable"
)]
struct WorkItem { /* externally reviewed columns */ }
let database = Database::<MssqlBackend>
::connect_ado_external_writable(connection_string)
.await?;Review the database principal, table contract, entity/field/row policies,
upsert key uniqueness, and transaction behavior before adopting this mode.
ExternalWritable permits generated row DML only. It does not enable SQL
Server migration planning/application, managed RLS/search structures,
runtime-schema writes, or backup/restore. No ORM schema or stored-data
migration is performed by this release.
Generated writes in 0.22.0 also tighten the row-authorization boundary without changing public
top-level method signatures. Update/delete/bulk helpers now evaluate the locked preimage and perform
exact-key DML on one pinned transaction. Generated upsert helpers select state-machine isolation.
If application code calls MutationContext::upsert inside an explicit
Database::transaction(TransactionMode::Default, ...), change that transaction to
TransactionMode::StateMachine and retry the complete callback when a serialization conflict is
classified retryable. Default-mode upserts now fail closed instead of accepting an unfenced
absent-key decision. No table, column, stored-data, migration-history, backup, GraphQL SDL, or AI
schema-module migration is required.
Update graphql-orm and graphql-orm-macros together to 0.21.1 at one
reviewed full Git revision. Applications that directly use agql-auth must
align that dependency to the same exact source:
agql-auth = { git = "https://github.com/Dastari/agql-auth.git", rev = "e841ffd382082ad7419be259fe957f949b956ff7", version = "0.15.0" }The optional auth-agql bridge remains a one-way principal projection and
requires no source changes. Version 0.15 adds upstream session-bound,
access-token-only delegation types; it does not make the ORM an issuer or
session authority. Hosts using those APIs must install an authoritative
VerifiedActiveUserSessionResolver, narrow current authority, and retain the
exact actor, resource, correlation, and registered-operation bindings.
There is no ORM schema, GraphQL SDL, migration-history, backup, stored-data, or agql-auth database migration. Remove older direct pins rather than resolving two auth package/type universes.
Update graphql-orm and graphql-orm-macros together to 0.21.0 at one
reviewed full Git revision. Existing imports of resolver-operation metadata
through graphql_orm::graphql::orm remain source compatible; the canonical
types now live in graphql-orm-operation-catalog 0.1.0 so database-neutral
manifest producers do not have to select an ORM backend.
Reusable companion packages exposing mutually exclusive sqlite, postgres,
and mssql features may replace #[graphql_entity(...)] with
#[backend_selected_graphql_entity(...)]. The latter accepts the same entity
arguments except backend and supplies the backend from the companion crate's
own selected feature. Application entities with a fixed backend should keep
the ordinary explicit backend = "..." declaration.
No database, SDL, migration-history, backup, operation fingerprint, or stored data migration is required.
Update graphql-orm and graphql-orm-macros together to 0.20.0 at one
reviewed full Git revision. Existing derives and handwritten Entity
implementations require no source changes.
Hosts may add a public entity description and public field descriptions:
#[graphql_entity(
table = "records",
plural = "Records",
description = "Records visible in reviewed application workflows"
)]
struct Record {
#[primary_key]
#[graphql_orm(description = "Stable public record identity")]
id: String,
}Read the resulting GraphqlEntitySemanticMetadata through
Entity::graphql_semantic_metadata. The derive omits private/non-readable
fields and never includes physical column identities or policy keys. This is
descriptive metadata only; consumers must still explicitly select fields,
assign disclosure classification, and perform ordinary authorization.
There is no database, SDL, backup, or stored-data migration. The optional
router-protocol feature now resolves protocol crate 0.2.0, whose wire payload
remains protocol major 1.
Update graphql-orm and graphql-orm-macros together to 0.19.0 at one
reviewed full Git revision. Existing entity declarations using the original
index = "a,b" and single-column relation forms remain source compatible.
Compound managed relations now produce one physical constraint. Declare the ordered Rust source fields and ordered target database columns together:
#[relation(
target = "SnapshotRecord",
from = ["provider", "tenant_key", "generation"],
to = ["provider", "tenant_key", "generation"],
on_delete = "cascade"
)]
snapshot: Option<SnapshotRecord>,Every target member must be present in the same managed schema model and the
ordered tuple must be an exact primary key, composite unique declaration, or
unconditional unique index. Source members are translated through
#[graphql_orm(db_column = "...")]; target members remain database-column
names. A migration that previously passed only the referencing entity may need
to include the referenced repository/schema entity so uniqueness and type
compatibility can be proven.
Named directional ordinary indexes use the nested form:
#[graphql_entity(index(
name = "idx_snapshot_latest",
columns = ["provider", "tenant_key", "generation"],
directions = ["asc", "asc", "desc"]
))]Omitting directions keeps every column ascending. If supplied, its arity must
match columns. Existing string index declarations keep generated names.
Use #[graphql_orm(min_exclusive = 0)] or
#[graphql_orm(max_exclusive = 100)] when an existing check uses strict
comparison. Use #[graphql_orm(default = false)] only to suppress the
conventional implicit default inferred for a created_at or updated_at
field; SQL-expression defaults remain string literals.
ForeignKeyModel now owns column_pairs: Vec<ForeignKeyColumnPairModel> and
an optional observed constraint_name instead of scalar source/target column
fields. Replace single-column struct literals with
ForeignKeyModel::single(...); compound callers construct the ordered pair
list. IndexDef adds column_directions; constructor-based callers need no
change, while direct struct literals must supply &[] for compatibility or an
exact direction slice. These changes are why the aligned packages advance as
a pre-1.0 minor version.
There is no adoption flag or unchecked migration-history bypass. Plan against
the live database normally. When introspected primary keys, unique keys,
foreign-key members/order/target/delete action, ordinary index name/order, and
supported check expressions are semantically equal, the plan is empty and
ordinary apply_migration records the version without DDL. This preserves all
rows and constraints.
The check comparator intentionally accepts only a closed grammar of simple identifiers, literals, comparison operators, parentheses, and commas. It normalizes physical names, whitespace, keyword case, redundant balanced outer parentheses, and quoting of ordinary lowercase portable identifiers; case-sensitive quoted identifiers remain distinct. It does not infer mathematical or type-affinity equivalence. Unsupported, weakened, partial, reordered, or ambiguous contracts remain explicit drift and must not be force-adopted.
Schema hashes now bind ordered compound-FK members and effective index directions while treating physical check/FK names as non-semantic. Refresh any reviewed hash expectations and run an empty replan plus backend integrity checks before removing a legacy migration path. No application row or GraphQL schema migration is otherwise required.
Update graphql-orm and graphql-orm-macros together to 0.18.0 at one
reviewed full Git revision. Generated operation authorization declarations,
the optional router-protocol export, and standard Federation authorization
metadata are additive source and schema capabilities, but generated operation
authorization fingerprints advance to version 2. Rebuild generated code and
review any consumer that persists or compares those fingerprints.
Existing databases, schema modules, RLS policy, and stored application data do not require migration. Router adoption is separately opt-in and requires each host to expose its finished SDL and matching protocol descriptor.
The workspace now pins agql-auth 0.14.0 at exact revision
413fda3435f060604cd653c11e2cc18a668aace1:
agql-auth = { git = "https://github.com/Dastari/agql-auth.git", rev = "413fda3435f060604cd653c11e2cc18a668aace1", version = "0.14.0" }Update any direct host dependency at the same time so Cargo resolves one
package and type universe. The auth-agql bridge continues consuming the
normalized AuthPrincipal::scopes() vector, so it has no Rust API, GraphQL,
database, RLS, or stored-data migration.
The upstream access-token wire default is breaking: newly issued access JWTs
use the OAuth space-delimited scope string instead of the pre-0.14 scopes
array. Upstream validators accept both by default for a bounded transition;
direct JWT decoders must add standard-claim support before issuer cutover.
Purpose tokens retain their separate scopes array. Follow the upstream
0.13-to-0.14 staged migration and wait for the maximum old access-token TTL
plus validation leeway before rejecting legacy claims.
Update graphql-orm and graphql-orm-macros together to 0.17.0 at the final
reviewed full revision. If auth-agql is enabled, match its exact upstream
dependency:
agql-auth = { git = "https://github.com/Dastari/agql-auth.git", rev = "413fda3435f060604cd653c11e2cc18a668aace1", version = "0.14.0" }This release is additive and opt-in. Existing schemas install no assurance enforcement, apply no interactive-mutation default, and retain their current query, mutation, subscription, authorization, RLS, SDL, and database behavior.
let schema = schema_builder(database).finish();First build a compatibility registry and audit the current mutation surface:
let registry = OperationAssuranceRegistry::builder(
graphql_orm_operation_catalog(),
AssuranceSchemaConfig::legacy(),
).build()?;
let audit = registry.audit();Then register custom resolver fields, assign machine/service/safety-teardown
actor classes, and give every exposed mutation either require(...) or
exempt(...). Custom fields use DeclaredAssuranceGuard; generated fields
already call the runtime hook.
Next configure an interactive default while strict mode remains off:
let config = AssuranceSchemaConfig::legacy()
.with_default_interactive_mutation_policy("interactive.recent-auth")?;Install AssuranceEnforcement only after the upstream policy set, injected
clock, accepted request principal, error handling, and step-up UX are ready.
With auth-agql, use AgqlAssuranceEvaluator. Server denials expose lowercase
extension key code with STEP_UP_REQUIRED, UNAUTHENTICATED, or
FORBIDDEN.
Finally enable with_strict_mutation_classification(true) and make
ensure_complete() or audit().assert_complete() a CI gate. Export
manifest() for client codegen and schema_metadata() for directive-aware
schema tooling. Both are descriptive/advisory; keep server enforcement enabled
for every protected operation.
No database, stored-data, GraphQL-data, session, or token migration is needed. Queries and subscriptions never inherit the mutation default. Ordinary resolver auth and authorization continue independently; an assurance exemption does not grant authority. Machine/API-token principals cannot satisfy a user session requirement through the upstream evaluator and must be explicitly classified according to server policy.
To roll back, remove AssuranceEnforcement from schema data and restore
AssuranceSchemaConfig::legacy() (or stop building the registry), then return
both ORM crates to the prior reviewed exact revision. Remove the client
manifest expectation at the same time. Existing database rows and
authentication sessions require no rewrite.
See Operation assurance for the complete API and trust boundaries.
Update both Git-only graphql-orm crates to 0.16.0 at the reviewed final full
revision. Existing entity declarations and schema_roots! blocks require no
source changes. GraphQLOperations now also implements
GraphqlOperationMetadata; schema_roots! adds the
graphql_orm_operation_catalog() helper in the same module as its existing
schema helpers.
Hosts that previously reconstructed resolver names from entity/plural names should instead select the exact generated root coordinate from the catalog:
let operation = graphql_orm_operation_catalog()
.resolve(GraphqlOperationKind::Query, "users")
.expect("reviewed generated operation remains exposed");
let generated_fingerprint = operation.fingerprint();Do not substitute the catalog fingerprint for a complete finished-schema registry fingerprint when a host composes custom roots. Do not treat metadata discovery as registration, enablement, authorization, or disclosure approval. Document operation names, server-authored document hashes, selected result fields, model-facing argument schemas, static disclosure contracts, and current resolver/RLS authorization remain separate host/downstream bindings.
Generated mutation none/allowlist/denylist policy now appears as
is_exposed() == false on the corresponding resolved descriptors; repository
writes and generated subscription behavior are unchanged. A root-level
external_read_only policy also reports derive-generated mutation and
subscription descriptors as unexposed because that root composes neither
operation type. Private typed read projections remain repository-only and do
not add descriptors or change generated GraphQL operation fingerprints. List
categories describe connection shape rather than a fixed runtime bound; retain
explicit host/tool output limits because PaginationConfig remains
configurable.
This is an additive pre-1.0 public API/generated-code release. Existing GraphQL SDL, resolver execution, database schema, stored data, migrations, backups, authorization, and RLS behavior do not change. No database or data migration is required. Refresh lockfiles, update both aligned versions, and rerun backend, naming, schema exposure, and consumer fingerprint gates.
Update both Git-only graphql-orm crates to 0.15.0 at the final reviewed full
revision. The release corrects generated single/composite bounded update and
delete plus retention purge when MutationLimit is 100 or greater. Hosts do
not need to change existing calls: the generated implementation now obtains
the exact maximum + 1 sentinel without applying the public 100-row page cap.
Public GraphQL, connection, repository, runtime-query, and PageInput limits
are unchanged. No uncapped public read surface is introduced. Residual or
in-memory filters on bounded mutations now return stable INVALID_INPUT
before mutation; replace such filters with a completely database-rendered
predicate. Database-renderable filters preserve exact all-or-nothing results,
and any selected-versus-affected cardinality change fails the transaction.
No database schema, stored data, GraphQL SDL, entity declaration, or backend
migration is required. Refresh lockfiles and rerun high-ceiling overflow,
authorization/RLS, event, rollback, and restart checks. The optional agql-auth
bridge remains released 0.12.0 at exact revision
3f3b0c5365adfbe436514a681d977b600991b797; matching direct host dependencies
must keep that exact version and full revision so one type universe resolves.
Update both Git-only graphql-orm crates to 0.14.0 at the final reviewed full revision. When the host also depends on agql-auth, its dependency must match the bridge exactly:
agql-auth = { git = "https://github.com/Dastari/agql-auth.git", rev = "3f3b0c5365adfbe436514a681d977b600991b797", version = "0.12.0" }This yields one agql-auth package/type universe. Do not use a branch, local path override, abbreviated revision, or different version requirement.
The public converter functions and exact identity/authorization-context
mappings remain available. The bridge now omits malformed or structurally
inconsistent session assurance, and it copies only the documented string
policy_version from AccessTokenMetadata.additional. Hosts that read other
custom values from AuthSubject.claims.additional must move those values into
an explicit application-owned request context. This observable narrowing makes
the release 0.14.0; macro syntax/output is unchanged, but the companion version
advances under the aligned release policy.
Direct agql-auth users must separately follow its 0.10→0.12 migration. The 0.11
AuthRateLimitStore contract is atomic and versioned; graphql-orm supplies no
implementation and requires no ORM schema migration. The 0.12 typed
EssentialAcrs request and matched_acrs outcome remain provider evidence.
They do not enter the bridge or imply local MFA. Only a host-constructed,
session-bound, structurally consistent SessionAssurance can populate
AuthAssurance/DbAuthContext.assurance; the exact MfaAcceptance decision is
retained. A mapped microsoft-entra/acrs/c1 context remains distinct from
standard scalar acr, AMR, roles, scopes, tenant, and policy_version.
No database schema, stored data, GraphQL SDL, generated code, resolver naming,
or backend migration is required. Refresh lockfiles and inspect cargo tree for
one agql-auth revision before running the host's authorization and live restart
gates; compilation alone is not endpoint approval.
This Git-only 0.13.0 release contains two coordinated prompts: validated runtime relation batching and PostgreSQL constraint-index upgrade idempotency. Update both aligned crates to the final reviewed full revision.
Existing static entities, GraphQL SDL, generated repositories/relations,
transactions, authorization/RLS, top-level gormrq1 and legacy cursors,
RuntimeRecord, serialized RuntimeSchema, third-party backend traits, and
stored rows require no source or data migration. The new gormrr1 relation
cursor is accepted only by RuntimeRelationBatchRequest and must not be
translated to or from another cursor family. Hosts opting into runtime
relations replace host SQL/N+1 loaders with an anchored parent read followed by
explicit bounded relation layers. Request next-layer relation keys with
runtime_relation_batch_request_with_relation_keys, then pass the opaque
anchors returned by RuntimeRelationBatch::relation_parents into the next
batch request; see
Validated runtime relation batching.
PostgreSQL operators should replan a complete generated target before rollout.
Constraint-owned PRIMARY KEY/UNIQUE backing indexes are now classified as
constraints, not secondary indexes, so an unchanged schema or table-additive
upgrade produces no DropIndex for them. Composite UNIQUE metadata now renders
as UNIQUE (a, b) during new table creation and introspects in key order. No
existing constraint or index is rewritten merely by upgrading the library. If
an older managed table was created without a declared composite UNIQUE because
of the former CREATE TABLE omission, the live/target mismatch is real and
requires an explicit reviewed migration; do not mark an old module version as
newly applied.
MSSQL static reads remain compatible and runtime relation execution returns
unsupported_backend before I/O. No macros or entity declarations changed;
the macros version advances only under the repository's aligned Git-only
release policy.
Update both Git-only crates to 0.12.0 at the reviewed full Git revision. This is an additive pre-1.0 public runtime API release; macro declaration/generated behavior is unchanged, but the aligned companion version advances under the repository release policy.
Existing static entities, repository/GraphQL reads, mutations, transactions,
authorization, RLS, keyset cursors, third-party OrmBackend implementations,
database schemas, and serialized RuntimeSchema documents need no source,
cursor, schema, or data migration. The new gormrq1 cursor is used only by
RuntimeReadRequest and is intentionally distinct from static/legacy cursors.
Do not translate or accept offset cursors on this boundary.
Runtime-schema hosts may replace their own SQL/filter/order/row-decoding layer
with the validated constructors on ValidatedRuntimeSchema and
Database::execute_runtime_read. Re-resolve handles after schema activation;
old fingerprints fail closed. Hosts must compile authorization constraints to
a second RuntimePredicate and structurally combine it with the application
predicate. See Runtime queries.
No migration is required. MSSQL continues to compile and retain static reads, but returns the stable unsupported capability for runtime execution.
Update both Git-only crates to 0.11.0 at the final reviewed full Git revision.
This is an additive pre-1.0 minor release: it introduces the public
RepositoryEntity derive, #[repository_entity(...)] declaration attribute,
RepositoryQuery, repository field-policy callbacks, and generated ordinary
Rust DTO/input types. Existing GraphQLEntity, GraphQLSchemaEntity,
GraphQLOperations, schema roots, SDL, and stored schemas remain compatible.
To move a persisted entity out of GraphQL entirely, replace its GraphQL derives
and #[graphql_entity(...)] with:
#[derive(RepositoryEntity, Clone, serde::Serialize, serde::Deserialize)]
#[repository_entity(
backend = "sqlite",
table = "credentials",
plural = "Credentials",
default_sort = "username ASC"
)]
struct Credential { /* unchanged persisted fields */ }Remove it from schema_roots!; attempting to register it is now an intentional
compile error. Replace the legacy pool-bound list builder with
Credential::query(&database). Primary/unique lookups, writes, projections,
and MutationContext calls retain their generated names. Repository-only
create/update types include writable private fields, so host duplicate DTOs are
not needed.
Existing FieldPolicy implementations compile unchanged. Fields with no
declared key keep the existing repository decision; a field carrying
read_policy/write_policy is denied by the new default repository callbacks
until the provider implements can_read_repository_field and/or
can_write_repository_field. This deliberate fail-closed behavior prevents a
missing GraphQL Context from becoming authority.
Changing only the generation surface does not alter SchemaModel, migration
plans, schema/module fingerprints, backup descriptors, tables, indexes,
constraints, RLS, or data. No data or schema migration is required. Sensitive
repository mutation-hook snapshots/events are more restrictive by design: hook
state is redacted and cannot be downcast to the original entity, and change
events omit the entity payload when the declaration has a sensitive field.
Before-write input hooks remain typed and can deliberately transform the value.
See Repository-only entities.
Update both Git-only crates to 0.10.0 at the reviewed full Git revision. Repository release policy keeps companion versions aligned for a public runtime API release; declaration syntax and generated code are unchanged.
This is an additive pre-1.0 runtime API release. Existing derived entities,
repositories, GraphQL SDL, database schemas, migration history, backups,
authorization, and static backend behavior require no source or data
migration. Existing OrmBackend implementations remain source-compatible;
the new RuntimeRowDecoder capability is separate and defaults to a
fail-closed unsupported result.
Runtime-schema hosts may replace host-owned value/row-decoding models with
RuntimeValue, RuntimeRecord, and handles resolved from their existing
ValidatedRuntimeSchema. Resolve fresh handles after catalog/schema
activation: owned handles and records are bound to the creating schema
fingerprint, and mixing generations returns schema_mismatch. SQLite UUID,
JSON, and datetime fields must use the documented text representation;
PostgreSQL uses native UUID/JSON(B)/TIMESTAMPTZ types. Datetimes canonicalize to
UTC at PostgreSQL-compatible rounded microsecond precision.
No database migration is required. MSSQL runtime-row decoding is deliberately unsupported in this slice; its static generated reads are unchanged. See Runtime values, records, handles, and row decoding.
This release adds public runtime and generated surfaces and extends public schema/backup descriptors, so it is a pre-1.0 minor release rather than a patch. Update both crates to 0.9.0 at the final reviewed release revision.
Derive-generated entities need no source change unless they opt in. Code that constructs public descriptors manually must add disabled retention state:
RuntimeCollection { retention_purge: false, .. };TableModel { retention_purge: false, .. };EntityBackupDescriptor { retention_purge: false, .. }(older serialized descriptors default this field tofalse);EntityMetadataliterals addretention_policy: None. ExistingEntityMetadata::from_schemacalls remain source-compatible; only code that deliberately supplies a retention key usesfrom_schema_with_retention; and- exhaustive matches on
EntityAccessSurface,RuntimeSchemaDiagnosticCode, orMigrationStep::SetAppendOnlymust handle the new retention variant/field.
Low-level backend capability traits gained safe default methods that reject
retention maintenance, so out-of-tree implementations remain source-compatible
unless they deliberately opt into this contract. SQLite and PostgreSQL provide
the supported implementations. Existing format-v1 owned runtime-schema JSON
without retention_purge continues to deserialize with the capability
disabled.
Existing append-only entities and fingerprints remain unchanged. To opt in,
add a dedicated policy key such as
retention_purge = "audit.retention.purge", register an EntityPolicy that
allows only EntityAccessSurface::RetentionMaintenance, and replace raw purge
SQL with Database::retention_transaction[_with_auth] plus
RetentionContext::purge. Keep ordinary write policy keys separate.
Enabling or disabling retention changes managed enforcement and requires a new
host/module migration version. SQLite adds the reserved, structurally validated
__graphql_orm_retention_context table and replaces the DELETE trigger.
PostgreSQL replaces the append-only function contract and, when managed RLS is
enabled, adds the transaction-local retention DELETE policy. There is no row
data rewrite. Validate foreign-key behavior and the intended cutoff before
enabling physical deletion. See
Bounded append-only retention maintenance.
Update both graphql-orm and graphql-orm-macros to 0.8.0 at the same reviewed
Git revision. This is a breaking pre-1.0 release, not an additive one, in
two respects:
1. Public metadata struct fields. ColumnDef and FieldMetadata gained
api_name: &'static str, is_sortable: bool, and is_date_time: bool. Code
built through the derives or the ColumnDef const builders needs no changes.
Code constructing either struct as a literal must add the new fields
(api_name defaults to the column name through ColumnDef::new; both flags
default to false), or switch to the builders: .api_name(...),
.sortable(), .date_time().
2. Nullable-byte logical identity. Option<Vec<u8>> fields previously
reported logical type Json while storing BYTEA/BLOB; they now correctly
report Bytes. Column DDL, stored data, row decoding, and generated GraphQL
are unchanged. For entities with nullable byte columns:
stable_schema_hashand schema-module fingerprints that include such an entity change. Bump the semantic version of anyOrmSchemaModulewhose fingerprint covers one, and regenerate recorded fingerprints.- Backups taken before 0.8.0 that contain such an entity will fail 0.8.0 hash-compatibility verification. Take fresh backups after upgrading. Keep the old archives: they remain readable by pre-0.8.0 binaries, and their bytes are not corrupted — only the recorded schema identity differs. Do not overwrite or prune them until a post-upgrade backup has been verified.
- Logical backups written by 0.8.0 record byte columns as
Bytes(their values were already binary).
Entities without nullable byte columns keep their existing hashes, fingerprints, and backup compatibility; no database or data migration is required for anyone.
The new runtime_schema module is additive API surface. Runtime query
execution, migration planning from the IR, and dynamic GraphQL registration
are not part of this release.
Update graphql-orm to 0.7.1 at the reviewed full Git revision. The companion
graphql-orm-macros crate remains 0.7.0. Existing feature declarations do not
change:
graphql-orm = { git = "https://github.com/Dastari/graphql-orm.git", rev = "<reviewed-full-40-character-commit-sha>", version = "0.7.1", default-features = false, features = ["sqlite"] }Cargo now resolves only the selected backend driver. There is no Rust API, generated-code, GraphQL SDL, schema, configuration, or database migration. Remove any downstream workaround that patched SQLx features, refresh the lock file, and confirm the selected graph with the commands in Testing and verification.
Update both graphql-orm and graphql-orm-macros to 0.7.0 at the same reviewed
Git revision. If auth-agql is enabled or the host directly uses agql-auth,
align it to version 0.10.0 at revision
c92dcb441237bbe308499b26525945f60ffa394a so Cargo resolves one public type
universe.
Existing entities, generated GraphQL SDL, mutations, offset connections, and stored cursors remain valid. No automatic database or data migration is required.
The ORM bridge API and mapped principal/session-assurance data are unchanged.
Hosts that directly use agql-auth OIDC state and opt into 0.10 bound
reauthentication must follow its 0.10 migration guide: persisted
OAuthLoginState records gain an optional authorization-policy value, and a
decomposed relational store needs a nullable column before enabling the new
writer. Hosts that only consume the ORM principal bridge need no data
migration.
Dependency crates that own private tables may implement OrmSchemaModule and
compose a SchemaModuleCatalog. Applying the resulting schema target remains a
host-controlled migration operation and must use a fresh host migration/module
version whenever an owned entity, index, constraint, or persistent semantic
changes. Backup and restore code should persist SchemaModulesSnapshot and run
the declared restore phases through the owning dependency.
FencedLeaseState is a backend-neutral transition contract, not a replacement
for an atomic database predicate. Claims, heartbeats, child writes, and release
must compare resource, owner, attempt, fencing token, unexpired deadline, and
CAS row version in the same persistence operation.
Entities with configured keyset ordering gain the additive repository method
keyset_connection_page. Use first with optional after for forward reads,
or last with optional before for backward/tail reads. Limits remain bounded
by the database pagination configuration. Existing generated GraphQL keyset and
offset fields are unchanged.
Update the runtime to 0.6.3 and the companion macros crate to 0.6.1 at the same reviewed Git
revision. No Rust call-site or database migration is required: QueryRoot, MutationRoot, and
SubscriptionRoot remain the generated Rust names.
The generated GraphQL object names change to Query, Mutation, and Subscription. This makes
federation SDL valid when the exporter relies on GraphQL's conventional implicit operation roots.
If schema tooling explicitly matched the old GraphQL type names, update those matches. Empty
mutation and subscription roots remain absent rather than becoming fieldless objects.
Regenerate provider SDL, parse or validate it, and run the federation composition check described in Federation operation roots before promotion.
Update graphql-orm to the reviewed v0.6.2 commit and align any direct agql-auth dependency to
version 0.8.1 at exact revision f1fb5fe8c42d29806821d5f1a9032b007dee63e4. This ensures Cargo
resolves one agql-auth package and one set of public types. No bridge API, authorization behavior,
database migration, or generated-code change is required. The companion macros crate remains
0.6.0.
Pin both crates to the reviewed v0.6.0 commit. This release keeps existing single-key and 0.5
projection APIs, but adding assurance/organization/correlation fields to the public AuthSubject
and DbAuthContext structs is a source change for direct struct literals. Prefer
AuthSubject::builder and DbAuthContext { ..Default::default() }.
The optional auth-agql bridge now requires Git-only agql-auth 0.8.0 revision
be4e0a213ce9c9b9fbe9fe985602743a584e019b. It retains session assurance and safe policy/audit
metadata. Remove any direct 0.7 pin so Cargo resolves exactly one agql-auth version.
Natural composite-key writes are opt-in:
#[graphql_entity(
repository_mutations = true,
upsert = "tenant_id,natural_id",
unique_composite = "tenant_id,natural_id"
)]Mark every key field #[primary_key] and host-assigned with
#[graphql_orm(auto_generated = false)]. Use the generated EntityKey, CreateEntityInput, and
UpdateEntityInput with find_by_key, insert, insert_if_absent, upsert, update_by_key,
delete_by_key, update_if, and bounded typed filter mutations. These APIs add no GraphQL mutation
fields. See Typed Composite-Key and Bounded Mutations.
MutationLimit::new is required by bounded operations; an overflow returns LimitExceeded without
changing rows. Legacy update_where/delete_where remain available for source compatibility and
should be migrated when the caller needs a reviewable hard ceiling.
This additive release is source-compatible with 0.4.3. Pin both crates to the reviewed v0.5.0
commit. Projection declarations change generated Rust APIs only and require no database migration.
Add #[graphql_orm(projection(name = "...", fields = [id, field_name], private = true))] to a
GraphQLEntity, then replace least-privilege full-entity reads with the generated projection's
repository or transaction methods. Mark secrets #[graphql_orm(private, sensitive)]; existing
#[backup(redact)] also drives projection Debug redaction.
If the database registers an application RowPolicy, projection reads now return a fail-closed
error rather than fetching a full entity to evaluate it. Move projection-compatible tenant or
soft-delete enforcement to generated typed filters or PostgreSQL RLS before migrating that caller.
No GraphQL query or DTO is added. See Typed Read Projections.
This compatible patch needs no application API change. Pin both crates to the reviewed v0.4.3
commit and run managed validation with a new migration version.
Conditional indexes created by graphql-orm remain restart-idempotent. A same-name live index is now
accepted only when the entire stored predicate parses as the supported field IN (closed set) form
or PostgreSQL's equivalent field = ANY (ARRAY[...]) representation. Extra boolean expressions,
comments that SQLite persists, functions, and unsupported casts are drift. PostgreSQL discards SQL
comments when storing index expressions, so comment-only spelling has no persistent structural
meaning on that backend.
Append-only validation now checks complete SQLite trigger definitions and PostgreSQL trigger and function catalog contracts, including unconditional enforcement and privilege/search-path posture. If an older deployment contains a recognizable managed name with hand-edited SQL, planning will produce repair work. Reusing an already recorded migration version then fails closed; review and apply that work under a fresh version with the schema-owner migration role.
This compatible patch needs no application API change. Move both crates to the reviewed v0.4.2
commit before adopting a database created by an older migration helper.
At managed-schema preparation, a history table containing exactly version as a non-null textual
sole primary key and a non-null textual/timestamp applied_at is upgraded in one transaction.
Every existing version and timestamp is preserved. Missing descriptions are set to
Legacy migration <version>, and missing current metadata columns are added as nullable text. No
historical migration is re-executed. Repeated preparation is idempotent.
SQLite rebuilds the recognized legacy table to install the complete current schema while preserving
rows verbatim. PostgreSQL requires applied_at TIMESTAMPTZ NOT NULL; arbitrary legacy text is not
converted because doing so could change timestamp meaning. PostgreSQL restores the
CURRENT_TIMESTAMP default for future rows without changing existing values. Unknown columns,
incorrect types or nullability, an empty version, or any other primary-key identity are rejected.
Recorded-version reuse and remaining-plan drift checks still run after adoption and still fail
closed.
Back up the database before first preparation. If a legacy table is rejected, inspect and migrate it explicitly rather than renaming columns until it happens to pass validation.
This is a compatible Git-pin update. Move both crates to the reviewed v0.4.1 commit.
- Binary
Vec<u8>keys require no host encoding. Mark host-assigned keys#[graphql_orm(auto_generated = false)]; useprivate,skip_input, or#[graphql(skip)]when they must not appear in public GraphQL inputs. Addmin_lengthandmax_lengthbefore migration when fixed digest width is an invariant. - Existing repository upsert entities with hidden conflict targets now compile. Their repository and transaction helpers remain available, while the unsafe GraphQL upsert field is absent.
- Before adding a unique conditional index, validate that rows inside the selected predicate set contain no duplicate indexed keys. Apply the generated create-index plan under a new migration version.
- Adding
gt_field,gte_field,lte_field, orlt_fieldcreates managed checks. SQL check predicates evaluate to UNKNOWN when either nullable operand is NULL, so NULL rows pass unless a separate non-null constraint applies.
This is an additive migration for existing entities. Upgrade both crates together to 0.4.0.
- Replace host-owned pool transactions with
Database::transaction; useStateMachinefor security-sensitive read/decide/write flows and retry the whole callback when classified retryable. - Add
#[graphql_orm(version, default = "0")]to ani64field, apply the planned column migration, then move guarded updates tocompare_and_swap. - Add
append_only = trueonly after removing update/delete/upsert callers. Review and apply the trigger plan with the schema-owner migration role; remove UPDATE/DELETE grants from ordinary PostgreSQL roles as defense in depth. - Add portable constraint attributes, validate existing data, and apply the planned SQLite table rebuild or PostgreSQL named checks. New checks may reject historical invalid rows during rebuild.
- Add a deterministic
keyset = "..., id asc"order and migrate clients to the generated keyset field. Discard legacy numeric cursors; they intentionally fail strict keyset decoding.
Managed startup should validate after every step. Missing append-only triggers or named checks are schema drift and must not be ignored or repaired with a reused migration version.
Release as 0.3.0 (minor with documented breaking security defaults for pagination
and public error messages). A future major can flip AuthorizationMode default to
DeclaredPoliciesRequired.
// Before (implicit fail-open when no policy provider)
let database = Database::new(pool);
// After (recommended production setting)
let database = Database::new(pool)
.with_authorization_mode(AuthorizationMode::DeclaredPoliciesRequired);
database.set_entity_policy(MyEntityPolicy);| Mode | Current default | Secure recommended | Future default |
|---|---|---|---|
LegacyPermissive |
yes | no | removed as default |
DeclaredPoliciesRequired |
no | yes | planned default |
ExplicitPolicyForAllExposedOperations |
no | for high-assurance APIs | optional |
// Before
AuthSubject::from_parts(id, roles, scopes, tenant_id)
// After (compatible) — same helper still works
// Prefer builder for new fields:
AuthSubject::builder(id)
.user_id(user_id)
.roles(roles)
.scopes(scopes)
.tenant_id(tenant)
.token_id(jti)
.session_id(session)
.actor_id(actor)
.build()Debug no longer prints claim JSON bodies.
// Before
Err(async_graphql::Error::new(error.to_string()))
// After
Err(OrmPublicError::from_sqlx(&error).into_graphql_error())Missing auth messages changed from "missing auth" to "unauthenticated" with
extensions.code = "UNAUTHENTICATED".
// Restore 0.2.x limits
Database::new(pool).with_pagination_config(PaginationConfig::legacy())Default limit: 1000 → 50. Max limit: 1000 → 100.
graphql-orm = { git = "https://github.com/Dastari/graphql-orm.git", rev = "<reviewed-full-40-character-commit-sha>", version = "0.6.0", features = ["sqlite", "auth-agql"] }The optional feature depends on upstream agql-auth 0.8.0 via git revision
be4e0a213ce9c9b9fbe9fe985602743a584e019b (tag v0.8.0). It does not use a
local path, sibling checkout, or Cargo [patch].
use graphql_orm::graphql::auth_agql::auth_bundle_from_principal;
let (subject, db_auth) = auth_bundle_from_principal(&principal);let resolution = resolve_structural_auth(
StructuralAuthMetadata::new(Some("tenant_id"), None, StructuralAuthorization::Required),
&StructuralAuthValues::from_subject(&subject),
);Macro-generated wiring of structural predicates on every operation path remains a follow-up; helpers are available for host and incremental macro integration.
Prefer FilterExpression::trusted_fragment(clause, values) for host-authored
predicates. FilterExpression::Raw remains for generated compatibility and is
documented as a trusted surface.
No application migration is required. After upgrading, reopening a file-backed
SQLite database and replanning the same managed schema should produce an empty
plan even when live PRAGMA table_info returns unixepoch() while generated
metadata previously declared (unixepoch()).
ApplyOptions::additive_only behavior is unchanged for real non-additive steps
such as altering a default from unixepoch() to date('now').
SchemaManager::apply_migration is idempotent only when:
- the version is already present in
__graphql_orm_migrations; and - the plan has no remaining steps or statements.
Restart code that re-plans and re-applies the same version when the schema is
already current receives
AppliedMigrationReport { already_applied: true, statements_applied: 0, .. }.
If the version is recorded but the plan still has work, apply fails with an explicit protocol error. That is intentional: it surfaces schema drift or unsafe reuse of a migration version rather than silently treating the plan as done.
For apply_schema_target, “remaining work” includes nested migration
steps/statements, RLS statements, and the combined executable plan.statements.
An empty nested table migration with remaining RLS work is not a no-op.
Callers that pattern-match AppliedMigrationReport must accept the new
already_applied field.
AuthExt::auth_user() is deprecated but still available. Migrate call sites based on what they need:
// Before
let user_id = ctx.auth_user()?;
// After: id only
let user_id = ctx.auth_user_id()?;
// After: id, roles, scopes, tenant id
let subject = ctx.auth_subject()?;Applications can keep injecting the legacy String user id while migrating. graphql-orm upgrades it
to AuthSubject { id, roles: [], scopes: [], tenant_id: None, ... }. New code should inject
AuthSubject directly.
If a downstream crate implemented AuthExt itself, add implementations for auth_user_id,
auth_subject, and auth_subject_opt. Most applications only use the built-in implementation for
async_graphql::Context<'_> and do not need to change anything beyond call-site names.
let request = request.data(AuthSubject {
id: user.id.to_string(),
user_id: None,
roles: user.roles.clone(),
scopes: user.scopes.clone(),
tenant_id: user.tenant_id.clone(),
claims: None,
token_id: None,
session_id: None,
actor_id: None,
});DbAuthContext::from_subject(&subject) can mirror the same principal into PostgreSQL RLS settings:
let request = request
.data(subject.clone())
.data(DbAuthContext::from_subject(&subject));Generated resolvers keep the previous fail-closed default: if no auth setting is present, they
require an auth subject before database access. This preserves the old generated ctx.auth_user()?
gate.
Use auth = "none" for public generated resolvers:
#[graphql_entity(table = "pages", plural = "Pages", auth = "none")]
pub struct Page {
// fields...
}Use auth = "optional" when a schema should read a subject if present but leave allow/deny decisions
to EntityPolicy, RowPolicy, or FieldPolicy:
schema_roots! {
auth: "optional",
query_custom_ops: [],
entities: [Record],
}Use auth = "required" explicitly for new private schemas or entities:
schema_roots! {
auth: "required",
query_custom_ops: [],
entities: [Ticket, Session],
}Entity-level auth overrides the schema-root mode.
ScopeEntityPolicy is exact-match only:
let mut database = Database::new(pool);
database.set_entity_policy(ScopeEntityPolicy::new(
&["tickets.read"],
&["tickets.write"],
));require_auth: true returns an unauthenticated GraphQL error when no subject exists. A subject that
lacks the required exact scope returns Ok(false) from the policy and is denied by the generated
access path.
- No JWT, OIDC, cookie, wildcard, or application-specific scope logic was added to
graphql-orm. - PostgreSQL RLS helper functions still use exact scope matching.
- The current
auth-agqlfeature targetsagql-auth0.16.0 at revision3bc38cd94794f1e868a9cc3a5551047b95a32105; earlier release sections above retain their historical pins.