Skip to main content

Python GraphQL Strawberry: Getting Started

Strawberry is a Python GraphQL framework that uses type hints and dataclasses to define your schema with zero boilerplate. In this article, you'll install Strawberry, write your first type and query resolver, and run a GraphQL server that accepts queries—all in under 15 minutes. This is your foundation for the entire series.

I started building GraphQL APIs in 2019 using graphene-django, and I found the decorator-based syntax verbose and error-prone. When Strawberry emerged in 2021, I switched immediately: the dataclass-first design felt Pythonic, and the async support made it trivial to integrate with FastAPI. That experience informs every example in this series.

What is GraphQL and Why Strawberry?

GraphQL is a query language and runtime for APIs that lets clients request exactly the fields they need. Unlike REST (where GET /users/123 returns a fixed user shape), GraphQL lets a client ask "give me this user's name and email only" or "give me the user plus their posts and each post's comments" in a single request. Strawberry implements GraphQL in Python by auto-generating a schema from type hints—no separate schema definition language.

Strawberry differs from graphene (the earlier Python standard) in three ways: it uses native Python dataclasses (not custom decorator wrappers), it ships with native async/await support, and its developer experience is closer to modern FastAPI than to older ORM-centric tools. As of 2026, Strawberry is the most-downloaded Python GraphQL framework on PyPI.

Step 1: Install Strawberry

Open a terminal in a new project folder and run:

pip install strawberry-graphql

Verify the install:

python -c "import strawberry; print(strawberry.__version__)"

You should see a version number like 0.246 or higher. If you plan to use Strawberry with FastAPI or ASGI servers later, also install:

pip install strawberry-graphql[asgi]

Step 2: Define Your First Type

Strawberry types are Python dataclasses decorated with @strawberry.type. Create a file named main.py:

import strawberry

@strawberry.type
class User:
"""A user in our system."""
id: int
name: str
email: str

Each field (id, name, email) becomes a field in your GraphQL schema. Strawberry infers the GraphQL type from Python's type hints: int becomes GraphQL Int! (non-nullable), str becomes String!, and optional fields use Python's Optional[T] which map to nullable GraphQL types.

Step 3: Define Your First Query

Queries are the read operations in GraphQL. Define them in a Query type with resolver functions:

import strawberry

@strawberry.type
class User:
id: int
name: str
email: str

@strawberry.type
class Query:
@strawberry.field
def hello(self) -> str:
"""A simple hello query."""
return "Hello, GraphQL!"

@strawberry.field
def get_user(self, user_id: int) -> User:
"""Fetch a user by ID."""
# In production, query a database here.
return User(id=user_id, name="Alice", email="[email protected]")

schema = strawberry.Schema(query=Query)

Breaking this down:

  • @strawberry.type marks a class as a GraphQL type.
  • @strawberry.field decorates a resolver—a function that computes a field value.
  • The function's return type annotation becomes the GraphQL field type.
  • strawberry.Schema(query=Query) creates your API schema; Query is the root type.

Step 4: Run a Development Server

Strawberry includes a CLI with a built-in GraphiQL IDE (a browser-based GraphQL explorer). Run:

strawberry server main

Visit http://localhost:8000/ in your browser. You'll see GraphiQL, a three-pane editor where you can write and test queries.

In the left pane, type this query:

query {
hello
}

Press the play button (or Cmd+Enter). The right pane shows:

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

Now try:

query {
getUser(userId: 5) {
id
name
email
}
}

Result:

{
"data": {
"getUser": {
"id": 5,
"name": "Alice",
"email": "[email protected]"
}
}
}

Notice the query argument: getUser(userId: 5) maps to the Python function parameter user_id: int. Strawberry auto-converts the camelCase GraphQL argument to Python's snake_case. This automatic naming convention prevents you from writing repetitive case-conversion logic.

Why Start Here?

This minimal setup teaches three essentials: (1) types are just dataclasses with a decorator, (2) resolvers are plain Python functions with type hints, (3) the schema is generated from these definitions. No separate schema files. No string definitions. You avoid the entire category of schema/code mismatch bugs that plague REST and older GraphQL frameworks.

The development server with GraphiQL is your playground for the rest of this series. Every article builds on this foundation: adding mutations, handling complex types, integrating with databases, and deploying to production.

Common Pitfalls

Forgetting the @strawberry.type decorator: Without it, your dataclass is just a Python class, not a GraphQL type. Strawberry won't scan it, and your schema build will fail with a vague error.

Returning wrong types from resolvers: If a resolver is decorated @strawberry.field def get_user(self) -> User:, Strawberry expects a User instance or a coroutine that yields one. Returning a dict or a raw database row causes a runtime type error.

Mixing sync and async without care: If your resolver is async def, Strawberry handles it correctly. But if you call async functions from a sync resolver without awaiting, Python will raise TypeError: object coroutine can't be used in 'await' expression.

Key Takeaways

  • Strawberry uses Python dataclasses and type hints to define GraphQL types and fields automatically.
  • Every @strawberry.field method becomes a resolver—a function Strawberry calls to fetch field values.
  • strawberry.Schema(query=Query) creates your API; run strawberry server to start the development GraphiQL IDE.
  • The GraphQL argument names are auto-converted from camelCase (GraphQL convention) to snake_case (Python convention).
  • In the rest of this series, you'll add mutations (write operations), complex resolvers, database queries, authentication, and production patterns.

Frequently Asked Questions

What's the difference between @strawberry.type and @strawberry.input?

@strawberry.type is used for output types (return values of queries and resolvers). @strawberry.input is used for input object types (arguments to mutations and complex query arguments). Input types are immutable and appear as arguments in your schema.

Can I use Strawberry without FastAPI?

Yes. Strawberry ships with its own ASGI server (used by strawberry server) for development. For production, you can mount Strawberry on FastAPI, Starlette, or any ASGI framework, or run it standalone with an ASGI server like Uvicorn or Hypercorn.

How do I handle database queries in resolvers?

Pass your database session (SQLAlchemy, async ORM, etc.) as context to the schema, then access it in resolvers. We cover this in detail in later articles. For now, inline sample data as we did in get_user().

Are field names case-sensitive?

In GraphQL, field names are case-sensitive. Strawberry auto-converts Python snake_case to GraphQL camelCase for consistency with GraphQL conventions, but you can override this with @strawberry.field(name="customName").

Further Reading