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 outputMost 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
Top-Level Structure
Section titled “Top-Level Structure”{ "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 Rules
Section titled “Nullability Rules”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.
Defaults When nullable Is Omitted
Section titled “Defaults When nullable Is Omitted”The default varies by context:
| Element | Default nullable | Rationale |
|---|---|---|
| Type fields | true | Matches GraphQL convention (nullable by default) |
| Input type fields | true | Matches GraphQL convention |
| Query/mutation arguments | true | Arguments are optional by default |
| Query return type | false | Queries return data or error, not null |
| Mutation return type | false | Mutations return data or error, not null |
Type Definitions
Section titled “Type Definitions”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 } ]}Field Properties
Section titled “Field Properties”| Property | Type | Required | Default | Description |
|---|---|---|---|---|
name | string | yes | — | Field name |
type | string | yes | — | Bare type name (no ! suffix) |
nullable | boolean | no | true | Whether the field can be null |
description | string | no | — | Field description for GraphQL docs |
sql_column | string | no | — | Override the SQL column name (defaults to field name) |
list | boolean | no | false | Whether this is a list type |
Available Scalar Types
Section titled “Available Scalar Types”| AuthoringIR type | GraphQL type | PostgreSQL type |
|---|---|---|
ID | ID | UUID |
String | String | TEXT |
Int | Int | INTEGER |
BigInt | BigInt | BIGINT |
Float | Float | DOUBLE PRECISION |
Boolean | Boolean | BOOLEAN |
DateTime | DateTime | TIMESTAMPTZ |
JSON | JSON | JSONB |
Date | Date | DATE |
Time | Time | TIME |
Decimal | Decimal | NUMERIC |
Plus all semantic scalars (Email, IBAN, Money, etc.) — see Semantic Scalars.
List Types
Section titled “List Types”// 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 }Object References
Section titled “Object References”Reference custom types by name (must match a type defined in the types array):
{ "name": "address", "type": "Address", "nullable": true }Enum Definitions
Section titled “Enum Definitions”{ "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 Type Definitions
Section titled “Input Type Definitions”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.
Query Definitions
Section titled “Query Definitions”{ "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" }}Query Properties
Section titled “Query Properties”| Property | Type | Required | Default | Description |
|---|---|---|---|---|
name | string | yes | — | Query name |
return_type | string | yes | — | Bare return type name |
returns_list | boolean | no | false | Whether the query returns a list |
nullable | boolean | no | false | Whether the return value can be null |
sql_source | string | no | — | SQL view or function backing this query |
description | string | no | — | Query description for GraphQL docs |
arguments | array | no | [] | Query arguments (see below) |
rest | object | no | — | REST transport annotation (path, method) |
grpc | object | no | — | gRPC transport annotation (service, method) |
Argument Properties
Section titled “Argument Properties”| Property | Type | Required | Default | Description |
|---|---|---|---|---|
name | string | yes | — | Argument name |
type | string | yes | — | Bare type name |
nullable | boolean | no | true | Whether the argument is optional |
default_value | any | no | — | Default value when argument is omitted |
description | string | no | — | Argument description |
Mutation Definitions
Section titled “Mutation Definitions”{ "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" }}Mutation Properties
Section titled “Mutation Properties”| Property | Type | Required | Default | Description |
|---|---|---|---|---|
name | string | yes | — | Mutation name |
return_type | string | yes | — | Bare return type name |
nullable | boolean | no | false | Whether the return value can be null |
operation | string | yes | — | "CREATE", "UPDATE", "DELETE", or "CUSTOM" |
sql_source | string | no | — | SQL function backing this mutation |
description | string | no | — | Mutation description |
arguments | array | no | [] | Mutation arguments |
rest | object | no | — | REST transport annotation |
Subscription Definitions
Section titled “Subscription Definitions”{ "name": "order_updated", "return_type": "Order", "description": "Watch for order changes", "arguments": [ { "name": "customer_id", "type": "ID", "nullable": false } ]}inject_params
Section titled “inject_params”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.
Interface and Union Definitions
Section titled “Interface and Union Definitions”Interfaces
Section titled “Interfaces”{ "name": "Node", "description": "Relay node interface", "fields": [ { "name": "id", "type": "ID", "nullable": false } ]}Unions
Section titled “Unions”{ "name": "SearchResult", "description": "Union of searchable types", "types": ["User", "Post", "Comment"]}Custom Scalar Definitions
Section titled “Custom Scalar Definitions”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": []}Common Errors
Section titled “Common Errors”| Error | Cause | Fix |
|---|---|---|
Field missing 'name' | Missing name in a field/argument | Add "name" property |
Field missing 'type' | Missing type in a field/argument | Add "type" property with bare type name |
Query missing 'return_type' | Missing return_type on a query | Add "return_type" property |
duplicate field 'eq' in WhereInput | Duplicate argument names | Remove duplicate argument definitions |
Generating schema.json
Section titled “Generating schema.json”Each SDK has a compile command that produces schema.json:
# Pythonfraiseql compile schema.py -o schema.json
# TypeScriptfraiseql compile schema.ts -o schema.jsonOr 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.