strawberry
strawberry copied to clipboard
Add Query Batching Support
Add Query Batching Support
Description
This PR adds Query batching support for GraphQL APIs using Strawberry GraphQL.
This makes your GraphQL API compatible with batching features supported by various client side libraries, such as Apollo GraphQL and Relay.
Example (FastAPI):
import strawberry
from fastapi import FastAPI
from strawberry.fastapi import GraphQLRouter
from strawberry.schema.config import StrawberryConfig
@strawberry.type
class Query:
@strawberry.field
def hello(self) -> str:
return "Hello World"
schema = strawberry.Schema(
Query, config=StrawberryConfig(batching_config={"enabled": True, "share_context": True})
)
graphql_app = GraphQLRouter(schema)
app = FastAPI()
app.include_router(graphql_app, prefix="/graphql")
Example (Flask):
import strawberry
from flask import Flask
from strawberry.flask.views import GraphQLView
app = Flask(__name__)
@strawberry.type
class Query:
@strawberry.field
def hello(self) -> str:
return "Hello World"
schema = strawberry.Schema(
Query, config=StrawberryConfig(batching_config={"enabled": True, "share_context": True})
)
app.add_url_rule(
"/graphql",
view_func=GraphQLView.as_view("graphql_view", schema=schema),
)
if __name__ == "__main__":
app.run()
Note: Query Batching is not supported for multipart subscriptions
Types of Changes
- [ ] Core
- [ ] Bugfix
- [x] New feature
- [ ] Enhancement/optimization
- [ ] Documentation
Issues Fixed or Closed by This PR
- https://github.com/strawberry-graphql/strawberry/issues/1383
Checklist
- [x] My code follows the code style of this project.
- [x] My change requires a change to the documentation.
- [x] I have updated the documentation accordingly.
- [x] I have read the CONTRIBUTING document.
- [x] I have added tests to cover my changes.
- [x] I have tested the changes and verified that they work and don't break anything (as well as I can manage).
Summary by Sourcery
Add support for batching GraphQL queries across all supported frameworks.
New Features:
- This change introduces support for batching GraphQL queries, enabling clients to send multiple queries in a single request and receive corresponding responses. This is compatible with client-side libraries like Apollo and Relay.
Tests:
- Added tests to verify query batching functionality and ensure compatibility with existing features.
Reviewer's Guide by Sourcery
This pull request introduces batch query support for GraphQL APIs, enabling clients to send multiple queries in a single request. This is implemented by adding a "batch" parameter to the view classes and modifying the request handling logic to process lists of queries. Batching is NOT supported for multipart subscriptions.
Sequence diagram for GraphQL batch query processing
sequenceDiagram
participant Client
participant GraphQLView
participant Schema
Client->>GraphQLView: POST /graphql/batch
Note over Client,GraphQLView: Request with multiple queries
GraphQLView->>GraphQLView: parse_http_body()
GraphQLView->>GraphQLView: validate batch mode
loop For each query in batch
GraphQLView->>Schema: execute_single(query)
Schema-->>GraphQLView: execution result
GraphQLView->>GraphQLView: process_result()
end
GraphQLView-->>Client: Combined response array
Class diagram showing GraphQL view modifications for batch support
classDiagram
class AsyncBaseHTTPView {
+bool batch
+execute_operation()
+execute_single()
+parse_http_body()
+create_response()
}
class SyncBaseHTTPView {
+bool batch
+execute_operation()
+execute_single()
+parse_http_body()
+create_response()
}
class GraphQLRequestData {
+str query
+dict variables
+str operation_name
+str protocol
}
AsyncBaseHTTPView ..> GraphQLRequestData
SyncBaseHTTPView ..> GraphQLRequestData
note for AsyncBaseHTTPView "Added batch support"
note for SyncBaseHTTPView "Added batch support"
Flow diagram for batch query request handling
flowchart TD
A[Client Request] --> B{Is Batch Request?}
B -->|Yes| C[Validate Batch Mode]
B -->|No| D[Process Single Query]
C --> E{Batch Enabled?}
E -->|No| F[Return 400 Error]
E -->|Yes| G[Process Multiple Queries]
G --> H[Execute Each Query]
H --> I[Combine Results]
I --> J[Return Response]
D --> J
File-Level Changes
| Change | Details | Files |
|---|---|---|
Added batch parameter to view classes |
|
strawberry/http/async_base_view.pystrawberry/http/sync_base_view.pystrawberry/channels/handlers/http_handler.pystrawberry/flask/views.pystrawberry/asgi/__init__.pystrawberry/fastapi/router.pystrawberry/litestar/controller.pystrawberry/quart/views.pystrawberry/sanic/views.pystrawberry/aiohttp/views.pystrawberry/chalice/views.pystrawberry/django/views.pytests/http/clients/channels.pytests/http/clients/django.pytests/http/clients/aiohttp.pytests/http/clients/asgi.pytests/http/clients/async_flask.pytests/http/clients/chalice.pytests/http/clients/fastapi.pytests/http/clients/flask.pytests/http/clients/litestar.pytests/http/clients/quart.pytests/http/clients/sanic.pytests/http/clients/async_django.py |
| Modified request handling logic to support batch queries |
|
strawberry/http/async_base_view.pystrawberry/http/sync_base_view.pystrawberry/asgi/__init__.pystrawberry/flask/views.pystrawberry/fastapi/router.pystrawberry/litestar/controller.pystrawberry/quart/views.pystrawberry/sanic/views.pystrawberry/aiohttp/views.pystrawberry/chalice/views.pystrawberry/django/views.py |
| Added tests for batch query functionality |
|
tests/http/test_query_batching.py |
| Updated client implementations for batch query testing |
|
tests/http/clients/base.pytests/http/clients/channels.py |
| Added a new parameter to HTTPClient implementations for enabling batch queries |
|
tests/http/clients/django.pytests/http/clients/aiohttp.pytests/http/clients/asgi.pytests/http/clients/async_flask.pytests/http/clients/chalice.pytests/http/clients/fastapi.pytests/http/clients/flask.pytests/http/clients/litestar.pytests/http/clients/quart.pytests/http/clients/sanic.pytests/http/clients/async_django.py |
Possibly linked issues
- #1383: The PR addresses the issue by adding query batching support.
Tips and commands
Interacting with Sourcery
- Trigger a new review: Comment
@sourcery-ai reviewon the pull request. - Continue discussions: Reply directly to Sourcery's review comments.
- Generate a GitHub issue from a review comment: Ask Sourcery to create an issue from a review comment by replying to it.
- Generate a pull request title: Write
@sourcery-aianywhere in the pull request title to generate a title at any time. - Generate a pull request summary: Write
@sourcery-ai summaryanywhere in the pull request body to generate a PR summary at any time. You can also use this command to specify where the summary should be inserted.
Customizing Your Experience
Access your dashboard to:
- Enable or disable review features such as the Sourcery-generated pull request summary, the reviewer's guide, and others.
- Change the review language.
- Add, remove or edit custom review instructions.
- Adjust other review settings.
Getting Help
- Contact our support team for questions or feedback.
- Visit our documentation for detailed guides and information.
- Keep in touch with the Sourcery team by following us on X/Twitter, LinkedIn or GitHub.
Thanks for adding the RELEASE.md file!

