Strawberry is a Python library that makes building GraphQL APIs in Django feel like writing regular Python code.
Let’s see it in action. Imagine you have a simple Django app with a Book model:
# models.py
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=200)
author = models.CharField(max_length=100)
published_year = models.IntegerField()
def __str__(self):
return self.title
To expose this as a GraphQL API with Strawberry, you’d define a Book type and a query:
# schema.py
import strawberry
from typing import List
from .models import Book
@strawberry.django.type(Book)
class BookType:
id: int
title: str
author: str
published_year: int
@strawberry.type
class Query:
@strawberry.field
def books(self) -> List[BookType]:
return Book.objects.all()
schema = strawberry.Schema(query=Query)
And then wire it up in your urls.py:
# urls.py
from django.urls import path
from django.views.decorators.csrf import csrf_exempt
from strawberry.django.views import GraphQLView
from .schema import schema
urlpatterns = [
path("graphql", csrf_exempt(GraphQLView.as_view(schema=schema))),
]
Now, if you send a POST request to /graphql with the following JSON body:
{
"query": "{ books { id title author publishedYear } }"
}
You’ll get back a response like this (assuming you have books in your database):
{
"data": {
"books": [
{
"id": 1,
"title": "The Hitchhiker's Guide to the Galaxy",
"author": "Douglas Adams",
"publishedYear": 1979
},
{
"id": 2,
"title": "Pride and Prejudice",
"author": "Jane Austen",
"publishedYear": 1813
}
]
}
}
The core problem Strawberry solves is bridging the gap between your Django ORM and the GraphQL specification. Instead of manually mapping Django models to GraphQL types and writing resolvers for every field, Strawberry’s strawberry.django.type decorator inspects your Django model and automatically generates the corresponding GraphQL type. This dramatically reduces boilerplate and keeps your GraphQL schema in sync with your database models.
Internally, when you define BookType using @strawberry.django.type(Book), Strawberry inspects the Book model. It looks at the fields (title, author, published_year) and their Django types (CharField, IntegerField) and maps them to their GraphQL equivalents (String, Int). It also exposes the primary key (id) by default. When a query for books is executed, Strawberry’s Django integration knows how to translate the GraphQL request into a Django ORM query (Book.objects.all()) and then map the results back to the BookType structure.
You can also define mutations to modify data. Let’s add a mutation to create a new book:
# schema.py (continued)
import strawberry
from typing import List, Optional
from .models import Book
# ... (BookType and Query definition) ...
@strawberry.type
class Mutation:
@strawberry.mutation
def create_book(self, title: str, author: str, published_year: int) -> BookType:
book = Book.objects.create(
title=title,
author=author,
published_year=published_year
)
return book
schema = strawberry.Schema(query=Query, mutation=Mutation)
With this mutation, you can send a POST request to /graphql with:
{
"query": "mutation($title: String!, $author: String!, $year: Int!) { createBook(title: $title, author: $author, publishedYear: $year) { id title } }",
"variables": {
"title": "Dune",
"author": "Frank Herbert",
"year": 1965
}
}
And receive:
{
"data": {
"createBook": {
"id": 3,
"title": "Dune"
}
}
}
The most surprising part for many is how seamlessly Strawberry handles Django’s related_name and foreign key relationships. If your Book model had a ForeignKey to an Author model, you could simply add an author: AuthorType field to your BookType, and Strawberry would automatically generate the resolver to fetch the related author object, making complex nested queries incredibly simple to define.
The next concept to explore is how to handle custom resolvers and integrate Strawberry with Django’s authentication and permissions.