FraiseQL

π You are here: Main FraiseQL Framework (v1.8.3) - Stable Release
Current Version: v1.8.3 | Status: Stable | Python: 3.13+ | PostgreSQL: 13+
GraphQL for the LLM era. Simple. Powerful. Rust-fast.
PostgreSQL returns JSONB. Rust transforms it. Zero Python overhead.
from fraiseql import type, query
from fraiseql.fastapi import create_fraiseql_app
@fraiseql.type(sql_source="v_user", jsonb_column="data")
class User:
id: int
name: str
email: str
@fraiseql.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
- π GraphQL Cascade - Automatic cache updates and side effect tracking
- β¨ Auto-populated mutations - status, message, errors handled automatically (50-60% less boilerplate)
- π― Auto-wired query params -
where, orderBy, limit, offset added automatically to list queries
- π Advanced filtering - Full-text search, JSONB queries, array operations, regex
- π§ Vector search - pgvector integration for semantic search, RAG, recommendations (6 distance operators)
π€ 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:
user = db.query(User).first()
user_dict = user.__dict__
json_str = json.dumps(user_dict)
SELECT data FROM v_user LIMIT 1
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
class User(Base):
id = Column(Integer, primary_key=True)
email = Column(String)
password_hash = Column(String)
is_admin = Column(Boolean)
api_key = Column(String)
@strawberry.type
class UserType:
id: int
email: str
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
CREATE VIEW v_user AS
SELECT
id,
jsonb_build_object(
'id', id,
'email', email
) as data
FROM tb_user;
@type(sql_source="v_user", jsonb_column="data")
class User:
id: int
email: str
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:
query {
user(id: 1) {
posts {
author {
posts {
author {
posts {
}
}
}
}
}
}
}
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:
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
))
FROM tb_post p
WHERE p.user_id = tb_user.id
LIMIT 100
)
) as data
FROM tb_user;
What happens when attacker tries deep query:
query {
user {
posts {
author {
}
}
}
}
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.
π Security Features
FraiseQL includes enterprise-grade security features designed for global regulatory compliance and production deployment:
π Software Bill of Materials (SBOM)
- Automated generation via
fraiseql sbom generate
- Global compliance: US EO 14028, EU NIS2/CRA, PCI-DSS 4.0, ISO 27001
- CycloneDX 1.5 format with cryptographic signing
- CI/CD integration for continuous compliance
π Key Management Service (KMS)
- HashiCorp Vault: Production-ready with transit engine
- AWS KMS: Native integration with GenerateDataKey
- GCP Cloud KMS: Envelope encryption support
- Local Provider: Development-only with warnings
π‘οΈ Security Profiles
STANDARD: Default protections for general applications
REGULATED: PCI-DSS/HIPAA/SOC 2 compliance
RESTRICTED: Government, defence, critical infrastructure
- πΊπΈ FedRAMP, DoD, NIST 800-53
- πͺπΊ NIS2 Essential Entities, EU CRA
- π¨π¦ CPCSC (defence contractors)
- π¦πΊ Essential Eight Level 3
- πΈπ¬ Singapore CII operators
π Observability
- OpenTelemetry tracing with sensitive data sanitization
- Security event logging
- Audit trail support
π Advanced Security Controls
- Rate limiting for API endpoints and GraphQL operations
- CSRF protection for mutations and forms
- Security headers middleware for defense in depth
- Input validation and sanitization
- Field-level authorization with role inheritance
- Row-level security via PostgreSQL RLS
π Security Configuration β’ π Global Compliance Guide β’ π KMS Architecture
π€ 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
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 INTO tb_user (email, name)
VALUES (p_email, p_name)
RETURNING id INTO v_user_id;
INSERT INTO audit_log (action, details, timestamp)
VALUES ('user_created', jsonb_build_object('user_id', v_user_id), NOW());
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
name: str
@success
class UserCreated:
user_id: str
message: str
@error
class ValidationError:
error: str
code: str = "VALIDATION_ERROR"
@fraiseql.mutation(function="fn_create_user", schema="public")
class CreateUser:
input: CreateUserInput
success: UserCreated
failure: 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:
β¨ 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
@fraiseql.type(sql_source="v_note", jsonb_column="data")
class Note:
id: UUID
title: str
content: str | None
@fraiseql.query
async def notes(info) -> list[Note]:
"""Get all notes."""
db = info.context["db"]
return await db.find("v_note")
@fraiseql.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)
@input
class CreateNoteInput:
title: str
content: str | None = None
@fraiseql.mutation
class CreateNote:
input: CreateNoteInput
success: Note
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
CREATE VIEW v_user AS
SELECT
id,
jsonb_build_object(
'id', id,
'name', name,
'email', email,
) as data
FROM tb_user;
@fraiseql.type(sql_source="v_user", jsonb_column="data")
class User:
id: int
name: str
email: str
posts: list[Post]
@fraiseql.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 UUID) RETURNS JSONB AS $$
DECLARE
v_post RECORD;
BEGIN
SELECT p.*, u.email as user_email
INTO v_post
FROM tb_post p
JOIN tb_user u ON p.fk_user = u.pk_user
WHERE p.id = p_post_id;
IF NOT FOUND THEN
RETURN jsonb_build_object('success', false, 'error', 'Post not found');
END IF;
IF v_post.published_at IS NOT NULL THEN
RETURN jsonb_build_object('success', false, 'error', 'Post already published');
END IF;
UPDATE tb_post
SET published_at = NOW()
WHERE id = p_post_id;
PERFORM fn_sync_tv_post(p_post_id);
INSERT INTO audit_log (action, details)
VALUES ('post_published', jsonb_build_object('post_id', p_post_id, 'user_email', v_post.user_email));
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.
Selective CASCADE Querying
Request only the CASCADE data you need:
mutation CreatePost($input: CreatePostInput!) {
createPost(input: $input) {
post { id title }
cascade {
metadata { affectedCount }
}
cascade {
updated { __typename id entity }
deleted { __typename id }
invalidations { queryName }
metadata { affectedCount }
}
}
}
Performance: Not requesting CASCADE reduces response size by 2-10x.
π° In PostgreSQL Everything
Replace 4 services with 1 database.
Cost Savings Calculator
| 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)
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={...})
Observability (Replaces APM)
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 computation
tv_* - Denormalized tables with generated JSONB columns (for complex queries)
Mutations use functions:
fn_* - Business logic, validation, side effects
tb_* - 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_*)
CREATE TABLE tv_user (
id INT PRIMARY KEY,
data JSONB NOT NULL,
updated_at TIMESTAMPTZ DEFAULT NOW()
);
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;
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);
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
| 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
config = FraiseQLConfig(apq_storage_backend="memory")
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
β‘ APQ Details
Specialized Type System
Advanced operators for network types, hierarchical data, ranges, and nested arrays:
query {
servers(where: {
ipAddress: { eq: "192.168.1.1" }
port: { gt: 1024 }
location: { ancestor_of: "US.CA" }
dateRange: { overlaps: "[2024-01-01,2024-12-31)" }
printServers(where: {
AND: [
{ operatingSystem: { in: ["Linux", "Windows"] } }
{ OR: [
{ nTotalAllocations: { gte: 100 } }
{ NOT: { ipAddress: { isnull: true } } }
]
}
]
}) {
hostname operatingSystem
}
}) {
id name ipAddress port
}
}
50+ Specialized Scalar Types:
Financial & Trading:
- CUSIP, ISIN, SEDOL, MIC, LEI - Security identifiers
- Money, Percentage, ExchangeRate - Financial values
- CurrencyCode, StockSymbol - Trading symbols
Network & Infrastructure:
- IPv4, IPv6, CIDR, MACAddress - Network addresses with subnet operations
- Hostname, DomainName, Port, EmailAddress - Internet identifiers
- APIKey, HashSHA256 - Security tokens
Geospatial & Location:
- Coordinate, Latitude, Longitude - Geographic coordinates with distance calculations
- PostalCode, Timezone - Location data
Business & Logistics:
- ContainerNumber, FlightNumber, TrackingNumber, VIN - Asset identifiers
- IBAN, LicensePlate - Financial & vehicle identifiers
- PhoneNumber, LocaleCode, LanguageCode - Contact & localization
Technical & Data:
- UUID, JSON, Date, DateTime, Time, DateRange - Standard types with validation
- LTree - Hierarchical data with ancestor/descendant queries
- SemanticVersion, Color, MIMEType, File, Image - Specialized formats
- HTML, Markdown - Rich text content
Advanced Filtering: Full-text search, JSONB queries, array operations, regex, vector similarity search on all types
Scalar Type Usage Examples
from fraiseql import type
from fraiseql.types import (
EmailAddress, PhoneNumber, Money, Percentage,
CUSIP, ISIN, IPv4, MACAddress, LTree, DateRange
)
@fraiseql.type(sql_source="v_financial_data")
class FinancialRecord:
id: int
email: EmailAddress
phone: PhoneNumber
balance: Money
margin: Percentage
security_id: CUSIP | ISIN
@fraiseql.type(sql_source="v_network_devices")
class NetworkDevice:
id: int
ip_address: IPv4
mac_address: MACAddress
location: LTree
maintenance_window: DateRange
query {
financialRecords(where: {
balance: { gte: "1000.00" }
margin: { between: ["5.0", "15.0"] }
security_id: { eq: "037833100" }
}) {
id balance margin security_id
}
networkDevices(where: {
ip_address: { inSubnet: "192.168.1.0/24" }
location: { ancestor_of: "US.CA.SF" }
maintenance_window: { overlaps: "[2024-01-01,2024-12-31)" }
}) {
id ip_address location
}
}
π Nested Array Filtering Guide
Enterprise Security
from fraiseql import authorized
@fraiseql.authorized(roles=["admin", "editor"])
@fraiseql.mutation
class DeletePost:
"""Only admins and editors can delete posts."""
input: DeletePostInput
success: DeleteSuccess
failure: PermissionDenied
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)
"""
id: UUID
identifier: str | None
title: str
content: str
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
pip install fraiseql
fraiseql init my-api
cd my-api
createdb my_api
psql my_api < schema.sql
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
fraiseql init <name>
fraiseql dev
fraiseql check
fraiseql generate schema
fraiseql generate types
fraiseql sql analyze <query>
fraiseql sql explain <query>
π Learn More
π€ Contributing
We welcome contributions! See CONTRIBUTING.md for:
- Development setup and testing
- Architecture decisions and patterns
- Code style and review process
Quick Start
git clone https://github.com/fraiseql/fraiseql
cd fraiseql && make setup-dev
Pre-commit with prek (7-10x faster)
FraiseQL uses prek - a Rust-based replacement for pre-commit:
brew install j178/tap/prek
cargo install prek
prek install
prek run --all
Why prek? β‘ 7-10x faster than pre-commit, single binary, zero Python dependencies.
For more details: See .claude/CLAUDE.md or run make prek-list
π 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:
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
| v1.8.0-beta.5 | Root level | Beta | Security hardening + documentation improvements | β
Production Ready |
| Rust Pipeline | fraiseql_rs/ | Integrated | Included in v1.0+ | β
Stable |
| v1.7.2 | Superseded | Legacy | Use v1.8.0-beta.5 or wait for v1.8.0 stable | β οΈ Upgrade |
New to FraiseQL? β First Hour Guide β’ Project Structure
π Complete Version Roadmap
Ready to build the most efficient GraphQL API in Python?
pip install fraiseql && fraiseql init my-api
π PostgreSQL β Rust β Production