GraphQL¶
A single, precise question can bring down prey faster than a scattered chase. GraphQL lets clients ask for exactly the fields they need and nothing more, while VelociPy keeps the integration protocol-agnostic and RSGI-friendly.
VelociPy ships optional GraphQL support through strawberry-graphql. Define a schema with Python type hints, mount the endpoint, and add a WebSocket route if you need subscriptions.
Run the examples
See examples/graphql.py
for a runnable demo of queries, mutations, subscriptions, file uploads, and
context injection.
Installation¶
Install the graphql extra to pull in strawberry-graphql:
Quick start¶
import strawberry
from velocipy import VelociPy
from velocipy.constants import Method
from velocipy.features.graphql import GraphQL
@strawberry.type
class Query:
@strawberry.field
def hello(self, name: str = "world") -> str:
return f"Hello, {name}!"
schema = strawberry.Schema(query=Query)
app = VelociPy()
app.mount("/graphql", GraphQL(schema), methods={Method.GET, Method.POST, Method.HEAD})
Run the app as usual:
Queries and mutations¶
Both GET and POST are supported. GET reads query, variables, and
operationName from the query string. POST reads them from a JSON body:
curl -X POST http://localhost:8000/graphql \
-H "Content-Type: application/json" \
-d '{"query": "{ hello(name: \"VelociPy\") }"}'
Mutations work the same way:
@strawberry.type
class Mutation:
@strawberry.mutation
def create_user(self, name: str) -> str:
return f"Created {name}"
schema = strawberry.Schema(query=Query, mutation=Mutation)
GraphiQL¶
A minimal GraphiQL IDE is served on GET /graphql when graphiql=True
(default) and the request prefers text/html:
from velocipy.constants import Method
app.mount(
"/graphql",
GraphQL(schema, graphiql=True),
methods={Method.GET, Method.POST, Method.HEAD},
)
Disable it in production if you do not want to expose the playground:
app.mount(
"/graphql",
GraphQL(schema, graphiql=False),
methods={Method.GET, Method.POST, Method.HEAD},
)
Subscriptions¶
Subscriptions use WebSocket and the graphql-transport-ws subprotocol by
default. Mount the HTTP endpoint and register a separate WebSocket handler:
@strawberry.type
class Subscription:
@strawberry.subscription
async def count(self, limit: int = 5) -> int:
for value in range(limit):
yield value
schema = strawberry.Schema(query=Query, mutation=Mutation, subscription=Subscription)
graphql_app = GraphQL(schema)
app = VelociPy()
app.mount(
"/graphql", graphql_app, methods={Method.GET, Method.POST, Method.HEAD}
)
app.add_websocket_route("/graphql", graphql_app.websocket_handler)
The same WebSocket connection can also execute queries and mutations (single-result operations).
Context injection¶
Provide a static context value or an async callable that receives the request (for HTTP) or WebSocket (for subscriptions):
from velocipy.http.requests import Request
async def get_context(request: Request) -> dict[str, object]:
return {"request": request, "db": request.state["db"]}
graphql_app = GraphQL(schema, context=get_context)
For subscriptions, the callable receives the :class:WebSocket object and
Strawberry injects the connectionParams from the client's
connection_init message into the same context under the
connection_params key.
File uploads¶
Uploads use Strawberry's Upload scalar and the GraphQL multipart request
spec. The python-multipart extra is required.
from velocipy import Upload
@strawberry.type
class Mutation:
@strawberry.mutation
def upload(self, file: Upload) -> str:
return file.filename
Uploads are enabled by default. Disable them with
multipart_uploads_enabled=False.
WebSocket protocols¶
By default both the recommended graphql-transport-ws and the legacy
graphql-ws subprotocols are accepted. Restrict them if your clients only
support one:
from strawberry.subscriptions import GRAPHQL_TRANSPORT_WS_PROTOCOL
graphql_app = GraphQL(schema, subscription_protocols=[GRAPHQL_TRANSPORT_WS_PROTOCOL])
OpenAPI¶
The mounted GraphQL routes are registered in the OpenAPI schema so the
endpoint is discoverable. The detailed schema introspection is provided by
GraphQL itself at /graphql.
At a glance¶
| Setting | What it does |
|---|---|
GraphQL(schema) |
Wraps a Strawberry schema as an ASGI/RSGI app. |
app.mount("/graphql", ..., methods={...}) |
Exposes queries and mutations over HTTP. |
GraphQL(schema, graphiql=True) |
Serves the GraphiQL IDE on GET /graphql. |
app.add_websocket_route(..., graphql_app.websocket_handler) |
Enables subscriptions over WebSocket. |
context=get_context |
Injects the request or WebSocket into resolvers. |
Upload scalar |
Handles GraphQL multipart file uploads. |
subscription_protocols=[...] |
Restricts accepted WebSocket subprotocols. |
Related examples¶
| Example | What it shows |
|---|---|
examples/graphql.py |
Queries, mutations, subscriptions, file uploads, and context injection. |
Related documentation¶
- WebSockets — keep a persistent connection open for subscriptions.
- OpenAPI — document the mounted GraphQL endpoint alongside REST routes.
- Uploads — multipart file handling outside of GraphQL.