Python: Getting Started
|
This section documents the current GraphQL specification (October 2021), plus the working draft for
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production, as GraphQL tooling iterates quickly. |
Python has three actively maintained ways to serve a GraphQL schema, all built on the same execution engine. This page surveys that landscape, explains why Strawberry + FastAPI is this section’s primary style, and installs a minimal server to run locally before the next pages go deep on each library.
The Python GraphQL landscape
Every Python GraphQL server — regardless of which library you write your schema in — ultimately executes
requests through graphql-core, the reference implementation of the
GraphQL specification’s execution engine, type system, and validator, ported from the JavaScript reference
implementation (graphql-js). You rarely import graphql-core directly; instead you pick one of the
higher-level libraries built on it, and each one shapes how you author a schema very differently even though
they all resolve requests the same underlying way:
| Library | Schema authoring style | Where it fits |
|---|---|---|
Strawberry |
Code-first: Python dataclasses and type hints generate the schema. |
The modern default for a new Python GraphQL API, especially with FastAPI. |
Ariadne |
Schema-first: you hand-write GraphQL SDL and bind resolver functions to it. |
An existing SDL contract, or a team where the schema is owned by someone other than the Python backend (API design-first workflows, a shared schema versioned outside the Python codebase). |
Graphene |
Code-first, class-based ( |
The earliest popular Python GraphQL library; still found in older codebases, but development has slowed relative to Strawberry and it is mentioned here for context rather than documented in depth. |
None of these compete with graphql-core — they compete with each other, on how much of the schema you write
by hand versus derive from Python code. This section documents Strawberry and Ariadne in depth
(FastAPI & Strawberry,
Strawberry schema & features, and
Ariadne (schema-first)) and does not cover Graphene
further.
graphql-core: the reference execution engine
graphql-core implements the same four-stage request lifecycle — parse, validate, execute, respond — that
every GraphQL server follows, regardless of language. Concretely, it provides:
-
A
GraphQLSchemaobject model (GraphQLObjectType,GraphQLField,GraphQLArgument, and so on) that both Strawberry and Ariadne build and hand to the executor — Strawberry builds it from your typed Python classes, Ariadne builds it by parsing the SDL you wrote and attaching your resolver functions to it. -
A
graphql()(orgraphql_sync()) entry point that parses a query document, validates it against the schema, executes it field by field, and returns anExecutionResult— the\{ data, errors }envelope described on the Getting Started page. -
The scalar types, introspection system, and directive machinery (
@skip,@include) defined by the specification itself.
You will not typically call graphql-core’s API directly in application code — Strawberry and Ariadne both
wrap it behind a friendlier surface — but knowing it sits underneath both explains why a schema built with
either library behaves identically with respect to validation errors, null propagation, and introspection: that
behavior lives in `graphql-core, not in the library on top of it.
Code-first with Strawberry
Strawberry derives a GraphQL schema from ordinary Python type hints and
dataclasses, using the standard library’s dataclasses module (or Pydantic models, via an integration) rather
than a bespoke class hierarchy:
import strawberry
@strawberry.type
class Book:
title: str
published_year: int
@strawberry.type
class Query:
@strawberry.field
def book(self, id: strawberry.ID) -> Book:
return Book(title="The Left Hand of Darkness", published_year=1969)
schema = strawberry.Schema(query=Query)
The @strawberry.type decorator turns a dataclass into a GraphQL object type, and @strawberry.field marks a
method as a resolver for one field; Strawberry reads the Python type annotations (str, int,
strawberry.ID, Optional[…], list[…]) to derive the matching GraphQL type (String, Int, ID,
a nullable type, a list type) automatically, so the schema and the language-level types can never drift apart
silently. Because the schema is Python code, IDEs, type checkers (mypy, via Strawberry’s own plugin), and
ordinary unit tests all work against it directly — there is no separate SDL file to keep in sync by hand.
Strawberry ships first-class ASGI integrations for FastAPI, Starlette, and Django, which is the other half of why it pairs so naturally with a modern Python web stack; FastAPI & Strawberry covers mounting a Strawberry schema on a FastAPI application in depth, and Strawberry schema & features covers the rest of its type system — interfaces, unions, permissions, DataLoaders, and Relay-style pagination.
Schema-first with Ariadne
Ariadne takes the opposite starting point: you write the schema as plain GraphQL SDL, and then bind ordinary Python functions to its fields as resolvers, rather than deriving the schema from Python types:
type Book {
title: String!
publishedYear: Int!
}
type Query {
book(id: ID!): Book!
}
from ariadne import QueryType, make_executable_schema
type_defs = load_schema_from_path("schema.graphql")
query = QueryType()
@query.field("book")
def resolve_book(_, info, id):
return {"title": "The Left Hand of Darkness", "publishedYear": 1969}
schema = make_executable_schema(type_defs, query)
QueryType (and its siblings MutationType, ObjectType, SubscriptionType) collects resolver bindings by
field name, and make_executable_schema parses the SDL and attaches those bindings to produce the same kind of
executable GraphQLSchema that graphql-core expects. This style suits a team where the schema is a contract
owned outside the Python code — designed up front, reviewed independently of any implementation, or shared with
non-Python services — because the SDL file is the source of truth and no Python annotation can silently
diverge from it. Ariadne (schema-first) covers resolver
binding, custom scalars, union/interface `type_resolver`s, and mounting Ariadne’s ASGI app under FastAPI in
depth.
Why Strawberry + FastAPI is this section’s default
Both libraries are actively maintained and production-ready, and the choice between them is a genuine trade-off rather than one being strictly better:
-
Strawberry wins when the Python codebase is the schema’s source of truth — one team owns both the API and its implementation, and deriving the schema from typed Python code keeps a hand-written SDL file from ever drifting out of sync with the resolvers behind it.
-
Ariadne wins when the SDL itself is the contract — an API designed schema-first, reviewed independently of any implementation, or shared with non-Python schema owners.
This section uses Strawberry + FastAPI as its primary worked style for the same reason Spring for GraphQL is
this section’s primary Java style: it is the combination most new Python GraphQL services reach for today, it
has first-class native ASGI support (no adapter layer), and FastAPI’s own dependency-injection model composes
cleanly with Strawberry’s resolver Info.context, as
FastAPI & Strawberry shows next. Ariadne is documented
alongside it, in full, rather than as an afterthought, because schema-first is a legitimate and common choice,
not a fallback.
Installing and running a minimal server
Strawberry’s FastAPI integration is an optional extra, installed via its [fastapi] extra rather than as a
separate package:
pip install "strawberry-graphql[fastapi]"
A minimal server needs only a schema and an ASGI app to mount it on — strawberry.fastapi.GraphQLRouter wraps
a strawberry.Schema as a FastAPI-mountable router, which the next page,
FastAPI & Strawberry, builds on directly:
from fastapi import FastAPI
from strawberry.fastapi import GraphQLRouter
app = FastAPI()
graphql_app = GraphQLRouter(schema)
app.include_router(graphql_app, prefix="/graphql")
Run it with uvicorn, an ASGI server, the same way you would run any other FastAPI application:
uvicorn main:app --reload
Visiting http://localhost:8000/graphql in a browser serves Strawberry’s built-in GraphiQL interface — one of the interactive tools introduced on
Getting Started — so the schema can
be explored and queried immediately, with no separate IDE to install.
Ariadne’s equivalent installation pulls in uvicorn explicitly, since Ariadne does not bundle a specific ASGI
server as a default the way Strawberry’s FastAPI extra implies one:
pip install ariadne uvicorn
Ariadne (schema-first) covers mounting Ariadne’s GraphQL
ASGI application under FastAPI or Starlette, including context_value and error formatting, in depth.
What’s next
This page only surveyed the landscape and got a schema running; the next three pages go deep on each library in turn:
-
FastAPI & Strawberry —
@strawberry.type/fieldresolvers, sync andasyncresolvers, mountingGraphQLRouterwith acontext_getterthat injects FastAPI dependencies into resolvers, and subscriptions over WebSocket/SSE. -
Strawberry schema & features — scalars, enums, interfaces, unions, generics, input types, permissions, DataLoaders, and Relay-style pagination.
-
Ariadne (schema-first) — SDL-driven resolver binding,
make_executable_schema, custom scalars, union/interface type resolution, and when schema-first is the better fit.
Everything language-agnostic covered earlier in this section — the request lifecycle, the query language itself, pagination conventions, security and demand control, and authorization patterns — applies to a Python server exactly as it does to a JVM one; only the mechanics of wiring up a schema and its resolvers differ between languages, which is what these Python-specific pages focus on.