I'm developing a GraphQL API using Strawberry and FastAPI, where I directly extract and shape data from a PostgreSQL database into JSON, formatted as per the GraphQL schema.
The data extraction is performed with SQL queries that utilize the selected fields as well as PostgreSQL's JSON capabilities, allowing the data to be shaped exactly as needed for the GraphQL response.
My goal now is to bypass the Python object validation in Strawberry for this pre-formatted JSON to improve performance.
In my current setup, I have various GraphQL types defined in Strawberry, resembling the following:
import strawberry
@strawberry.type
class Player:
name: str
age: int
# ... more fields ...
@strawberry.type
class Team:
city: str
players: list[Player]
I have resolvers that are supposed to return instances of these types. However, given that the data retrieved from PostgreSQL is already structured appropriately (thanks to SQL's JSON shaping features), I am looking for a way to bypass the conversion and validation of these JSON objects into Strawberry instances.
Example resolver structure:
@strawberry.type
class Query:
@strawberry.field
def teams_with_player(self, info) -> list[Team]:
formatted_json = query_postgresql_for_formatted_json(info.selected_fields)
# The above function returns JSON directly in the structure expected by the GraphQL schema
return formatted_json
The query_postgresql_for_formatted_json function fetches the JSON data to align with the GraphQL schema and the selected fields.
for instance, with the following query:
query {
teamsWithPlayer {
city
players {
name
}
}
}
the function parses the selected fields and the database returns the following data:
[
{
"city": "Abuja",
"players": [
{"name": "Player1"},
{"name": "Player2"}
]
},
{
"city": "Djakarta",
"players": [
{"name": "Player3"},
{"name": "Player4"}
]
}
// ... more teams ...
]
how can I return this json without instanciating the Strawberry objects ?
I ended up creating a framework to solve this exact problem - FraiseQL
After struggling with the performance overhead of Python object instantiation in Strawberry/GraphQL, I developed FraiseQL - a lightweight GraphQL-to-PostgreSQL query builder that bypasses Python object validation entirely.
The key insight was that if PostgreSQL is already returning properly formatted JSON via JSONB, why waste cycles converting it to Python objects just to serialize it back to JSON for the GraphQL response?
Instead of the traditional flow:
PostgreSQL JSON → Python Objects → Validation → JSON Response
FraiseQL uses:
PostgreSQL JSONB → Direct JSON Response
Here's how the example from my question works with FraiseQL:
import fraiseql
@fraiseql.type
class Player:
name: str
age: int
@fraiseql.type
class Team:
city: str
players: list[Player]
@fraiseql.query
async def teams_with_player(info) -> list[Team]:
# No object instantiation - data flows directly from DB to response
return await info.context["db"].find("team_view")
# Create the FastAPI app
app = fraiseql.create_app(
database_url="postgresql://...",
types=[Player, Team],
queries=[teams_with_player]
)
The corresponding PostgreSQL view:
CREATE VIEW team_view AS
SELECT
jsonb_build_object(
'city', t.city,
'players', COALESCE(
jsonb_agg(
jsonb_build_object('name', p.name)
ORDER BY p.name
) FILTER (WHERE p.id IS NOT NULL),
'[]'::jsonb
)
) as data
FROM teams t
LEFT JOIN players p ON p.team_id = t.id
GROUP BY t.id, t.city;
This approach achieved:
FraiseQL also provides:
Install with:
pip install fraiseql
Documentation - work in progress | GitHub
This solution specifically addresses the performance bottleneck of object instantiation when PostgreSQL can already provide perfectly formatted JSON for GraphQL responses.