User Guide
Creating an application
The main piece of API that tartiflette-asgi
brings is TartifletteApp
, an ASGI3-compliant ASGI application.
You can build it from either:
- The Schema Definition Language (SDL):
# This is an SDL string, but Tartiflette supports
# other formats, e.g. paths to schema files or directories.
sdl = "type Query { hello: String }"
app = TartifletteApp(sdl=sdl)
- A Tartiflette
Engine
instance:
from tartiflette import Engine
engine = Engine(sdl=..., modules=[...])
app = TartifletteApp(engine=engine)
For more information on what values sdl
and engine
can accept, see the Engine API reference.
Routing
You can define which URL path the TartifletteApp
should be accessible at using the path
parameter.
It is served at /
by default, but a popular choice is to serve it at /graphql
:
app = TartifletteApp(..., path="/graphql")
Making requests
tartiflette-asgi
allows you to pass the query in several ways:
- URL query string (methods:
GET
,POST
):
curl 'http://localhost:8000/graphql?query=\{hello(name:"Chuck")\}'
- JSON-encoded body (methods:
POST
):
curl http://localhost:8000/graphql \
-d '{"query": "{ hello(name: \"Chuck\") }"}' \
-H "Content-Type: application/json"
- Raw body with the
application/graphql
content type (methods:POST
):
curl http://localhost:8000/graphql \
-d '{ hello(name: "Chuck") }' \
-H "Content-Type: application/graphql"
GraphiQL client
Default behavior
When you access the GraphQL endpoint in a web browser, TartifletteApp
serves a GraphiQL client, which allows you to make interactive GraphQL queries in the browser.
Customization
You can customize the GraphiQL interface using TartifletteApp(graphiql=GraphiQL(...))
.
For example, this snippet will:
- Serve the GraphiQL web interface at
/graphiql
. - Send an
Authorization
header when making requests to the API endpoint. - Setup the default variables and query to show when accessing the web interface for the first time.
from tartiflette_asgi import TartifletteApp, GraphiQL
sdl = "type Query { hello(name: String): String }"
graphiql = GraphiQL(
path="/graphiql",
default_headers={"Authorization": "Bearer 123"},
default_variables={"name": "world"},
default_query="""
query Hello($name: String) {
hello(name: $name)
}
""",
)
app = TartifletteApp(sdl=sdl, graphiql=graphiql)
If you run this application, you should see the customized GraphiQL client when accessing http://localhost:8000/graphiql:
For the full list of options, see GraphiQL
.
Disabling the GraphiQL client
To disable the GraphiQL client altogether, use TartifletteApp(graphiql=False)
.
ASGI sub-mounting
You can mount a TartifletteApp
instance as a sub-route of another ASGI application.
This is useful to have a GraphQL endpoint and other (non-GraphQL) endpoints within a single application. For example, you may want to have a REST endpoint at /api/users
, or serve an HTML page at /index.html
, as well as expose a GraphQL endpoint at /graphql
.
How to achieve this depends on the specific ASGI web framework you are using, so this section documents how to achieve it in various situations.
General approach
In general, you'll need to do the following:
- Create a
TartifletteApp
application instance. - Mount it under the main ASGI application's router. (Most ASGI frameworks expose a method such as
.mount()
for this purpose.) - Register the startup lifespan event handler on the main ASGI application. (Frameworks typically expose a method such as
.add_event_handler()
for this purpose.)
Important
The startup event handler is responsible for preparing the GraphQL engine (a.k.a. cooking the engine), e.g. loading modules, SDL files, etc.
If your ASGI framework does not implement the lifespan protocol and/or does not allow to register custom lifespan event handlers, or if you're working at the raw ASGI level, you can still use tartiflette-asgi
but you'll need to add lifespan support yourself, e.g. using asgi-lifespan.
Routing
Although TartifletteApp
has minimal support for routing, when using ASGI sub-mounting you'll probably want to leave the path
parameter on TartifletteApp
empty, e.g. use:
graphql = TartifletteApp(sdl=...)
app.mount("/graphql", app=graphql)
This is because path
is relative to the mount path on the host ASGI application. As a result, mounting at /graphql
while setting path="/graphql"
would make the GraphQL API accessible at /graphql/graphql
, which is typically not what you want.
If you want to have your GraphQL API accessible at /graphql
, you should do as above, i.e.:
- Leave
path
empty onTartifletteApp
. - Mount it at
/graphql
on the host ASGI app.
(Mounting at /
and setting path="/graphql"
typically won't have the behavior you'd expect. For example, Starlette would send all requests to your GraphQL endpoint, regardless of whether the requested URL path.)
Examples
This section documents how to mount a TartifletteApp
instance under an ASGI application for various ASGI web frameworks.
To run an example:
- Save the application script as
graphql.py
. - Run the server using
$ uvicorn graphql:app
. - Make a request using:
curl http://localhost:8000/graphql -d '{ hello(name: "Chuck") }' -H "Content-Type: application/graphql"
Starlette
from starlette.applications import Starlette
from starlette.responses import PlainTextResponse
from starlette.routing import Mount, Route
from tartiflette import Resolver
from tartiflette_asgi import TartifletteApp
# Create a 'TartifletteApp' instance.
@Resolver("Query.hello")
async def hello(parent, args, context, info):
name = args["name"]
return f"Hello, {name}!"
sdl = "type Query { hello(name: String): String }"
graphql = TartifletteApp(sdl=sdl)
# Declare regular routes as seems fit...
async def home(request):
return PlainTextResponse("Hello, world!")
# Create a Starlette application, mounting the 'TartifletteApp' instance.
routes = [
Route("/", endpoint=home),
Mount("/graphql", graphql),
]
app = Starlette(routes=routes, on_startup=[graphql.startup])
Advanced usage
Accessing request information
All the information about the current HTTP request (URL, headers, query parameters, etc) is available on context["req"]
, which returns a Starlette Request
object representing the current HTTP request.
@Resolver("Query.whoami")
async def resolve_whoami(parent, args, context, info):
request = context["req"]
who = request.query_params.get("username", "unknown")
return who
For detailed usage notes about the Request
object, see Requests in the Starlette documentation.
Shared GraphQL context
If you need to make services, functions or data available to GraphQL resolvers, you can use TartifletteApp(context=...)
. Contents of the context
argument will be merged into the GraphQL context
passed to resolvers.
For example:
import os
from tartiflette import Resolver
from tartiflette_asgi import TartifletteApp
@Resolver("Query.human")
async def resolve_human(parent, args, context, info):
planet = context["planet"]
return f"Human living on {planet}"
app = TartifletteApp(
sdl="type Query { human(): String }",
context={"planet": os.getenv("PLANET", "Earth")},
)
WebSocket subscriptions
This package provides support for GraphQL subscriptions over WebSocket. Subscription queries can be issued via the built-in GraphiQL client, as well as Apollo GraphQL and any other client that uses the subscriptions-transport-ws protocol.
Example:
import asyncio
from tartiflette import Subscription
from tartiflette_asgi import TartifletteApp, GraphiQL
sdl = """
type Query {
_: Boolean
}
type Subscription {
timer(seconds: Int!): Timer
}
enum Status {
RUNNING
DONE
}
type Timer {
remainingTime: Int!
status: Status!
}
"""
@Subscription("Subscription.timer")
async def on_timer(parent, args, context, info):
seconds = args["seconds"]
for i in range(seconds):
yield {"timer": {"remainingTime": seconds - i, "status": "RUNNING"}}
await asyncio.sleep(1)
yield {"timer": {"remainingTime": 0, "status": "DONE"}}
app = TartifletteApp(
sdl=sdl,
subscriptions=True,
graphiql=GraphiQL(
default_query="""
subscription {
timer(seconds: 5) {
remainingTime
status
}
}
"""
),
)
Note: the subscriptions endpoint is exposed on
/subscriptions
by default.
Save this file as graphql.py
, then run $ uvicorn graphql:app
. Open the GraphiQL client at http://localhost:8000, and hit "Play"! The timer should update on real-time.
See Subscriptions
in the API reference for a complete description of the available options.
For more information on using subscriptions in Tartiflette, see the Tartiflette documentation.