Skip to main content

ASGI support for the Tartiflette Python GraphQL engine

Project description

tartiflette-starlette logo

Build status Package version Code style

tartiflette-starlette is a wrapper that provides ASGI support for the Tartiflette Python GraphQL engine.

Build your GraphQL API with Tartiflette, then use the included TartifletteApp and get the following:

Table of contents

Quickstart

from tartiflette import Resolver
from tartiflette_starlette import TartifletteApp

@Resolver("Query.hello")
async def hello(parent, args, context, info):
    name = args["name"]
    return f"Hello, {name}!"

sdl = """
  type Query {
    hello(name: String): String
  }
"""

app = TartifletteApp(sdl=sdl)

Save the file as graphql.py and start a uvicorn server:

uvicorn graphql:app

Note: the GraphQL endpoint is exposed on / by default.

Make a request:

curl -H "Content-Type: application/graphql"  -d '{ hello(name: "Chuck") }' http://localhost:8000

Response:

{ "data": { "hello": "Hello, Chuck!" } }

Or access http://localhost:8000 in a browser to make interactive queries using the built-in GraphiQL client:

Installation

  1. Install Tartiflette's external dependencies as explained in the Tartiflette tutorial.
  2. Install tartiflette-starlette from PyPI:
pip install tartiflette-starlette

This will also install Tartiflette and Starlette, so you're good to go!

Note: tartiflette-starlette requires Python 3.6+.

User guide

The TartifletteApp class is an ASGI3-compliant application. There are two ways to use it:

  • Serve it as a standalone ASGI app.
  • Mount it as an endpoint of another ASGI app (e.g. a Starlette application).

Standalone serving

The Quickstart example shows how to build a TartifletteApp and serve it as a standalone ASGI app.

The app is served using Uvicorn, but any other ASGI web server will do, for example:

ASGI submounting

Most ASGI web frameworks provide a way to mount another ASGI app at a given URL prefix. You can use this to serve a TartifletteApp at an endpoint such as /graphql on the root ASGI application.

This is useful to have a GraphQL endpoint and other (non-GraphQL) endpoints within a single application. For example, to have a REST endpoint at /api/users and a GraphQL endpoint at /graphql.

Important: this should work with any web framework that supports ASGI submounting — it doesn't have to be Starlette. See also: What is the role of Starlette?

Starlette example

from starlette.applications import Starlette
from starlette.responses import PlainTextResponse
from tartiflette import Resolver
from tartiflette_starlette import TartifletteApp, mount

app = Starlette()

@app.route("/")
async def home(request):
  return PlainTextResponse("Hello, world!")

@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)
mount.starlette(app, "/graphql", graphql)  # (*)

(*) This is a shorthand for:

app.mount("/graphql", graphql)
app.add_event_handler("startup", graphql.startup)

Save the file as app.py, and serve it with uvicorn:

uvicorn app:app

Make a request:

curl -H "Content-Type: application/graphql/"  -d '{ hello(name: "Chuck") }' http://localhost:8000

Note: if you receive a 307 Temporary Redirect response, make sure to include the trailing slash: /graphql/.

Response:

{ "data": { "hello": "Hello, Chuck!" } }

General approach

Assuming you have an instance of TartifletteApp called graphql, you need to:

  1. Add the graphql app as a sub-application (also known as "mounting"). The parent ASGI application may expose a method such as .mount() for this purpose.
  2. Add graphql.startup as a startup event handler so that the Tartiflette engine is built upon application startup. Note that:
  • Not doing this will result in a RuntimeError when requesting the GraphQL endpoint.
  • The parent ASGI application may expose a method such as .add_event_handler() for this purpose.
  • This is only required if the parent ASGI application does not call lifespan event handlers for sub-applications, as is the case for Starlette.

Tip: the mount module provides mounting helpers for various ASGI frameworks.

Making requests

tartiflette-starlette complies with the GraphQL spec, which allows you to pass the query in several ways:

  • URL query string (methods: GET, POST):
curl 'http://localhost:8000?query=\{hello(name:"Chuck")\}'
  • JSON-encoded body (methods: POST):
curl \
  -H "Content-Type: application/json" \
  -d '{"query": "{ hello(name: \"Chuck\") }"}' \
  http://localhost:8000
  • Raw body with the application/graphql content type (methods: POST):
curl \
  -H "Content-Type: application/graphql" \
  -d '{ hello(name: "Chuck") }' \
  http://localhost:8000

Note: you may have your GraphQL API served at a different endpoint.

Accessing request information

You can access the Starlette Request object from resolvers using context["req"]:

@Resolver("Query.whoami")
async def resolve_whoami(parent, args, context, info) -> str:
    request = context["req"]
    return getattr(request.state, "user", "a mystery")

See also Requests in the Starlette documentation.

GraphiQL client

