Scalars Reference
Scalar type reference for all built-in and custom scalars.
FraiseQL’s type system bridges Python types, GraphQL schema, and PostgreSQL. This guide explains how types flow through the system.
Each layer has its own type representation:
| Python | GraphQL | PostgreSQL | JSON |
|---|---|---|---|
str | String! | TEXT | "string" |
int | Int! | INTEGER | 123 |
float | Float! | DOUBLE PRECISION | 1.23 |
bool | Boolean! | BOOLEAN | true |
str | None | String | TEXT | "string" or null |
list[str] | [String!]! | TEXT[] | ["a", "b"] |
| TypeScript | GraphQL | PostgreSQL | MySQL | SQLite |
|---|---|---|---|---|
string | String | TEXT | VARCHAR(255) | TEXT |
number (int) | Int | INTEGER | INT | INTEGER |
number (float) | Float | FLOAT8 | DOUBLE | REAL |
boolean | Boolean | BOOLEAN | TINYINT(1) | INTEGER |
Date | DateTime | TIMESTAMPTZ | DATETIME | TEXT |
string[] | [String!]! | TEXT[] | JSON | TEXT (JSON) |
import fraiseqlfrom fraiseql.scalars import ID, DateTime, Decimal
@fraiseql.typeclass User: """A user in the system.""" id: ID # UUID → ID! → UUID → "uuid-string" email: str # str → String! → TEXT → "string" name: str # str → String! → TEXT → "string" age: int # int → Int! → INTEGER → 123 balance: Decimal # Decimal → Decimal! → NUMERIC → "123.45" is_active: bool # bool → Boolean! → BOOLEAN → true created_at: DateTime # DateTime → DateTime! → TIMESTAMPTZ → "2024-01-15T..."Use union with None for nullable fields:
@fraiseql.typeclass User: bio: str | None # String (nullable) avatar_url: str | None # String (nullable) deleted_at: DateTime | None # DateTime (nullable)@fraiseql.typeclass User: roles: list[str] # [String!]! - non-null list, non-null items tags: list[str] | None # [String!] - nullable list, non-null items scores: list[int] # [Int!]!@fraiseql.typeclass Post: id: ID title: str author: User # Nested User objectGraphQL:
type Post { id: ID! title: String! author: User!}Use string literals for forward references:
@fraiseql.typeclass User: id: ID name: str posts: list['Post'] # Forward reference to Post
@fraiseql.typeclass Post: id: ID title: str author: 'User' # Forward reference to User@fraiseql.typeclass Comment: id: ID content: str parent: 'Comment | None' # Self-reference replies: list['Comment'] # Self-reference list@fraiseql.typeclass Post: id: ID featured_image: 'Image | None' # Optional relationshipfrom fraiseql.scalars import ( ID, # UUID DateTime, # Timestamp with timezone Date, # Date only Time, # Time only Decimal, # Arbitrary precision Json, # JSONB Vector, # pgvector)@fraiseql.scalarclass Email: """Email address with validation."""
@staticmethod def serialize(value: str) -> str: return value.lower()
@staticmethod def parse(value: str) -> str: import re if not re.match(r'^[^@]+@[^@]+\.[^@]+$', value): raise ValueError("Invalid email") return value.lower()Usage:
@fraiseql.typeclass User: email: Email # Custom scalarfrom enum import Enum
@fraiseql.enumclass OrderStatus(Enum): """Order status values.""" PENDING = "pending" CONFIRMED = "confirmed" SHIPPED = "shipped" DELIVERED = "delivered" CANCELLED = "cancelled"
@fraiseql.typeclass Order: id: ID status: OrderStatus # Enum typeGraphQL:
enum OrderStatus { PENDING CONFIRMED SHIPPED DELIVERED CANCELLED}
type Order { id: ID! status: OrderStatus!}Define shared fields across types:
@fraiseql.interfaceclass Node: """An object with a globally unique ID.""" id: ID
@fraiseql.interfaceclass Timestamped: """An object with timestamps.""" created_at: DateTime updated_at: DateTime
@fraiseql.type(implements=["Node", "Timestamped"])class User: id: ID name: str created_at: DateTime updated_at: DateTimeGraphQL:
interface Node { id: ID!}
interface Timestamped { createdAt: DateTime! updatedAt: DateTime!}
type User implements Node & Timestamped { id: ID! name: String! createdAt: DateTime! updatedAt: DateTime!}Represent multiple possible types:
@fraiseql.typeclass User: id: ID name: str
@fraiseql.typeclass Organization: id: ID name: str
@fraiseql.union(members=[User, Organization])class Actor: """An entity that can perform actions.""" passGraphQL:
union Actor = User | OrganizationQuery with type resolution:
query { actor(id: "...") { ... on User { name email } ... on Organization { name memberCount } }}Input types for mutations:
@fraiseql.inputclass CreateUserInput: """Input for creating a user.""" email: str name: str bio: str | None = None
@fraiseql.inputclass UpdateUserInput: """Input for updating a user.""" name: str | None = None bio: str | None = NoneGraphQL:
input CreateUserInput { email: String! name: String! bio: String}
input UpdateUserInput { name: String bio: String}FraiseQL infers types from Python annotations:
CREATE VIEW v_user ASSELECT u.id, -- UUID jsonb_build_object( 'id', u.id::text, -- String in JSONB 'name', u.name, -- String 'age', u.age, -- Integer 'balance', u.balance::text -- Decimal as string ) AS dataFROM tb_user u;CREATE FUNCTION fn_create_user( user_email TEXT, user_name TEXT) RETURNS UUID AS $$FraiseQL infers:
email: str, name: strID (UUID)| Python | GraphQL | Notes |
|---|---|---|
str | String! | Non-null |
str | None | String | Nullable |
list[str] | [String!]! | Non-null list and items |
list[str] | None | [String!] | Nullable list, non-null items |
list[str | None] | [String]! | Non-null list, nullable items |
| PostgreSQL | Python |
|---|---|
NOT NULL | Required field |
| Nullable | ... | None |
DEFAULT | Default value |
@fraiseql.inputclass CreateUserInput: email: Annotated[str, fraiseql.validate( pattern=r'^[^@]+@[^@]+\.[^@]+$' )] age: Annotated[int, fraiseql.validate( minimum=0, maximum=150 )]FraiseQL coerces types at boundaries:
# Query variable: "123"# Python receives: 123 (int)
# Query variable: "2024-01-15"# Python receives: datetime objectFields not stored in database:
@fraiseql.typeclass User: first_name: str last_name: str
# Computed from other fields full_name: Annotated[str, fraiseql.field(computed=True)]SQL implementation:
CREATE VIEW v_user ASSELECT jsonb_build_object( 'first_name', u.first_name, 'last_name', u.last_name, 'full_name', u.first_name || ' ' || u.last_name -- Computed ) AS dataFROM tb_user u;Control field exposure:
@fraiseql.typeclass User: id: ID email: str password_hash: Annotated[str, fraiseql.field( exclude=True # Never expose in GraphQL )] salary: Annotated[Decimal, fraiseql.field( requires_scope="hr:read" # Require scope )]# Good: Specific typesid: IDemail: strprice: Decimalcreated_at: DateTime
# Avoid: Generic typesid: str # Could be anythingprice: float # Precision issuescreated_at: str # Format unclear# PostgreSQL: DECIMAL(12,2)# Python: Decimal (not float)price: Decimal
# PostgreSQL: TIMESTAMPTZ# Python: DateTime (not str)created_at: DateTime@fraiseql.typeclass Order: """ Represents a customer order.
Contains order details, line items, and fulfillment status. """ id: ID """Unique order identifier."""
total: Decimal """Order total in USD."""Scalars Reference
Scalar type reference for all built-in and custom scalars.
Decorators
All decorators available in the FraiseQL schema DSL.
Schema Design
Design patterns for structuring your FraiseQL schema.
Elo Validation
Custom scalar validation with the Elo expression language.