Here's a preview of the changelog:
Add GraphQL Query batching support
GraphQL query batching is now supported across all frameworks (sync and async)
To enable query batching, add a valid batching_config to the schema configuration.
This makes your GraphQL API compatible with batching features supported by various client side libraries, such as Apollo GraphQL and Relay.
Example (FastAPI):
import strawberry
from fastapi import FastAPI
from strawberry.fastapi import GraphQLRouter
from strawberry.schema.config import StrawberryConfig
@strawberry.type
class Query:
@strawberry.field
def hello(self) -> str:
return "Hello World"
schema = strawberry.Schema(
Query, config=StrawberryConfig(batching_config={"max_operations": 10})
)
graphql_app = GraphQLRouter(schema)
app = FastAPI()
app.include_router(graphql_app, prefix="/graphql")
Example (Flask):
import strawberry
from flask import Flask
from strawberry.flask.views import GraphQLView
app = Flask(__name__)
@strawberry.type
class Query:
@strawberry.field
def hello(self) -> str:
return "Hello World"
schema = strawberry.Schema(
Query, config=StrawberryConfig(batching_config={"max_operations": 10})
)
app.add_url_rule(
"/graphql/batch",
view_func=GraphQLView.as_view("graphql_view", schema=schema),
)
if __name__ == "__main__":
app.run()
Note: Query Batching is not supported for multipart subscriptions
Here's the tweet text:
🆕 Release (next) is out! Thanks to @aryaniyaps for the PR 👏
GraphQL Query Batching support is here! 🍓 ✨ and it works across all frameworks (sync/async)!
Get it here 👉 https://strawberry.rocks/release/(next)
Codecov Report
Attention: Patch coverage is 96.46018% with 4 lines in your changes missing coverage. Please review.
Project coverage is 94.41%. Comparing base (
741b0bc) to head (fa8ac60). Report is 3 commits behind head on main.
Additional details and impacted files
@@ Coverage Diff @@
## main #3755 +/- ##
========================================
Coverage 94.40% 94.41%
========================================
Files 527 528 +1
Lines 34230 34342 +112
Branches 1785 1803 +18
========================================
+ Hits 32316 32423 +107
- Misses 1625 1627 +2
- Partials 289 292 +3
:rocket: New features to boost your workflow:
- :snowflake: Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
- :package: JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
CodSpeed Performance Report
Merging #3755 will not alter performance
Comparing aryaniyaps:add-batching (fa8ac60) with main (5510bf1)
Summary
✅ 26 untouched benchmarks
@sourcery-ai review
hello everyone, since this has been a blocker for quite some time, I've updated the code to raise ValueErrors when context sharing is disabled. Could this PR be merged to add batching support with context sharing?
I'll open a new PR for disabling context sharing!
@bellini666 let's chat about this at PyCon!
adding notes from the discussion with Patrick today (I might forget this):
- it might be beneficial to add custom request data to the execution context.
- right now only the query and variables are set in the context, but adding the request body will be helpful for schema extensions when batching is enabled
Here's an example, in case the above text wasn't clear:
In a persisted queries extension, we get the request body like this:
class PersistedQueriesExtension(SchemaExtension):
def __init__(self, *, persisted_queries_path: Path) -> None:
self.cache: dict[str, str] = {}
with Path.open(persisted_queries_path, "r") as f:
self.cache = json.load(f)
async def on_operation(self) -> AsyncIterator[None]:
body = await self.execution_context.context.get("request").json()
document_id = body.get("document_id")
persisted_query = self.cache.get(document_id)
but when batching is enabled, there would be no way to get the right individual request data from the total list of queries passed in.
example:
class PersistedQueriesExtension(SchemaExtension):
def __init__(self, *, persisted_queries_path: Path) -> None:
self.cache: dict[str, str] = {}
with Path.open(persisted_queries_path, "r") as f:
self.cache = json.load(f)
async def on_operation(self) -> AsyncIterator[None]:
body = await self.execution_context.context.get("request").json()
document_ids = body.get("document_ids")
# What is the right document ID for this particular query here? there's no way to get it from the list of document IDs...
# might be beneficial to set something like:
# custom_request_data = self.execution_context.custom_request_data
# document_id = custom_request_data.get("document_id")
This should probably be a separate PR