pub struct FieldDefinition {Show 15 fields
pub name: FieldName,
pub field_type: FieldType,
pub nullable: bool,
pub description: Option<String>,
pub default_value: Option<GraphQLValue>,
pub vector_config: Option<VectorConfig>,
pub vector_distance: Option<String>,
pub alias: Option<String>,
pub deprecation: Option<DeprecationInfo>,
pub requires_scope: Option<String>,
pub on_deny: FieldDenyPolicy,
pub authorize: bool,
pub encryption: Option<FieldEncryptionConfig>,
pub hierarchy: Option<String>,
pub unique: bool,
}Expand description
A field within a GraphQL type.
This represents a single field definition after compilation from authoring-language decorators. All data is Rust-owned.
§JSONB Architecture Note
FraiseQL stores all field data in a JSONB column (typically data).
The name field corresponds to the key in the JSONB object.
SQL columns are only used for WHERE clause filtering, not data retrieval.
§Example
use fraiseql_core::schema::{FieldDefinition, FieldDenyPolicy, FieldType};
let field = FieldDefinition {
name: "email".into(),
field_type: FieldType::String,
nullable: true,
description: Some("User's email address".to_string()),
default_value: None,
vector_config: None,
vector_distance: None,
alias: None,
deprecation: None,
requires_scope: None,
on_deny: FieldDenyPolicy::default(),
authorize: false,
encryption: None,
hierarchy: None,
unique: false,
};Fields§
§name: FieldNameField name - the key in the JSONB data column (e.g., “email”).
field_type: FieldTypeField type.
nullable: boolIs this field nullable?
description: Option<String>Optional description (from docstring).
default_value: Option<GraphQLValue>Default value.
vector_config: Option<VectorConfig>Vector configuration (for pgvector fields).
Only present when field_type is Vector or BitVector.
vector_distance: Option<String>Names the vector field whose search distance this field carries (#959).
A Float field declaring vector_distance = "embedding" is projected
from the distance expression a nearest search on embedding orders
by — the same expression, so the number and the order agree by
construction. It is not a stored key: selecting it on a query that
did not search that field is refused rather than answered with null,
because “no distance” and “distance zero” are not the same claim.
alias: Option<String>GraphQL alias for this field (output key name in response).
When set, the field value from JSONB key name is output under this alias.
Example: { writer: author { name } } - reads JSONB key “author”, outputs as “writer”
deprecation: Option<DeprecationInfo>Deprecation information (from @deprecated directive). When set, the field is marked as deprecated in the schema.
requires_scope: Option<String>Scope required to access this field (field-level access control).
When set, users must have this scope in their JWT to query this field.
The runtime FieldFilter validates these requirements.
§Example
use fraiseql_core::schema::{FieldDefinition, FieldDenyPolicy, FieldType};
let field = FieldDefinition {
name: "salary".into(),
field_type: FieldType::Int,
nullable: false,
description: None,
default_value: None,
vector_config: None,
vector_distance: None,
alias: None,
deprecation: None,
requires_scope: Some("read:Employee.salary".to_string()),
on_deny: FieldDenyPolicy::Reject,
authorize: false,
encryption: None,
hierarchy: None,
unique: false,
};on_deny: FieldDenyPolicyPolicy when a user lacks the required scope for this field.
Reject(default): the entire query fails with aFORBIDDENerror.Mask: the query succeeds but this field returnsnull.
Whether this field is gated by the dynamic
FieldAuthorizer.
When true and a FieldAuthorizer is configured on the runtime, the
authorizer is consulted per row for this field (composing as a logical AND
with any static requires_scope). The decision can vary by principal, parent
row, and field arguments — expressing rules requires_scope cannot.
Defaults to false (no dynamic gate). false is not serialized, so compiled
schemas that predate this field deserialize unchanged.
encryption: Option<FieldEncryptionConfig>Encryption configuration for this field.
Not supported in this release. End-to-end field-level at-rest encryption is not implemented: the mutation/write path does not encrypt field values, so a field marked for encryption would be stored in plaintext and then fail to decrypt on read. To avoid silently storing sensitive data unencrypted, the server refuses to start when any field declares this. The field is retained so existing compiled schemas still deserialize and produce that boot-time error rather than a silent plaintext write.
hierarchy: Option<String>Named hierarchy reference for ID-based ltree operators.
When set, this field can use descendantOfId / ancestorOfId filter
operators. The value references a key in the hierarchies config map,
which provides the table and ltree path column for subquery generation.
unique: boolWhether this field’s column is unique over the type’s relation.
A compiled fact, not a runtime observation. It exists so a to-one embed
(ManyToOne/OneToOne) can be refused at load when its join key does not
identify at most one target row — see
relationship_violations.
Without it the REST embed resolves the target with LIMIT max_page_size and
takes the first row returned, calling an arbitrary one “the” object.
Read FieldDefinition::is_unique_key rather than this flag directly: the
entity identity (id: ID) is unique under ADR-0017 and does not carry it.
Nothing in the catalog reports uniqueness for a view, which is what a
FraiseQL type usually is — so where the relation is a view this is the author’s
assertion, taken on the same trust as
PaginationOrder::Column, whose
ordering key has the identical problem and the identical resolution. A
--database compile can introspect it for a table (pg_index.indisunique);
that path is not yet wired.
Defaults to false and is not serialized when false, so compiled schemas that
predate it deserialize unchanged.
Implementations§
Source§impl FieldDefinition
impl FieldDefinition
Sourcepub fn new(name: impl Into<String>, field_type: FieldType) -> Self
pub fn new(name: impl Into<String>, field_type: FieldType) -> Self
Create a new required field.
Sourcepub fn nullable(name: impl Into<String>, field_type: FieldType) -> Self
pub fn nullable(name: impl Into<String>, field_type: FieldType) -> Self
Create a new nullable field.
Sourcepub fn vector(name: impl Into<String>, config: VectorConfig) -> Self
pub fn vector(name: impl Into<String>, config: VectorConfig) -> Self
Create a new vector field.
§Example
use fraiseql_core::schema::{FieldDefinition, VectorConfig};
let embedding = FieldDefinition::vector("embedding", VectorConfig::openai());Sourcepub fn bit_vector(name: impl Into<String>, config: VectorConfig) -> Self
pub fn bit_vector(name: impl Into<String>, config: VectorConfig) -> Self
Create a new binary (bit) vector field (#959).
§Example
use fraiseql_core::schema::{FieldDefinition, VectorConfig};
let fingerprint = FieldDefinition::bit_vector("fingerprint", VectorConfig::binary(768));Sourcepub fn with_requires_scope(self, scope: impl Into<String>) -> Self
pub fn with_requires_scope(self, scope: impl Into<String>) -> Self
Add a scope requirement to the field (field-level access control).
§Example
use fraiseql_core::schema::{FieldDefinition, FieldType};
let salary = FieldDefinition::new("salary", FieldType::Int)
.with_requires_scope("read:Employee.salary");Sourcepub const fn with_on_deny(self, policy: FieldDenyPolicy) -> Self
pub const fn with_on_deny(self, policy: FieldDenyPolicy) -> Self
Set the deny policy for when a user lacks the required scope.
Mark this field as gated by the dynamic
FieldAuthorizer.
When true and a FieldAuthorizer is configured on the runtime, the
authorizer is consulted per row for this field.
§Example
use fraiseql_core::schema::{FieldDefinition, FieldType};
let email = FieldDefinition::new("email", FieldType::String).with_authorize(true);
assert!(email.authorize);Sourcepub fn with_description(self, desc: impl Into<String>) -> Self
pub fn with_description(self, desc: impl Into<String>) -> Self
Add description to field.
Sourcepub fn with_default(self, value: GraphQLValue) -> Self
pub fn with_default(self, value: GraphQLValue) -> Self
Add default value to field.
Sourcepub const fn with_vector_config(self, config: VectorConfig) -> Self
pub const fn with_vector_config(self, config: VectorConfig) -> Self
Add vector configuration to field.
Sourcepub fn with_vector_distance(self, vector_field: impl Into<String>) -> Self
pub fn with_vector_distance(self, vector_field: impl Into<String>) -> Self
Declare this field as the search distance to a named vector field (#959).
§Example
use fraiseql_core::schema::{FieldDefinition, FieldType};
let similarity =
FieldDefinition::new("similarity", FieldType::Float).with_vector_distance("embedding");
assert_eq!(similarity.vector_distance.as_deref(), Some("embedding"));Sourcepub fn with_alias(self, alias: impl Into<String>) -> Self
pub fn with_alias(self, alias: impl Into<String>) -> Self
Set a GraphQL alias for this field (output key name in response).
The alias determines the key name in the JSON response, while name
remains the JSONB key where data is read from.
§Example
use fraiseql_core::schema::{FieldDefinition, FieldType};
// JSONB key "author" will be output as "writer" in the response
let field = FieldDefinition::new("author", FieldType::Object("User".to_string()))
.with_alias("writer");
assert_eq!(field.output_name(), "writer");
assert_eq!(field.name, "author"); // JSONB key unchangedSourcepub fn output_name(&self) -> &str
pub fn output_name(&self) -> &str
Get the output name for this field (alias if set, otherwise name).
This is the key name that appears in the GraphQL JSON response.
Sourcepub fn jsonb_key(&self) -> &str
pub fn jsonb_key(&self) -> &str
Get the JSONB key name for this field.
This is always name, regardless of alias. Used for:
- Reading data from JSONB column
- Building WHERE clause paths
Sourcepub const fn is_bit_vector(&self) -> bool
pub const fn is_bit_vector(&self) -> bool
Check if this is a binary (bit) vector field (#959).
Sourcepub fn deprecated(self, reason: Option<String>) -> Self
pub fn deprecated(self, reason: Option<String>) -> Self
Mark this field as deprecated.
§Example
use fraiseql_core::schema::{FieldDefinition, FieldType};
let field = FieldDefinition::new("oldId", FieldType::Int)
.deprecated(Some("Use 'id' instead".to_string()));
assert!(field.is_deprecated());Sourcepub const fn is_deprecated(&self) -> bool
pub const fn is_deprecated(&self) -> bool
Check if this field is deprecated.
Sourcepub fn deprecation_reason(&self) -> Option<&str>
pub fn deprecation_reason(&self) -> Option<&str>
Get the deprecation reason if deprecated.
Sourcepub fn with_encryption(self, config: FieldEncryptionConfig) -> Self
pub fn with_encryption(self, config: FieldEncryptionConfig) -> Self
Add encryption configuration to this field.
Sourcepub const fn is_encrypted(&self) -> bool
pub const fn is_encrypted(&self) -> bool
Check if this field is encrypted.
Sourcepub fn is_primary_key(&self) -> bool
pub fn is_primary_key(&self) -> bool
Whether this field is a primary key (name starts with “pk_” or equals “id”).
Sourcepub fn is_unique_key(&self) -> bool
pub fn is_unique_key(&self) -> bool
Whether this field identifies at most one row of its type’s relation.
True when the author declared unique, and true for the entity
identity without a declaration: ADR-0017 makes id: ID! the one global identity
every queryable entity exposes, and Relay Node resolution, Apollo Federation
@key(fields: "id") and cache normalization already read it as unique. A schema
whose id did not identify a row would be answering those three wrongly before
an embed ever asked.
The identity arm tests the type as well as the name, so a field merely
called id — examples/basic authors id: Int, which ADR-0017 deliberately
declines to canonicalize — is not mistaken for one. That keeps this a reading of
the identity contract rather than an inference from a spelling, which is the
difference between a fact and a guess.
Deliberately not extended to pk_<type>: is_primary_key
accepts that prefix, but it is a naming convention rather than a contract, and the
Trinity pattern’s pk_entity is documented as an internal join key that is never
exposed. A type that does publish one declares unique like any other column.