
Research
2025 Report: Destructive Malware in Open Source Packages
Destructive malware is rising across open source registries, using delays and kill switches to wipe code, break builds, and disrupt CI/CD.
fraiseql
Advanced tools
GraphQL for the LLM era. Simple. Powerful. Rust-fast. Production-ready GraphQL API framework for PostgreSQL with CQRS, JSONB optimization, and type-safe mutations
π You are here: Main FraiseQL Framework (v1.8.9) - Stable Release
Current Version: v1.8.9 | Status: Stable | Python: 3.13+ | PostgreSQL: 13+
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
@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?
where, orderBy, limit, offset added automatically to list queriesFraiseQL is for production teams building high-performance GraphQL APIs with PostgreSQL.
See detailed audience guide for complete user profiles.
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 βββββββββ―
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:
Security benefits:
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.
Traditional ORM-based frameworks have inherent security risks:
# 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:
-- 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:
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:
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:
Result: Attackers cannot exceed the depth you define in views. No middleware needed.
FraiseQL includes enterprise-grade security features designed for global regulatory compliance and production deployment:
fraiseql sbom generateSTANDARD: Default protections for general applicationsREGULATED: PCI-DSS/HIPAA/SOC 2 complianceRESTRICTED: Government, defence, critical infrastructure
π Security Configuration β’ π Global Compliance Guide β’ π KMS Architecture
FraiseQL is the first GraphQL framework designed for the LLM era.
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.
@input
class CreateUserInput:
email: str # AI sees exact input structure
name: str
@success
class UserCreated:
user_id: str # AI sees success response
message: str
@error
class ValidationError:
error: str # AI sees failure cases
code: str = "VALIDATION_ERROR"
@fraiseql.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
Real Impact: Claude Code, GitHub Copilot, and ChatGPT generate correct FraiseQL code on first try.
New to FraiseQL? Understanding these core concepts will help you make the most of the framework:
π Concepts & Glossary - Essential terminology and mental models:
pk_*, id, identifier) for performance and UXtv_*) for complex queriesQuick links:
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
@fraiseql.type(sql_source="v_note", jsonb_column="data")
class Note:
id: UUID
title: str
content: str | None
# Step 2: Define queries
@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)
# Step 3: Define mutations
@input
class CreateNoteInput:
title: str
content: str | None = None
@fraiseql.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.
-- PostgreSQL view explicitly defines what's exposed
CREATE VIEW v_user AS
SELECT
id,
jsonb_build_object(
'id', id,
'name', name,
'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
@fraiseql.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
@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.
CREATE OR REPLACE FUNCTION fn_publish_post(p_post_id UUID) RETURNS JSONB AS $$
DECLARE
v_post RECORD;
BEGIN
-- Get post with user info (Trinity pattern: JOIN on pk_user)
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 -- β
Trinity: INTEGER FK to pk_user
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;
-- Sync projection table
PERFORM fn_sync_tv_post(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.
Request only the CASCADE data you need:
mutation CreatePost($input: CreatePostInput!) {
createPost(input: $input) {
post { id title }
# Option 1: No CASCADE (smallest payload)
# Just omit the cascade field
# Option 2: Metadata only
cascade {
metadata { affectedCount }
}
# Option 3: Full CASCADE
cascade {
updated { __typename id entity }
deleted { __typename id }
invalidations { queryName }
metadata { affectedCount }
}
}
}
Performance: Not requesting CASCADE reduces response size by 2-10x.
Replace 4 services with 1 database.
| 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 |
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:
βββββββββββββββββββ ββββββββββββββββββββ βββββββββββββββββββ
β GraphQL β β β PostgreSQL β β β Rust β
β Request β β JSONB Query β β Transform β
β β β β β (7-10x faster)β
βββββββββββββββββββ ββββββββββββββββββββ βββββββββββββββββββ
β
βββββββββββββββββββ
β FastAPI β
β HTTP Response β
βββββββββββββββββββ
Unified path for all queries:
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
1. Exclusive Rust Pipeline
2. JSONB Views
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
| 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 |
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:
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
}
}
50+ Specialized Scalar Types:
Financial & Trading:
Network & Infrastructure:
Geospatial & Location:
Business & Logistics:
Technical & Data:
Advanced Filtering: Full-text search, JSONB queries, array operations, regex, vector similarity search on all types
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 # Validated email addresses
phone: PhoneNumber # International phone numbers
balance: Money # Currency amounts with precision
margin: Percentage # Percentages (0.00-100.00)
security_id: CUSIP | ISIN # Financial instrument identifiers
@fraiseql.type(sql_source="v_network_devices")
class NetworkDevice:
id: int
ip_address: IPv4 # IPv4 addresses with subnet operations
mac_address: MACAddress # MAC addresses with validation
location: LTree # Hierarchical location paths
maintenance_window: DateRange # Date ranges with overlap queries
# Advanced filtering with specialized types
query {
financialRecords(where: {
balance: { gte: "1000.00" } # Money comparison
margin: { between: ["5.0", "15.0"] } # Percentage range
security_id: { eq: "037833100" } # CUSIP validation
}) {
id balance margin security_id
}
networkDevices(where: {
ip_address: { inSubnet: "192.168.1.0/24" } # CIDR operations
location: { ancestor_of: "US.CA.SF" } # LTree hierarchy
maintenance_window: { overlaps: "[2024-01-01,2024-12-31)" }
}) {
id ip_address location
}
}
π Nested Array Filtering Guide
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
# 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
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?
# 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 π
π Detailed Installation Guide - Platform-specific instructions, troubleshooting
# 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
We welcome contributions! See CONTRIBUTING.md for:
git clone https://github.com/fraiseql/fraiseql
cd fraiseql && make setup-dev
FraiseQL uses prek - a Rust-based replacement for pre-commit:
# Install prek (faster than pre-commit)
brew install j178/tap/prek # macOS
cargo install prek # or via Rust
# Setup git hooks
prek install
# Run before committing
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
FraiseQL draws inspiration from:
FraiseQL is created by Lionel Hamayon (@evoludigit), a self-taught developer and founder of Γvolution digitale.
Started: April 2025
I built FraiseQL after discovering something that changed everything: PostgreSQL views returning JSONB could eliminate SQLAlchemy's N+1 query problem and lazy loading chaos.
After years struggling with Django, Flask, FastAPI, and Strawberry GraphQL with SQLAlchemy, I realized the ORM approach was fundamentally flawed. Every relationship meant another query, and even with careful eager loading, the complexity exploded.
Then I tried something simple: What if PostgreSQL composed the entire data structure as JSONB in a single query? No ORM relationships. No lazy loading. No N+1 queries. Just one view that returns everything.
It worked beautifully. But there was still that stupid inefficiency: PostgreSQL returns JSON β Python deserializes to objects β GraphQL serializes back to JSON. Why are we doing this roundtrip?
Skip the ORM. Skip the object mapping. Let PostgreSQL return the JSON directly.
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:
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:
MIT License - see LICENSE for details.
| Version | Location | Status | Purpose | For Users? |
|---|---|---|---|---|
| v1.8.9 | Root level | Stable | Current production release | β Production Ready |
| Rust Pipeline | fraiseql_rs/ | Integrated | Included in v1.8.9+ | β Stable |
New to FraiseQL? β First Hour Guide β’ Project Structure
Ready to build the most efficient GraphQL API in Python?
pip install fraiseql && fraiseql init my-api
π PostgreSQL β Rust β Production
FAQs
GraphQL for the LLM era. Simple. Powerful. Rust-fast. Production-ready GraphQL API framework for PostgreSQL with CQRS, JSONB optimization, and type-safe mutations
We found that fraiseql demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago.Β It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Research
Destructive malware is rising across open source registries, using delays and kill switches to wipe code, break builds, and disrupt CI/CD.

Security News
Socket CTO Ahmad Nassri shares practical AI coding techniques, tools, and team workflows, plus what still feels noisy and why shipping remains human-led.

Research
/Security News
A five-month operation turned 27 npm packages into durable hosting for browser-run lures that mimic document-sharing portals and Microsoft sign-in, targeting 25 organizations across manufacturing, industrial automation, plastics, and healthcare for credential theft.