Production-ready GraphQL API framework for PostgreSQL with CQRS, JSONB optimization, and type-safe mutations
Project description
FraiseQL
๐ You are here: Main FraiseQL Framework (v1.3.0) - Production Stable
GraphQL for the LLM era. Simple. Powerful. Rust-fast.
PostgreSQL returns JSONB. Rust transforms it. Zero Python overhead.
# Complete GraphQL API in ~15 lines
from fraiseql import type, query
from fraiseql.fastapi import create_fraiseql_app
@type(sql_source="v_user", jsonb_column="data")
class User:
id: int
name: str
email: str
@query
async def users(info) -> list[User]:
db = info.context["db"]
return await db.find("v_user")
app = create_fraiseql_app(
database_url="postgresql://localhost/mydb",
types=[User],
queries=[users]
)
Why FraiseQL?
- โก Rust pipeline - No Python JSON overhead, compiled performance
- ๐ Secure by design - Explicit field contracts prevent data leaks
- ๐ค AI-native - LLMs generate correct code on first try
- ๐ฐ Save $5-48K/year - Eliminate Redis, Sentry, APM tools
- ๐ Advanced filtering - Full-text search, JSONB queries, array operations, regex
๐ค Is this for me?
FraiseQL is for production teams building high-performance GraphQL APIs with PostgreSQL.
โ You should use FraiseQL if you:
- Build customer-facing APIs with PostgreSQL
- Need sub-millisecond query performance
- Want enterprise-grade security and monitoring
- Have 2-50 developers on your team
- Are tired of Python serialization overhead
โ Consider alternatives if you:
- Need multi-database support (FraiseQL is PostgreSQL-only)
- Are building your first GraphQL API (start with simpler frameworks)
- Don't use JSONB columns in PostgreSQL
See detailed audience guide for complete user profiles.
โก The Rust Advantage
The problem with traditional GraphQL frameworks:
PostgreSQL โ Rows โ ORM deserialize โ Python objects โ GraphQL serialize โ JSON โ Response
โฐโโโโโโโโโโโโโโ Unnecessary roundtrip โโโโโโโโโโโโโโโฏ
FraiseQL's exclusive Rust pipeline:
PostgreSQL โ JSONB โ Rust field selection โ HTTP Response
โฐโโโโโโโโ Zero Python overhead โโโโโโโโโฏ
Why This Matters
No Python serialization overhead:
# Traditional framework (Strawberry + SQLAlchemy)
user = db.query(User).first() # SQL query
user_dict = user.__dict__ # Python object โ dict
json_str = json.dumps(user_dict) # dict โ JSON string (slow!)
# FraiseQL
SELECT data FROM v_user LIMIT 1 # Returns JSONB
# Rust transforms JSONB โ HTTP response (7-10x faster than Python)
Architectural benefits:
- PostgreSQL composes JSONB once - No N+1 query problems
- Rust selects fields - Respects GraphQL query shape in compiled code
- Direct HTTP response - Zero-copy path from database to client
- No ORM abstraction - Database returns final data structure
Security benefits:
- Explicit field exposure - Only fields in JSONB view are accessible (no accidental leaks)
- Clear data contracts - JSONB structure defines exactly what's exposed
- No ORM over-fetching - Can't accidentally expose hidden columns
- SQL injection protection - PostgreSQL prepared statements + typed parameters
- Audit trail by design - Every mutation function can log explicitly
- No mass assignment risks - Input types define allowed fields precisely
Other frameworks can't do this. They're locked into Python-based serialization because ORM returns Python objects. ORMs can accidentally expose fields you didn't mean to serialize, or fetch entire rows when only requesting specific fields.
FraiseQL is database-first, so data is already JSON. Rust just makes it fast and secure.
๐ Security by Architecture
Traditional ORM-based frameworks have inherent security risks:
The ORM Security Problem
# Traditional ORM (SQLAlchemy + Strawberry)
class User(Base):
id = Column(Integer, primary_key=True)
email = Column(String)
password_hash = Column(String) # Sensitive!
is_admin = Column(Boolean) # Sensitive!
api_key = Column(String) # Sensitive!
# Strawberry type
@strawberry.type
class UserType:
id: int
email: str
# Developer forgot to exclude password_hash, is_admin, api_key!
# Risk: ORM object has ALL columns accessible
# One mistake in serialization = data leak
Common ORM vulnerabilities:
- โ Accidental field exposure - ORM loads all columns, easy to forget exclusions
- โ Mass assignment attacks - ORM objects can be updated with any field
- โ Over-fetching - Fetching entire rows increases attack surface
- โ Hidden relationships - Lazy loading can expose unintended data
- โ Implicit behavior - ORM magic makes security audits difficult
FraiseQL's Explicit Security
-- PostgreSQL view explicitly defines what's exposed
CREATE VIEW v_user AS
SELECT
id,
jsonb_build_object(
'id', id,
'email', email
-- password_hash, is_admin, api_key NOT included
-- Impossible to accidentally expose them!
) as data
FROM tb_user;
# Python type mirrors EXACT view structure
@type(sql_source="v_user", jsonb_column="data")
class User:
id: int
email: str
# That's it. No other fields exist in this contract.
FraiseQL security advantages:
- โ Explicit field whitelisting - Only fields in JSONB view can be queried
- โ Impossible to over-fetch - View defines the complete data structure
- โ Fixed recursion depth - View defines max nesting, prevents depth attacks
- โ Protected against N+1 bombs - One query regardless of GraphQL complexity
- โ Clear audit trail - Database view + Python type = two-layer verification
- โ SQL injection protection - Prepared statements + typed parameters always
- โ Mass assignment prevention - Input types define allowed fields precisely
- โ Row-level security - PostgreSQL RLS integrates directly with views
- โ Cryptographic audit logging - Built-in SHA-256 + HMAC audit chains
Recursion Depth Attack Protection
Traditional GraphQL vulnerability:
# Malicious query - can crash traditional servers
query {
user(id: 1) {
posts { # 10 posts
author { # โ 10 queries
posts { # โ 10 ร 10 = 100 queries
author { # โ 100 queries
posts { # โ 1,000 queries
# ... 10 levels = 10^10 queries = server crash
}
}
}
}
}
}
}
Traditional framework response:
- Each resolver level executes database queries
- N+1 problem multiplies exponentially with depth
- Requires query complexity middleware (can be bypassed)
- DataLoader reduces but doesn't eliminate the problem
FraiseQL's built-in protection:
-- View defines MAXIMUM recursion depth
CREATE VIEW v_user AS
SELECT
id,
jsonb_build_object(
'id', id,
'name', name,
'posts', (
SELECT jsonb_agg(jsonb_build_object(
'id', p.id,
'title', p.title
-- NO 'author' field here!
-- Recursion is STRUCTURALLY IMPOSSIBLE
))
FROM tb_post p
WHERE p.user_id = tb_user.id
LIMIT 100 -- Hard limit on array size
)
) as data
FROM tb_user;
What happens when attacker tries deep query:
query {
user {
posts {
author { # โ GraphQL schema validation FAILS
# Field 'author' doesn't exist on Post type
# because v_post view doesn't include it
}
}
}
}
Protection layers:
- Schema validation - GraphQL rejects queries for non-existent fields
- View structure - Database defines allowed nesting depth
- Hard limits - LIMIT clauses prevent array size attacks
- One query - PostgreSQL executes entire JSONB in single query
Result: Attackers cannot exceed the depth you define in views. No middleware needed.
Mutation Security Example
CREATE OR REPLACE FUNCTION fn_update_user_email(
p_user_id UUID,
p_new_email TEXT
) RETURNS JSONB AS $$
BEGIN
-- Explicit validation (visible in code)
IF p_new_email !~ '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}$' THEN
RETURN jsonb_build_object('success', false, 'error', 'Invalid email');
END IF;
-- Only updates the email column (nothing else is possible)
UPDATE tb_user
SET email = p_new_email
WHERE id = p_user_id;
-- Automatic audit logging
INSERT INTO audit_log (action, user_id, details, timestamp)
VALUES ('email_updated', p_user_id, jsonb_build_object('new_email', p_new_email), NOW());
RETURN jsonb_build_object('success', true);
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
No ORM magic. No hidden behavior. Everything is explicit and auditable.
๐ค Built for AI-Assisted Development
FraiseQL is the first GraphQL framework designed for the LLM era.
Clear Context in SQL Functions
CREATE OR REPLACE FUNCTION fn_create_user(
p_email TEXT,
p_name TEXT
) RETURNS JSONB AS $$
DECLARE
v_user_id UUID;
BEGIN
-- AI can see exactly what happens here
-- No hidden ORM magic, no abstraction layers
-- Validate email
IF p_email !~ '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}$' THEN
RETURN jsonb_build_object(
'success', false,
'error', 'Invalid email format'
);
END IF;
-- Insert user
INSERT INTO tb_user (email, name)
VALUES (p_email, p_name)
RETURNING id INTO v_user_id;
-- Log for observability
INSERT INTO audit_log (action, details, timestamp)
VALUES ('user_created', jsonb_build_object('user_id', v_user_id), NOW());
-- Return clear JSONB contract
RETURN jsonb_build_object(
'success', true,
'user_id', v_user_id,
'message', 'User created successfully'
);
END;
$$ LANGUAGE plpgsql;
The entire business logic is in one place. LLMs don't need to guess about hidden ORM behavior.
Explicit Contracts
@input
class CreateUserInput:
email: str # AI sees exact input structure
name: str
@success
class UserCreated:
user_id: str # AI sees success response
message: str
@failure
class ValidationError:
error: str # AI sees failure cases
code: str = "VALIDATION_ERROR"
@mutation(function="fn_create_user", schema="public")
class CreateUser:
input: CreateUserInput
success: UserCreated
failure: ValidationError
# That's it! FraiseQL automatically:
# 1. Calls public.fn_create_user(input) with input as dict
# 2. Parses JSONB result into UserCreated or ValidationError
Why AI Loves This
- โ SQL + Python - Massively trained languages (no proprietary DSLs)
- โ JSONB everywhere - Clear data structures, obvious contracts
- โ Database functions - Complete context in one file
- โ Explicit logging - AI can trace execution without debugging
- โ No abstraction layers - What you see is what executes
Real Impact: Claude Code, GitHub Copilot, and ChatGPT generate correct FraiseQL code on first try.
๐ Core Concepts
New to FraiseQL? Understanding these core concepts will help you make the most of the framework:
๐ Concepts & Glossary - Essential terminology and mental models:
- CQRS Pattern - Separate read models (views) from write models (functions)
- Trinity Identifiers - Three-tier ID system (
pk_*,id,identifier) for performance and UX - JSONB Views - PostgreSQL composes data once, eliminating N+1 queries
- Database-First Architecture - Start with PostgreSQL, GraphQL follows
- Explicit Sync Pattern - Table views (
tv_*) for complex queries
Quick links:
- Understanding FraiseQL - 10-minute architecture overview
- Database API - Connection pooling and query execution
- Types and Schema - Complete type system guide
- Filter Operators - Advanced PostgreSQL filtering (arrays, full-text search, JSONB, regex)
โจ See How Simple It Is
Complete CRUD API in 20 Lines
from uuid import UUID
from fraiseql import type, query, mutation, input, success
from fraiseql.fastapi import create_fraiseql_app
# Step 1: Map PostgreSQL view to GraphQL type
@type(sql_source="v_note", jsonb_column="data")
class Note:
id: UUID
title: str
content: str | None
# Step 2: Define queries
@query
async def notes(info) -> list[Note]:
"""Get all notes."""
db = info.context["db"]
return await db.find("v_note")
@query
async def note(info, id: UUID) -> Note | None:
"""Get a note by ID."""
db = info.context["db"]
return await db.find_one("v_note", id=id)
# Step 3: Define mutations
@input
class CreateNoteInput:
title: str
content: str | None = None
@mutation
class CreateNote:
input: CreateNoteInput
success: Note
# Step 4: Create app
app = create_fraiseql_app(
database_url="postgresql://localhost/mydb",
types=[Note],
queries=[notes, note],
mutations=[CreateNote]
)
That's it. Your GraphQL API is ready.
The Database-First Pattern
-- Step 1: PostgreSQL view composes data as JSONB
CREATE VIEW v_user AS
SELECT
id,
jsonb_build_object(
'id', id,
'name', name,
'email', email,
'posts', (
SELECT jsonb_agg(
jsonb_build_object(
'id', p.id,
'title', p.title,
'content', p.content
)
)
FROM tb_post p
WHERE p.user_id = tb_user.id
)
) as data
FROM tb_user;
# Step 2: Python decorator maps it to GraphQL
@type(sql_source="v_user", jsonb_column="data")
class User:
id: int
name: str
email: str
posts: list[Post] # Nested relations! No N+1 queries!
# Step 3: Query it
@query
async def users(info) -> list[User]:
db = info.context["db"]
return await db.find("v_user")
No ORM. No complex resolvers. PostgreSQL composes data, Rust transforms it.
Mutations with Business Logic
CREATE OR REPLACE FUNCTION fn_publish_post(p_post_id INT) RETURNS JSONB AS $$
DECLARE
v_post RECORD;
BEGIN
-- Get post with user info
SELECT p.*, u.email as user_email
INTO v_post
FROM tb_post p
JOIN tb_user u ON p.user_id = u.id
WHERE p.id = p_post_id;
-- Validate post exists
IF NOT FOUND THEN
RETURN jsonb_build_object('success', false, 'error', 'Post not found');
END IF;
-- Validate not already published
IF v_post.published_at IS NOT NULL THEN
RETURN jsonb_build_object('success', false, 'error', 'Post already published');
END IF;
-- Update post
UPDATE tb_post
SET published_at = NOW()
WHERE id = p_post_id;
-- Log event
INSERT INTO audit_log (action, details)
VALUES ('post_published', jsonb_build_object('post_id', p_post_id, 'user_email', v_post.user_email));
-- Return success
RETURN jsonb_build_object('success', true, 'post_id', p_post_id);
END;
$$ LANGUAGE plpgsql;
Business logic, validation, logging - all in the database function. Crystal clear for humans and AI.
๐ฐ In PostgreSQL Everything
Replace 4 services with 1 database.
Cost Savings Calculator
| Traditional Stack | FraiseQL Stack | Annual Savings |
|---|---|---|
| PostgreSQL: $50/mo | PostgreSQL: $50/mo | - |
| Redis Cloud: $50-500/mo | โ In PostgreSQL | $600-6,000/yr |
| Sentry: $300-3,000/mo | โ In PostgreSQL | $3,600-36,000/yr |
| APM Tool: $100-500/mo | โ In PostgreSQL | $1,200-6,000/yr |
| Total: $500-4,050/mo | Total: $50/mo | $5,400-48,000/yr |
How It Works
Caching (Replaces Redis)
from fraiseql.caching import PostgresCache
cache = PostgresCache(db_pool)
await cache.set("user:123", user_data, ttl=3600)
# Uses PostgreSQL UNLOGGED tables
# - No WAL overhead = fast writes
# - Shared across instances
# - TTL-based expiration
# - Pattern-based deletion
Error Tracking (Replaces Sentry)
from fraiseql.monitoring import init_error_tracker
tracker = init_error_tracker(db_pool, environment="production")
await tracker.capture_exception(error, context={...})
# Features:
# - Automatic error fingerprinting and grouping
# - Full stack trace capture
# - OpenTelemetry trace correlation
# - Custom notifications (Email, Slack, Webhook)
Observability (Replaces APM)
-- All traces and metrics stored in PostgreSQL
SELECT * FROM monitoring.traces
WHERE error_id = 'error-123'
AND trace_id = 'trace-xyz';
Grafana Dashboards
Pre-built dashboards in grafana/ query PostgreSQL directly:
- Error monitoring dashboard
- Performance metrics dashboard
- OpenTelemetry traces dashboard
Operational Benefits
- โ 70% fewer services to deploy and monitor
- โ One database to backup (not 4 separate systems)
- โ No Redis connection timeouts or cluster issues
- โ No Sentry quota surprises or rate limiting
- โ ACID guarantees for everything (no eventual consistency)
- โ Self-hosted - full control, no vendor lock-in
๐๏ธ Architecture Deep Dive
Rust-First Execution
โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ
โ GraphQL โ โ โ PostgreSQL โ โ โ Rust โ
โ Request โ โ JSONB Query โ โ Transform โ
โ โ โ โ โ (7-10x faster)โ
โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ
โ
โโโโโโโโโโโโโโโโโโโ
โ FastAPI โ
โ HTTP Response โ
โโโโโโโโโโโโโโโโโโโ
Unified path for all queries:
- GraphQL query arrives at FastAPI
- Python resolver calls PostgreSQL view/function
- PostgreSQL returns pre-composed JSONB
- Rust pipeline transforms JSONB based on GraphQL selection
- FastAPI returns bytes directly (zero Python serialization)
CQRS Pattern
FraiseQL implements Command Query Responsibility Segregation:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ GraphQL API โ
โโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโค
โ QUERIES โ MUTATIONS โ
โ (Reads) โ (Writes) โ
โโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโค
โ v_* views โ fn_* functions โ
โ tv_* tables โ tb_* tables โ
โ JSONB ready โ Business logic โ
โโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโ
Queries use views:
v_*- Real-time views with JSONB computationtv_*- Denormalized tables with generated JSONB columns (for complex queries)
Mutations use functions:
fn_*- Business logic, validation, side effectstb_*- Base tables for data storage
๐ Detailed Architecture Diagrams
Key Innovations
1. Exclusive Rust Pipeline
- PostgreSQL โ Rust โ HTTP (no Python JSON processing)
- 7-10x faster JSON transformation vs Python
- No GIL contention, compiled performance
2. JSONB Views
- Database composes data once
- Rust selects fields based on GraphQL query
- No N+1 query problems
3. Table Views (tv_*)
-- Denormalized JSONB table with explicit sync
CREATE TABLE tv_user (
id INT PRIMARY KEY,
data JSONB NOT NULL, -- Regular column, not generated
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- Sync function populates tv_* from v_* view
CREATE FUNCTION fn_sync_tv_user(p_user_id INT) RETURNS VOID AS $$
BEGIN
INSERT INTO tv_user (id, data)
SELECT id, data FROM v_user WHERE id = p_user_id
ON CONFLICT (id) DO UPDATE SET
data = EXCLUDED.data,
updated_at = NOW();
END;
$$ LANGUAGE plpgsql;
-- Mutations call sync explicitly
CREATE FUNCTION fn_create_user(p_name TEXT) RETURNS JSONB AS $$
DECLARE v_user_id INT;
BEGIN
INSERT INTO tb_user (name) VALUES (p_name) RETURNING id INTO v_user_id;
PERFORM fn_sync_tv_user(v_user_id); -- โ Explicit sync call
RETURN (SELECT data FROM tv_user WHERE id = v_user_id);
END;
$$ LANGUAGE plpgsql;
Benefits: Instant lookups, embedded relations, explicitly synchronized
4. Zero-Copy Response
- Direct RustResponseBytes to FastAPI
- No Python serialization overhead
- Optimal for high-throughput APIs
๐ฏ How FraiseQL Is Different
Execution Path Comparison
| Framework | Data Flow | JSON Processing | Recursion Protection | Security Model |
|---|---|---|---|---|
| FraiseQL | PostgreSQL JSONB โ Rust โ HTTP | โ Rust (compiled) | โ View-enforced | โ Explicit contracts |
| Strawberry + SQLAlchemy | PostgreSQL โ ORM โ Python dict โ JSON | โ Python (2 steps) | โ ๏ธ Middleware required | โ ORM over-fetching risk |
| Hasura | PostgreSQL โ Haskell โ JSON | โ ๏ธ Haskell | โ ๏ธ Middleware required | โ ๏ธ Complex permission system |
| PostGraphile | PostgreSQL โ Node.js โ JSON | โ ๏ธ JavaScript | โ ๏ธ Middleware required | โ ๏ธ Plugin-based |
FraiseQL's Unique Advantages
- โ Database returns final structure (JSONB views)
- โ Rust handles field selection (compiled performance)
- โ No Python in hot path (zero serialization overhead)
- โ No ORM abstraction (SQL functions are business logic)
- โ Built-in recursion protection (view defines max depth, no middleware needed)
- โ Secure by design (explicit field contracts prevent data leaks)
- โ AI-readable (clear contracts, full context visible)
- โ PostgreSQL-native (caching, monitoring, APQ in one database)
๐ฏ Advanced Features
Automatic Persisted Queries (APQ)
Enterprise-grade APQ with pluggable storage backends:
from fraiseql import FraiseQLConfig
# Memory backend (zero configuration)
config = FraiseQLConfig(apq_storage_backend="memory")
# PostgreSQL backend (multi-instance coordination)
config = FraiseQLConfig(
apq_storage_backend="postgresql",
apq_storage_schema="apq_cache"
)
How it works:
- Client sends query hash instead of full query
- FraiseQL checks storage backend for cached query
- PostgreSQL โ Rust โ HTTP (same fast path)
- Bandwidth reduction with large queries
Specialized Type System
Advanced operators for network types, hierarchical data, ranges, and nested arrays:
query {
servers(where: {
ipAddress: { eq: "192.168.1.1" } # โ ::inet casting
port: { gt: 1024 } # โ ::integer casting
location: { ancestor_of: "US.CA" } # โ ltree operations
dateRange: { overlaps: "[2024-01-01,2024-12-31)" }
# Nested array filtering with logical operators
printServers(where: {
AND: [
{ operatingSystem: { in: ["Linux", "Windows"] } }
{ OR: [
{ nTotalAllocations: { gte: 100 } }
{ NOT: { ipAddress: { isnull: true } } }
]
}
]
}) {
hostname operatingSystem
}
}) {
id name ipAddress port
}
}
Supported specialized types:
- Network: IPv4, IPv6, CIDR, MACAddress with subnet operations
- Hierarchical: LTree with ancestor/descendant queries
- Temporal: DateRange with overlap/containment
- Geospatial: Coordinate (latitude/longitude) with distance calculations
- Standard: EmailAddress, UUID, JSON with validation
- Nested Arrays: Complete AND/OR/NOT logical operators
๐ Nested Array Filtering Guide
Enterprise Security
from fraiseql import authorized
@authorized(roles=["admin", "editor"])
@mutation
class DeletePost:
"""Only admins and editors can delete posts."""
input: DeletePostInput
success: DeleteSuccess
failure: PermissionDenied
# Features:
# - Field-level authorization with role inheritance
# - Row-level security via PostgreSQL RLS
# - Unified audit logging with cryptographic chain (SHA-256 + HMAC)
# - Multi-tenant isolation
# - Rate limiting and CSRF protection
Trinity Identifiers
Three types of identifiers per entity for different purposes:
@fraiseql.type(sql_source="posts")
class Post(TrinityMixin):
"""
Trinity Pattern:
- pk_post (int): Internal SERIAL key (NOT exposed, only in database)
- id (UUID): Public API key (exposed, stable)
- identifier (str): Human-readable slug (exposed, SEO-friendly)
"""
# GraphQL exposed fields
id: UUID # Public API (stable, secure)
identifier: str | None # Human-readable (SEO-friendly, slugs)
title: str
content: str
# ... other fields
# pk_post is NOT a field - accessed via TrinityMixin.get_internal_pk()
Why three?
- pk_*: Fast integer joins (PostgreSQL only, never in GraphQL schema)
- id: Public API stability (UUID, exposed, never changes)
- identifier: Human-friendly URLs (exposed, SEO, readability)
๐ Get Started in 5 Minutes
# Install
pip install fraiseql
# Create project
fraiseql init my-api
cd my-api
# Setup database
createdb my_api
psql my_api < schema.sql
# Start server
fraiseql dev
Your GraphQL API is live at http://localhost:8000/graphql ๐
Next Steps
๐ First Hour Guide - Build a complete blog API (60 minutes, hands-on) ๐ง Understanding FraiseQL - Architecture deep dive (10 minute read) โก 5-Minute Quickstart - Copy, paste, run ๐ Full Documentation - Complete guides and references
Prerequisites
- Python 3.13+ (required for Rust pipeline integration and advanced type features)
- PostgreSQL 13+
๐ Detailed Installation Guide - Platform-specific instructions, troubleshooting
๐ฆ Is FraiseQL Right for You?
โ Perfect For
- PostgreSQL-first teams already using PostgreSQL extensively
- Performance-critical APIs requiring efficient data access
- Multi-tenant SaaS with per-tenant isolation needs
- Cost-conscious startups ($5-48K annual savings vs traditional stack)
- AI-assisted development teams using Claude/Copilot/ChatGPT
- Operational simplicity - one database for everything
- Self-hosted infrastructure - full control, no vendor lock-in
โ Consider Alternatives
- Multi-database support - FraiseQL is PostgreSQL-specific
- Simple CRUD APIs - Traditional REST may be simpler
- Non-PostgreSQL databases - FraiseQL requires PostgreSQL
- Microservices - Better for monolithic or database-per-service
๐ ๏ธ CLI Commands
# Project management
fraiseql init <name> # Create new project
fraiseql dev # Development server with hot reload
fraiseql check # Validate schema and configuration
# Code generation
fraiseql generate schema # Export GraphQL schema
fraiseql generate types # Generate TypeScript definitions
# Database utilities
fraiseql sql analyze <query> # Analyze query performance
fraiseql sql explain <query> # Show PostgreSQL execution plan
๐ Learn More
- Documentation - Complete guides and API reference
- Examples - Real-world applications and patterns
- Architecture - Design decisions and trade-offs
- Performance Guide - Optimization strategies
- Benchmark Methodology - Reproducible performance benchmarks
- Reproduction Guide - Run benchmarks yourself
- Troubleshooting - Common issues and solutions
๐ค Contributing
We welcome contributions! See CONTRIBUTING.md for:
- Development setup and testing
- Architecture decisions and patterns
- Code style and review process
git clone https://github.com/fraiseql/fraiseql
cd fraiseql && make setup-dev
๐ Acknowledgments
FraiseQL draws inspiration from:
- Strawberry GraphQL - Excellent Python GraphQL library ("Fraise" = French for strawberry)
- Harry Percival's "Architecture Patterns with Python" - Clean architecture and repository patterns
- Eric Evans' "Domain-Driven Design" - Database-centric domain modeling
- PostgreSQL community - For building the world's most advanced open source database
๐จโ๐ป About
FraiseQL is created by Lionel Hamayon (@evoludigit), a self-taught developer and founder of รvolution digitale.
Started: April 2025
The Origin Story
I built FraiseQL out of frustration with a stupid inefficiency: PostgreSQL returns JSON โ Python deserializes to objects โ GraphQL serializes back to JSON.
Why are we doing this roundtrip?
After years moving through Django, Flask, FastAPI, and Strawberry GraphQL with SQLAlchemy, I realized the entire approach was wrong. Just let PostgreSQL return the JSON directly. Skip the ORM. Skip the object mapping.
But I also wanted something designed for the LLM era. SQL and Python are two of the most massively trained languagesโLLMs understand them natively. Why not make a framework where AI can easily get context and generate correct code?
FraiseQL is the result:
- Database-first CQRS where PostgreSQL does what it does best
- Rust pipeline for compiled performance (7-10x faster than Python JSON)
- Python stays minimal - just decorators and type hints
- LLM-readable by design - clear contracts, explicit logic
Full disclosure: I built this while compulsively preparing for scale I didn't have. But that obsession led somewhere realโzero N+1 queries, efficient architecture, and a framework that both humans and AI can understand.
Connect:
- ๐ผ GitHub: @evoludigit
- ๐ง lionel.hamayon@evolution-digitale.fr
- ๐ข รvolution digitale
Support FraiseQL:
- โญ Star fraiseql/fraiseql
- ๐ฌ Join discussions and share feedback
- ๐ค Contribute to the project
๐ License
MIT License - see LICENSE for details.
๐ Project Navigation
Version Overview
| Version | Location | Status | Purpose | For Users? |
|---|---|---|---|---|
| v1.3.0 | Root level | Production Stable | Latest stable release | โ Recommended |
| Rust Pipeline | fraiseql_rs/ |
Integrated | Included in v1.0+ | โ Stable |
| v1.2.2 | Superseded | Legacy | Use v1.3.0 | โ ๏ธ Migrate |
New to FraiseQL? โ First Hour Guide โข Project Structure
Migration Guides:
- v1 to v2 Migration - Unified Rust-first architecture
- Monitoring Migration - From Redis and Sentry
Ready to build the most efficient GraphQL API in Python?
pip install fraiseql && fraiseql init my-api
๐ PostgreSQL โ Rust โ Production
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fraiseql-1.3.0.tar.gz.
File metadata
- Download URL: fraiseql-1.3.0.tar.gz
- Upload date:
- Size: 2.8 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.9.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d8ce616293edf747d39a9fc68b32a56b2a964b90145188916b3bf778a473d4fb
|
|
| MD5 |
b5a14b2a2629fc0ce9213bfafc26630d
|
|
| BLAKE2b-256 |
058d7f7011cc46c41a7a6b9a182b42d33394a8e709bbaa929aa3e7e200dcf68d
|
File details
Details for the file fraiseql-1.3.0-cp314-cp314-win_amd64.whl.
File metadata
- Download URL: fraiseql-1.3.0-cp314-cp314-win_amd64.whl
- Upload date:
- Size: 769.3 kB
- Tags: CPython 3.14, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.9.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cfb1a4ac32ab50d0f3ed0c4ac49b83f83c58bf571395c0e874d37c998b6620c3
|
|
| MD5 |
25d5dbdc4c57131d297cd87220e8491a
|
|
| BLAKE2b-256 |
813ca9292d0440cc006b875c43a278ebe1a4789293467c213053f4e08102725f
|
File details
Details for the file fraiseql-1.3.0-cp314-cp314-macosx_11_0_arm64.whl.
File metadata
- Download URL: fraiseql-1.3.0-cp314-cp314-macosx_11_0_arm64.whl
- Upload date:
- Size: 826.9 kB
- Tags: CPython 3.14, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.9.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
76ebe5da0f3ba97b5549d343782353a6c185d26fe03334d0abb5d8ed89c9874c
|
|
| MD5 |
47a93ef0e340d5c5289e768a6ffc74b3
|
|
| BLAKE2b-256 |
29755c85bc75e92f76cf622a614f52be2e976dc513d3350e79cc712cae4943e2
|
File details
Details for the file fraiseql-1.3.0-cp313-cp313-win_amd64.whl.
File metadata
- Download URL: fraiseql-1.3.0-cp313-cp313-win_amd64.whl
- Upload date:
- Size: 769.7 kB
- Tags: CPython 3.13, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.9.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3751da726e938a23ea7587046e49f998d223670618f8ae2c96b08c45ca81ea32
|
|
| MD5 |
7cf53466c787dfd5bfc77c60e5a2f7ad
|
|
| BLAKE2b-256 |
b90572fa73fa283a119437b4cdef74545d193cfe092d5fe71409f84a8acb4f5f
|
File details
Details for the file fraiseql-1.3.0-cp313-cp313-manylinux_2_34_x86_64.whl.
File metadata
- Download URL: fraiseql-1.3.0-cp313-cp313-manylinux_2_34_x86_64.whl
- Upload date:
- Size: 865.0 kB
- Tags: CPython 3.13, manylinux: glibc 2.34+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.9.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f522c61679511db537d95c32a5c680133dce9bc2c9760e94831fa50155c8a307
|
|
| MD5 |
e3a61e26c7701897dc25a47f0258baf4
|
|
| BLAKE2b-256 |
4db62cb1909fd1414a4108134e0dbae48deb78437cc43645134b7bdb59798648
|
File details
Details for the file fraiseql-1.3.0-cp313-cp313-macosx_11_0_arm64.whl.
File metadata
- Download URL: fraiseql-1.3.0-cp313-cp313-macosx_11_0_arm64.whl
- Upload date:
- Size: 827.3 kB
- Tags: CPython 3.13, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.9.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f055c95abb0ed80297d79df9fc496bfd3a169b51351b3d62717f302c424afc64
|
|
| MD5 |
4dd697b037cb47b4de90ca286e6b41bd
|
|
| BLAKE2b-256 |
7c5a3cc0de978e06e2b712cd1068e88f08e5f43ba337c5ad01951d6c88fd7463
|