Skip to content

AuthoringIR Format

The AuthoringIR is the intermediate representation between SDK schema definitions and the FraiseQL compiler. SDKs generate schema.json in this format; the compiler reads it to produce compiled views, SQL migrations, and runtime artifacts.

SDK (Python/TypeScript/Go/...) → schema.json (AuthoringIR) → fraiseql compile → compiled output

Most users never edit schema.json directly — the SDK generates it. This reference is for:

  • Building custom code generators targeting FraiseQL
  • Debugging compilation errors
  • Understanding what the SDK produces
{
"version": "2.1",
"project": { "name": "my_app" },
"types": [],
"enums": [],
"input_types": [],
"interfaces": [],
"unions": [],
"scalars": [],
"queries": [],
"mutations": [],
"subscriptions": []
}

Only types, queries, and mutations are required. All other arrays default to empty when omitted.

Nullability is controlled by a nullable boolean field. The type field should be a bare type name.

// Recommended — bare type name + explicit nullable
{ "name": "id", "type": "ID", "nullable": false }
{ "name": "email", "type": "String", "nullable": true }
// Also valid — ! suffix is stripped and nullable is inferred as false
{ "name": "id", "type": "ID!" }

The ! suffix (e.g., "type": "ID!") is accepted for compatibility with GraphQL conventions: the parser strips the ! and sets nullable = false. If both ! and an explicit nullable field are present, the explicit nullable value wins. The bare type name form is preferred for clarity.

The default varies by context:

ElementDefault nullableRationale
Type fieldstrueMatches GraphQL convention (nullable by default)
Input type fieldstrueMatches GraphQL convention
Query/mutation argumentstrueArguments are optional by default
Query return typefalseQueries return data or error, not null
Mutation return typefalseMutations return data or error, not null

Each entry in the types array defines a GraphQL object type:

{
"name": "User",
"description": "A registered user",
"sql_source": "v_user",
"fields": [
{ "name": "id", "type": "ID", "nullable": false },
{ "name": "name", "type": "String", "nullable": false },
{ "name": "email", "type": "String", "nullable": false },
{ "name": "bio", "type": "String", "nullable": true },
{ "name": "created_at", "type": "DateTime", "nullable": false }
]
}
PropertyTypeRequiredDefaultDescription
namestringyesField name
typestringyesBare type name (no ! suffix)
nullablebooleannotrueWhether the field can be null
descriptionstringnoField description for GraphQL docs
sql_columnstringnoOverride the SQL column name (defaults to field name)
listbooleannofalseWhether this is a list type
AuthoringIR typeGraphQL typePostgreSQL type
IDIDUUID
StringStringTEXT
IntIntINTEGER
BigIntBigIntBIGINT
FloatFloatDOUBLE PRECISION
BooleanBooleanBOOLEAN
DateTimeDateTimeTIMESTAMPTZ
JSONJSONJSONB
DateDateDATE
TimeTimeTIME
DecimalDecimalNUMERIC

Plus all semantic scalars (Email, IBAN, Money, etc.) — see Semantic Scalars.

// List of strings (nullable list, non-null elements)
{ "name": "tags", "type": "String", "list": true, "nullable": true }
// Non-null list of non-null IDs
{ "name": "ids", "type": "ID", "list": true, "nullable": false }

Reference custom types by name (must match a type defined in the types array):

{ "name": "address", "type": "Address", "nullable": true }
{
"name": "OrderStatus",
"description": "Order lifecycle status",
"values": [
{ "name": "PENDING" },
{ "name": "CONFIRMED" },
{ "name": "SHIPPED" },
{ "name": "DELIVERED" },
{ "name": "CANCELLED", "deprecation_reason": "Use CANCELED instead" }
]
}

Each enum value can have an optional description and deprecation_reason.

Input types are used as arguments for mutations:

