Skip to main content

FieldDefinition

Struct FieldDefinition 

Source
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: FieldName

Field name - the key in the JSONB data column (e.g., “email”).

§field_type: FieldType

Field type.

§nullable: bool

Is 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: FieldDenyPolicy

Policy when a user lacks the required scope for this field.

  • Reject (default): the entire query fails with a FORBIDDEN error.
  • Mask: the query succeeds but this field returns null.
§authorize: bool

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: bool

Whether 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

Source

pub fn new(name: impl Into<String>, field_type: FieldType) -> Self

Create a new required field.

Source

pub fn nullable(name: impl Into<String>, field_type: FieldType) -> Self

Create a new nullable field.

Source

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());
Source

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));
Source

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");
Source

pub const fn with_on_deny(self, policy: FieldDenyPolicy) -> Self

Set the deny policy for when a user lacks the required scope.

Source

pub const fn with_authorize(self, authorize: bool) -> Self

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);
Source

pub fn with_description(self, desc: impl Into<String>) -> Self

Add description to field.

Source

pub fn with_default(self, value: GraphQLValue) -> Self

Add default value to field.

Source

pub const fn with_vector_config(self, config: VectorConfig) -> Self

Add vector configuration to field.

Source

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"));
Source

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 unchanged
Source

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.

Source

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
Source

pub const fn has_alias(&self) -> bool

Check if this field has an alias.

Source

pub const fn is_vector(&self) -> bool

Check if this is a float vector field.

Source

pub const fn is_bit_vector(&self) -> bool

Check if this is a binary (bit) vector field (#959).

Source

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());
Source

pub const fn is_deprecated(&self) -> bool

Check if this field is deprecated.

Source

pub fn deprecation_reason(&self) -> Option<&str>

Get the deprecation reason if deprecated.

Source

pub fn with_encryption(self, config: FieldEncryptionConfig) -> Self

Add encryption configuration to this field.

Source

pub const fn is_encrypted(&self) -> bool

Check if this field is encrypted.

Source

pub fn is_primary_key(&self) -> bool

Whether this field is a primary key (name starts with “pk_” or equals “id”).

Source

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.

Trait Implementations§

Source§

impl Clone for FieldDefinition

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for FieldDefinition

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'de> Deserialize<'de> for FieldDefinition

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl PartialEq for FieldDefinition

Source§

fn eq(&self, other: &Self) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl Serialize for FieldDefinition

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl StructuralPartialEq for FieldDefinition

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> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

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> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. 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: Sized + 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: Sized + 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> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

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

Source§

type Error = !

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

fn try_from(value: U) -> Result<T, !>

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