Skip to content

Direct Path Implementation

Status: ✅ IMPLEMENTED AND WORKING

Overview

The direct path provides maximum performance by bypassing GraphQL resolvers entirely:

GraphQL Query → FastAPI → Parser → SQL → JSONB → Rust → HTTP

Pipeline Components

1. GraphQL Query Parser

Location: src/fraiseql/core/direct_query_parser.py

Extracts essential information from GraphQL queries: - Field name (e.g., user, users) - Arguments (e.g., id, where, limit, offset) - Field paths for projection (e.g., [["id"], ["firstName"], ["email"]])

Example:

parse_graphql_query_simple('query { user(id: "123") { id firstName } }')
# Returns:
{
    "field_name": "user",
    "arguments": {"id": "123"},
    "field_paths": [["id"], ["firstName"]]
}

2. Direct Path Router

Location: src/fraiseql/fastapi/routers.py (lines 315-381)

Intercepts GraphQL requests and routes them through the direct path:

# 1. Parse GraphQL query
parsed = parse_graphql_query_simple(request.query)

# 2. Determine entity and view
entity_name = field_name.rstrip("s")  # "users" → "user"
view_name = f"v_{entity_name}"        # → "v_user"

# 3. Build SQL query (WHERE/LIMIT/ORDER BY)
query = db._build_find_query(
    view_name=view_name,
    field_paths=None,  # Rust does projection
    jsonb_column="data",
    **arguments
)

# 4. Execute via Rust pipeline
result_bytes = await execute_via_rust_pipeline(
    conn=conn,
    query=query.statement,
    params=query.params,
    field_name=field_name,
    type_name=type_name,
    is_list=is_list,
    field_paths=field_paths,
)

# 5. Return bytes directly to HTTP
return Response(content=bytes(result_bytes), media_type="application/json")

3. SQL Generation

Location: src/fraiseql/db.py

Generates optimized SQL for JSONB tables:

-- Single object query
SELECT data::text FROM v_user WHERE id = '123' LIMIT 1

-- List query with WHERE
SELECT data::text FROM v_user WHERE data->>'active' = 'true' LIMIT 10

-- List query with complex WHERE
SELECT data::text FROM v_user
WHERE data->>'role' = 'admin' AND data->>'active' = 'true'
ORDER BY data->>'created_at' DESC
LIMIT 10 OFFSET 20

Key Enhancement: Added jsonb_column parameter to WHERE clause builders to use JSONB path operators (data->>'field') instead of column names.

4. Rust Transformation

Location: Rust binary (fraiseql-rs)

Processes JSONB data and returns complete GraphQL response: - Field projection: Filters to requested fields only - camelCase conversion: first_namefirstName - __typename injection: Adds GraphQL type information - Response wrapping: Wraps in {"data": {"user": {...}}}

Zero-copy: Returns bytes directly without Python JSON parsing.

Trinity Pattern

The direct path respects the trinity pattern:

  • Table: tv_{entity} (projection table) - stores id + JSONB data
  • View: v_{entity} (view) - selects id, data FROM tv_{entity}
  • Type: {Entity} (GraphQL type) - Python class with @fraiseql_type

Example:

CREATE TABLE tv_user (
    id UUID PRIMARY KEY,
    data JSONB NOT NULL
);

CREATE VIEW v_user AS SELECT id, data FROM tv_user;

import fraiseql

@type(sql_source="v_user", jsonb_column="data")
class User:
    id: str
    first_name: str
    email: str

Performance Benefits

The direct path eliminates: - ❌ GraphQL resolver overhead - ❌ Python JSON parsing - ❌ Python field extraction - ❌ Python object creation - ❌ GraphQL serialization

Result: 3-4x faster for simple queries

Supported Features

Single object queries: user(id: "123") { ... }List queries: users(limit: 10) { ... }Field projection: Rust filters to requested fields ✅ WHERE filters: users(where: {active: {eq: true}}) { ... }Pagination: limit, offset parameters ✅ Filtering by ID: user(id: "123")

Test Coverage

Location: tests/integration/graphql/test_graphql_query_execution_complete.py

  • test_graphql_simple_query_returns_data - Single object queries
  • test_graphql_list_query_returns_array - List queries
  • test_graphql_field_selection - Field projection
  • test_graphql_with_where_filter - WHERE clause filtering

All tests passing

Fallback Behavior

If the direct path fails (e.g., complex nested queries), it automatically falls back to traditional GraphQL execution:

try:
    # Direct path...
    return Response(content=bytes(result_bytes), media_type="application/json")
except Exception as e:
    logger.warning(f"Direct path failed, falling back to GraphQL: {e}")
    # Continue to traditional GraphQL execution

This ensures 100% compatibility with all GraphQL features while providing performance benefits where possible.

Future Enhancements

  • [ ] ORDER BY support (currently uses default ordering)
  • [ ] Nested relationship queries
  • [ ] Mutations via direct path
  • [ ] Query complexity analysis for smart routing
  • Parser: src/fraiseql/core/direct_query_parser.py
  • Router: src/fraiseql/fastapi/routers.py (lines 315-381)
  • SQL Builder: src/fraiseql/db.py (WHERE clause enhancements)
  • Tests: tests/integration/graphql/test_graphql_query_execution_complete.py
  • Unit Tests: tests/unit/core/test_direct_query_parser.py