{
"name": "CreateUserInput",
"description": "Input for creating a user",
"fields": [
{ "name": "name", "type": "String", "nullable": false },
{ "name": "email", "type": "String", "nullable": false },
{ "name": "bio", "type": "String", "nullable": true }
]
}

Input type fields support default_value in addition to the standard field properties.

{
"name": "posts",
"sql_source": "v_post",
"return_type": "Post",
"returns_list": true,
"nullable": false,
"description": "List all posts",
"arguments": [
{ "name": "limit", "type": "Int", "nullable": true, "default_value": 10 },
{ "name": "offset", "type": "Int", "nullable": true, "default_value": 0 }
],
"rest": {
"path": "/posts",
"method": "GET"
},
"grpc": {
"service": "PostService",
"method": "ListPosts"
}
}
PropertyTypeRequiredDefaultDescription
namestringyesQuery name
return_typestringyesBare return type name
returns_listbooleannofalseWhether the query returns a list
nullablebooleannofalseWhether the return value can be null
sql_sourcestringnoSQL view or function backing this query
descriptionstringnoQuery description for GraphQL docs
argumentsarrayno[]Query arguments (see below)
restobjectnoREST transport annotation (path, method)
grpcobjectnogRPC transport annotation (service, method)
PropertyTypeRequiredDefaultDescription
namestringyesArgument name
typestringyesBare type name
nullablebooleannotrueWhether the argument is optional
default_valueanynoDefault value when argument is omitted
descriptionstringnoArgument description
{
"name": "create_post",
"sql_source": "fn_create_post",
"operation": "CREATE",
"return_type": "Post",
"nullable": false,
"arguments": [
{ "name": "title", "type": "String", "nullable": false },
{ "name": "author_id", "type": "ID", "nullable": false }
],
"rest": {
"path": "/posts",
"method": "POST"
}
}
PropertyTypeRequiredDefaultDescription
namestringyesMutation name
return_typestringyesBare return type name
nullablebooleannofalseWhether the return value can be null
operationstringyes"CREATE", "UPDATE", "DELETE", or "CUSTOM"
sql_sourcestringnoSQL function backing this mutation
descriptionstringnoMutation description
argumentsarrayno[]Mutation arguments
restobjectnoREST transport annotation
{
"name": "order_updated",
"return_type": "Order",
"description": "Watch for order changes",
"arguments": [
{ "name": "customer_id", "type": "ID", "nullable": false }
]
}

Inject JWT claims as implicit query parameters (not visible to clients):

{
"name": "orders",
"sql_source": "v_order",
"return_type": "Order",
"returns_list": true,
"inject_params": {
"tenant_id": "jwt.tenant_id"
},
"arguments": []
}

Keys are parameter names passed to the SQL view/function. Values are dot-paths into the JWT claims object.

{
"name": "Node",
"description": "Relay node interface",
"fields": [
{ "name": "id", "type": "ID", "nullable": false }
]
}
{
"name": "SearchResult",
"description": "Union of searchable types",
"types": ["User", "Post", "Comment"]
}

Register custom scalars with optional validation:

{
"name": "Email",
"description": "RFC 5322 email address",
"base_type": "String",
"specified_by_url": "https://datatracker.ietf.org/doc/html/rfc5322",
"validation_rules": []
}
ErrorCauseFix
Field missing 'name'Missing name in a field/argumentAdd "name" property
Field missing 'type'Missing type in a field/argumentAdd "type" property with bare type name
Query missing 'return_type'Missing return_type on a queryAdd "return_type" property
duplicate field 'eq' in WhereInputDuplicate argument namesRemove duplicate argument definitions

Each SDK has a compile command that produces schema.json:

Terminal window
# Python
fraiseql compile schema.py -o schema.json
# TypeScript
fraiseql compile schema.ts -o schema.json

Or use a custom code generator — any tool that produces valid AuthoringIR JSON is a valid FraiseQL schema source. See also SpecQL for TOML-first schema authoring.