Skip to content

How It Works

FraiseQL is a database-first GraphQL framework. You write SQL views that compose nested JSONB responses. FraiseQL maps your GraphQL types to those views. At runtime, every query resolves to a single SQL statement — no resolvers, no N+1, no DataLoader.

With a traditional GraphQL framework, a schema change cascades across multiple layers:

  1. Alter the database table
  2. Update the ORM model
  3. Regenerate GraphQL types
  4. Update resolvers
  5. Test everything together

With FraiseQL, the same change is localised:

  1. Update the SQL view (or add a column to it)
  2. Run fraiseql compile
  3. Done — the API reflects the view

The compile step is the key: FraiseQL reads your views and Python decorators, validates that everything aligns, and produces a compiled description of the API that the server executes without further interpretation. The database schema and the API schema are decoupled — a view is the contract between them.

graph LR
A[Write SQL Views<br/>v_* + fn_*] --> B[fraiseql compile<br/>GraphQL mapping]
B --> C[fraiseql run<br/>GraphQL API server]
C --> D[Single SQL query<br/>per GraphQL request]

You write SQL views following a simple pattern: each view has an id column and a data JSONB column containing the complete response for that entity.

db/schema/02_read/v_user.sql
CREATE VIEW v_user AS
SELECT
u.id,
jsonb_build_object(
'id', u.id::text,
'name', u.name,
'email', u.email
) AS data
FROM tb_user u;

Views compose other views. A post view embeds its author by referencing v_user.data:

db/schema/02_read/v_post.sql
CREATE VIEW v_post AS
SELECT
p.id,
jsonb_build_object(
'id', p.id::text,
'title', p.title,
'content', p.content,
'author', vu.data
) AS data
FROM tb_post p
JOIN tb_user u ON u.pk_user = p.fk_user
JOIN v_user vu ON vu.id = u.id;

Key insight: Each view owns its fields. Add a field to v_user once, and every view that embeds v_user.data gets it automatically. No duplication.

This is SQL you write, review, and own. You can use CTEs, window functions, stored procedures, custom aggregations — the full power of your database. FraiseQL works with PostgreSQL, SQL Server, MySQL, and SQLite (extensible to any database with a Rust driver). Or you can ask an LLM to generate the views. The pattern is consistent enough that local models produce accurate results.

Define your GraphQL schema in your preferred programming language:

schema.py
import fraiseql
@fraiseql.type
class User:
id: str
name: str
email: str
posts: list['Post']
@fraiseql.type
class Post:
id: str
title: str
content: str
author: User

This is real code in your language, with full IDE support, type checking, and refactoring tools — not a GraphQL SDL file.

When you run fraiseql compile, the compiler:

Terminal window
$ fraiseql compile
Compiled 2 types mapped to SQL views
Built query executor

What compilation does NOT do: It does not generate SQL views. Your views already exist in the database, created by you (or by 🍯 Confiture). Compilation maps GraphQL types to those views.

The compiled executor serves GraphQL queries:

  • No resolver execution — queries map directly to SQL views
  • No N+1 queries — relationships are pre-joined in the views
  • No runtime overhead — query paths are pre-compiled
Terminal window
$ fraiseql run
GraphQL API running at http://localhost:8080/graphql
Query → Parse → Validate → Execute Resolvers → Assemble Response
Multiple DB Queries (N+1)
DataLoader Batching
Memory Overhead

Problems:

  • Resolver functions execute for every field
  • N+1 queries require DataLoader workarounds
  • Performance depends on how resolvers are written
  • You debug generated SQL you didn’t write
Query → Match Compiled Path → Single SQL Query → Stream Response

Benefits:

  • No resolver functions to execute
  • Single SQL query per request — the view you wrote
  • Predictable, consistent performance — you see the query plan
  • Full database power — nothing is abstracted away (PostgreSQL, SQL Server, MySQL, SQLite)

Writing SQL views is one half. Managing the database is the other. 🍯 Confiture provides four strategies for every scenario:

StrategyWhat It DoesWhen to Use
BuildCreates a fresh database from DDL files in under 1sDevelopment, CI/CD, testing
MigrateApplies incremental ALTER statementsProduction schema changes
SyncCopies production data with anonymizationRealistic local development
Schema-to-SchemaZero-downtime migration via FDWMajor production refactoring
Terminal window
# Fresh database from your DDL files
confiture build --env local
# Apply pending migrations
confiture migrate up --env production

The compiler transforms your schema through three phases:

Schema files in any supported language are validated and parsed into a type graph:

graph LR
A[Schema Files<br/>Python / TS / Go] --> B[Lexer & Parser]
B --> C[Type Graph]

The type graph is analyzed to generate SQL views and optimize query paths:

graph LR
A[Type Graph] --> B[SQL View Analysis]
B --> C[Query Path Optimization]
C --> D[Compiled Query Map]

The final phase produces a compiled Rust binary, migration files, and all deployment artifacts:

graph LR
A[Compiled Query Map] --> B[Rust Code Generation]
B --> C[Query Executor Binary]
B --> D[SDK Types<br/>6 languages]
B --> E[Migration Files]

FraiseQL supports schema definition in Python, TypeScript, Go, Java, Rust, and more. All compile to the same optimized output.

  1. View Mapping — Maps each GraphQL type to its SQL view
  2. Query Executor — High-performance compiled query paths
  3. SDK Types — Schema authoring libraries in 6 languages

All configuration lives in a single TOML file:

fraiseql.toml
[project]
name = "my-api"
version = "1.0.0"
[database]
url = "${DATABASE_URL}"
[server]
port = 8080
host = "0.0.0.0"

No YAML. No JSON. Just readable TOML.

AspectTraditionalFraiseQL
Query executionResolver functionsCompiled SQL view mapping
SQL authorshipGenerated/hiddenDeveloper-owned
N+1 handlingDataLoader (manual)Eliminated by design
PerformanceVariablePredictable (you see the query)
Database managementORM migrations🍯 Confiture (4 strategies)
Schema definitionSDL onlyAny language

Query against a running FraiseQL instance. The demo uses the same single-query architecture described above — inspect the response shape and relate it back to the v_post view pattern.

FraiseQL Demo API

One GraphQL query. One SQL SELECT. See the result.

Loading Apollo Sandbox...

This sandbox uses Apollo Sandbox (the same GraphQL IDE as fraiseql serve). Your queries execute against the endpoint below. No data is sent to Apollo. Learn more about privacy →