By default, the GraphQL endpoint provided by TartifletteApp serves a GraphiQL client when it is accessed from a web browser. It can be customized using the GraphiQL helper.

Here's an example:

from tartiflette_starlette import TartifletteApp, GraphiQL

app = TartifletteApp(
    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)
        }
        """,
    ),
)

Save this as graphql.py and run uvicorn graphql:app. You should see the customized GraphiQL client when accessing http://127.0.0.1/graphiql:

See GraphiQL in the API reference for a complete description of the available options.

Providing additional context to resolvers

You can inject your own services, functions or data into the GraphQL context using the context option.

For example, assuming you use a publish/subscribe library named pubsub, you could write:

from pubsub import PubSub  # Fake

@Resolver("Query.human")
async def resolve_human(parent, args, context, info):
    pubsub = context["pubsub"]
    # ...
    await pubsub.publish("human_fetched", args)

graphql = TartifletteApp(
  # ...,
  context={"pubsub": PubSub()},
)

WebSocket subscriptions (Advanced)

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_starlette import TartifletteApp

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 {"remainingTime": seconds - i, "status": "RUNNING"}
        await asyncio.sleep(1)
    yield {"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.

API Reference

Note: unless specified, components documented here can be imported from tartiflette_starlette directly, e.g. from tartiflette_starlette import TartifletteApp.

TartifletteApp

Parameters

Note: all parameters are keyword-only.

  • engine (Engine): a Tartiflette engine. Required if sdl is not given.
  • sdl (str): a GraphQL schema defined using the GraphQL Schema Definition Language. Required if engine is not given.
  • path (str, optional): the path which clients should make GraphQL queries to. Defaults to "/".
  • graphiql (GraphiQL or bool, optional): configuration for the GraphiQL client. Defaults to True, which is equivalent to GraphiQL(). Use False to not register the GraphiQL client.
  • subscriptions (Subscriptions or bool, optional): subscriptions configuration. Defaults to True, which is equivalent to Subscriptions(path="/subscriptions"). Leave empty or pass None to not register the subscription WebSocket endpoint.
  • context (dict, optional): a copy of this dictionary is passed to resolvers when executing a query. Defaults to {}. Note: the Starlette Request object is always present as req.
  • schema_name (str, optional): name of the GraphQL schema from the Schema Registry which should be used — mostly for advanced usage. Defaults to "default".

Methods

  • __call__(scope, receive, send): ASGI3 implementation.

Error responses

Status code Description
400 Bad Request The GraphQL query could not be found in the request data.
404 Not Found The request does not match the GraphQL or GraphiQL endpoint paths.
405 Method Not Allowed The HTTP method is not one of GET, HEAD or POST.
415 Unsupported Media Type The POST request made to the GraphQL endpoint uses a Content-Type different from application/json and application/graphql.

GraphiQL

Configuration helper for the GraphiQL client.

Parameters

Note: all parameters are keyword-only.

  • path (str, optional): the path of the GraphiQL endpoint, relative to the root path which TartifletteApp is served at. If not given, defaults to the path given to TartifletteApp.
  • default_headers (dict, optional): extra HTTP headers to send when calling the GraphQL endpoint.
  • default_query (str, optional): the default query to display when accessing the GraphiQL interface.
  • default_variables (dict, optional): default variables to display when accessing the GraphiQL interface.
  • template (str, optional): an HTML template to use instead of the default one. In the template, default_headers, default_query and default_variables, as well as the GraphQL endpoint, are available as strings (JSON-encoded if needed) using template string substitutions, e.g.:
const endpoint = `${endpoint}`; // This is where the API call should be made.
const defaultHeaders = JSON.parse(`${default_headers}`);

Subscriptions

Configuration helper for WebSocket subscriptions.

Parameters

Note: all parameters are keyword-only.

  • path (str): the path of the subscriptions WebSocket endpoint, relative to the root path which TartifletteApp is served at. If not given, defaults to /subscriptions.

mount

This module contains helpers for mounting a TartifletteApp on other ASGI applications. Use these helpers to make sure you comply with the steps described in General approach.

Parameters

All mounting helpers expect the same parameters:

  • parent (ASGI app): the parent ASGI application which the TartifletteApp must be mounted onto.
  • path (str): the URL path where the TartifletteApp should be mounted.
  • app (TartifletteApp): the TartifletteApp to mount.
  • **kwargs (any): extra keyword arguments passed to the mount implementation of the parent app.

Available helpers

Helper Mount implementation Startup event handler implementation
mount.starlette() parent.mount() parent.add_event_handler()

Missing a helper for your favorite framework? Feel free to open a pull request!

FAQ

Does this package ship with Tartiflette?

Yes. Everything is included, which allows you to start building your GraphQL API right away. See also Installation.

Do I need to learn GraphQL/Tartiflette to use this package?

Yes: once you've got the TartifletteApp ASGI app up and running, you're in Tartiflette territory.

Here are some resources to get you started:

What is the role of Starlette?

tartiflette-starlette uses Starlette as a lightweight ASGI toolkit: internally, it uses Starlette's request and response classes, and some other components.

Luckily, this does not require your applications to use Starlette at all.

For example, if you are submounting your GraphQL app on an app built with an async web framework, this framework does not need to use Starlette — it just needs to speak ASGI.

What is ASGI?

ASGI provides a standard interface between async-capable Python web servers, frameworks, and applications.

See also the ASGI documentation.

Contributing

Want to contribute? Awesome! Be sure to read our Contributing guidelines.

Changelog

Changes to this project are recorded in the changelog.

License

MIT

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[Unreleased]

0.5.2 - 2019-10-09

Added

  • Add support for Python 3.8. (Pull #55)

Fixed

  • Type annotations are now correctly detected by mypy. (Pull #66)
  • Fix a bug that prevented the GraphiQL web interface from making queries when the application was mounted on a parent ASGI app. (Pull #51)

0.5.1 - 2019-07-16

Fixed

  • Fixed a bug that prevented accessing the GraphiQL interface when subscriptions were not enabled.

0.5.0 - 2019-07-12

Added

  • WebSocket subscriptions, configurable with the new subscriptions option on TartifletteApp.
  • Pass extra context to resolvers using the new context option on TartifletteApp.

0.4.0 - 2019-07-04

Added

  • Support for Tartiflette 0.12.x.
  • Add a mount module with submounting helpers.
  • Add mount.starlette().

Changed

  • Due to the new engine cooking API in Tartiflette 0.12, TartifletteApp now includes a startup event handler responsible for building the GraphQL engine. If submounting, it must be registered on the parent ASGI app. Helpers in the mount module take care of this for you.

Removed

  • Drop support for Tartiflette 0.11.x and below.

0.3.0 - 2019-07-03

Added

  • GraphiQL configuration via the GraphiQL helper. Options: path, default_query, default_headers, default_variables, template.

Changed

  • Internal refactoring that leverages more of Starlette's capabilities.
  • Documentation improvements.

0.2.0 - 2019-06-10

Added

  • Support for starlette>=0.12 (previously >=0.12.0b3).
  • Tartiflette is now installed too when installing tartiflette-starlette.

Changed

  • The default path is now "" (previously "/").
  • The request is now accessible in the GraphQL context via context["req"] (previously context["request"]).
  • If no error occurred, the errors field is not present in the response anymore (previously was None).

Fixed

  • More robust URL matching on TartifletteApp.

0.1.1 - 2019-04-28

Fixed

  • Add missing graphiql.html package asset.

0.1.0 - 2019-04-26

Added

Features:

  • TartifletteApp ASGI application.
  • Built-in GraphiQL client.

Project-related additions:

  • Package setup.
  • Changelog.
  • Contributing guide.
  • README and documentation.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tartiflette-starlette-0.5.2.tar.gz (23.4 kB view details)

Uploaded Source

Built Distribution

tartiflette_starlette-0.5.2-py2.py3-none-any.whl (18.9 kB view details)

Uploaded Python 2 Python 3

File details

Details for the file tartiflette-starlette-0.5.2.tar.gz.

File metadata

  • Download URL: tartiflette-starlette-0.5.2.tar.gz
  • Upload date:
  • Size: 23.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/2.0.0 pkginfo/1.5.0.1 requests/2.22.0 setuptools/40.8.0 requests-toolbelt/0.9.1 tqdm/4.36.1 CPython/3.7.3

File hashes

Hashes for tartiflette-starlette-0.5.2.tar.gz
Algorithm Hash digest
SHA256 6eb9c6dbb1378b635857c5a775c06b07dd0ab8893e33923c30070b9edc5f1603
MD5 fada7411a2eb72e2b8d614c9c3b47e01
BLAKE2b-256 2466aa088e7e2d7075ede259c8b77fd06c711de46df70b6b6b61022089a05f57

See more details on using hashes here.

File details

Details for the file tartiflette_starlette-0.5.2-py2.py3-none-any.whl.

File metadata

  • Download URL: tartiflette_starlette-0.5.2-py2.py3-none-any.whl
  • Upload date:
  • Size: 18.9 kB
  • Tags: Python 2, Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/2.0.0 pkginfo/1.5.0.1 requests/2.22.0 setuptools/40.8.0 requests-toolbelt/0.9.1 tqdm/4.36.1 CPython/3.7.3

File hashes

Hashes for tartiflette_starlette-0.5.2-py2.py3-none-any.whl
Algorithm Hash digest
SHA256 c8c93069cd6fa641e93c9153ea028e30eb6bfd414a894b913824422295eab2b1
MD5 ac8cd9e8809a6abcf2923aea105ab433
BLAKE2b-256 fd3cbca359b40fd3f63e0a7a53f6677813a6d5b120ec1470de7bb1a318d90bfb

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page