Queries

Queries are the primary way to fetch data from your GraphQL API. Strawberry Django provides powerful tools to define queries that automatically map to Django models and querysets.

Basic Queries

Queries can be written using strawberry_django.field() to load the fields defined in your types.

schema.py
import strawberry
import strawberry_django
from .types import Fruit
@strawberry.type
class Query:
fruit: Fruit = strawberry_django.field()
fruits: list[Fruit] = strawberry_django.field()
schema = strawberry.Schema(query=Query)

Tip

You must name your query class β€œQuery” or decorate it with @strawberry.type(name="Query") for the single query default primary filter to work

For single queries (like fruit above), Strawberry Django automatically provides a primary key filter. The fruits query returns all objects by default.

query {
# Single object by primary key
fruit(pk: "1") {
id
name
}
# All objects
fruits {
id
name
}
}

Queries with Filters

Add filters to your queries to enable flexible data filtering. Filters are defined in your types and automatically applied to queries.

types.py
import strawberry_django
from strawberry import auto
@strawberry_django.filter_type(models.Fruit, lookups=True)
class FruitFilter:
id: auto
name: auto
color: auto
@strawberry_django.type(models.Fruit, filters=FruitFilter)
class Fruit:
id: auto
name: auto
color: auto
schema.py
@strawberry.type
class Query:
fruits: list[Fruit] = strawberry_django.field()

This enables powerful filtering in your GraphQL queries:

query {
# Exact match
fruits(filters: { name: "apple" }) {
id
name
}
# With lookups
fruits(filters: { name: { iContains: "berry" } }) {
id
name
}
# Complex filters with AND/OR
fruits(
filters: {
OR: [{ name: { startsWith: "straw" } }, { color: { exact: "red" } }]
}
) {
id
name
}
}

See the Filtering Guide for comprehensive filter documentation.

Queries with Ordering

Add ordering to control the sort order of your results.

types.py
import strawberry_django
from strawberry import auto
@strawberry_django.order_type(models.Fruit)
class FruitOrder:
name: auto
price: auto
@strawberry_django.type(models.Fruit, order=FruitOrder)
class Fruit:
id: auto
name: auto
price: auto
query {
# Order by name ascending
fruits(order: { name: ASC }) {
name
price
}
# Order by multiple fields
fruits(order: { price: DESC, name: ASC }) {
name
price
}
}

See the Ordering Guide for detailed ordering documentation.

Queries with Pagination

Always paginate large result sets to prevent performance issues.

Offset Pagination

schema.py
from strawberry_django.pagination import OffsetPaginationInput
@strawberry.type
class Query:
@strawberry_django.field
def fruits(
self,
pagination: OffsetPaginationInput | None = None,
) -> list[Fruit]:
qs = models.Fruit.objects.all()
if pagination:
return qs[pagination.offset:pagination.offset + pagination.limit]
return qs[:100] # Default limit

Note

Returning list[Fruit] means the queryset is evaluated immediately. For paginated queries with metadata (total count, page info), use OffsetPaginated[Fruit] with pagination=True instead. See the Pagination guide for details.

query {
fruits(pagination: { offset: 0, limit: 10 }) {
id
name
}
}

Relay-Style Cursor Pagination

schema.py
from strawberry_django.relay import DjangoListConnection
import strawberry_django
@strawberry.type
class Query:
fruits: DjangoListConnection[Fruit] = strawberry_django.connection()
query {
fruits(first: 10, after: "cursor") {
edges {
node {
id
name
}
cursor
}
pageInfo {
hasNextPage
endCursor
}
}
}

See the Pagination Guide for more pagination strategies.

Custom Resolvers

Override default resolvers for custom query logic.

schema.py
from strawberry.types import Info
@strawberry.type
class Query:
@strawberry_django.field
def fruits(
self,
info: Info,
available_only: bool = True,
) -> list[Fruit]:
qs = models.Fruit.objects.all()
if available_only:
qs = qs.filter(available=True)
# Apply filters from info context
return qs
@strawberry_django.field
def featured_fruits(self, info: Info) -> list[Fruit]:
"""Return only featured fruits"""
return models.Fruit.objects.filter(
is_featured=True,
available=True
).order_by('-created_at')[:5]
query {
# All available fruits
fruits(availableOnly: true) {
name
}
# Featured fruits only
featuredFruits {
name
}
}

See the Custom Resolvers Guide for advanced resolver patterns.

Async Queries

For ASGI applications, you can define async queries for improved concurrency.

schema.py
from asgiref.sync import sync_to_async
@strawberry.type
class Query:
@strawberry_django.field
async def fruits(self) -> list[Fruit]:
# Django ORM calls must be wrapped in sync_to_async
return await sync_to_async(list)(
models.Fruit.objects.all()
)
@strawberry_django.field
async def fruit(self, pk: int) -> Fruit | None:
try:
return await sync_to_async(models.Fruit.objects.get)(pk=pk)
except models.Fruit.DoesNotExist:
return None

Warning

Always wrap Django ORM operations in sync_to_async when using async resolvers. The ORM is not async-native.

Nested Queries and Relationships

Strawberry Django automatically handles related objects with optimal queries using the Query Optimizer.

models.py
class Author(models.Model):
name = models.CharField(max_length=100)
class Book(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
types.py
@strawberry_django.type(models.Author)
class Author:
id: auto
name: auto
books: list['Book']
@strawberry_django.type(models.Book)
class Book:
id: auto
title: auto
author: Author
query {
books {
title
author {
name
books {
title
}
}
}
}

The Query Optimizer automatically adds select_related() and prefetch_related() to prevent N+1 queries.

See the Query Optimizer Guide for optimization details.

Query Arguments

Add custom arguments to your queries for flexibility.

schema.py
from datetime import date
@strawberry.type
class Query:
@strawberry_django.field
def books(
self,
author_id: int | None = None,
published_after: date | None = None,
min_rating: float | None = None,
) -> list[Book]:
qs = models.Book.objects.all()
if author_id:
qs = qs.filter(author_id=author_id)
if published_after:
qs = qs.filter(publication_date__gte=published_after)
if min_rating:
qs = qs.filter(rating__gte=min_rating)
return qs
query {
books(authorId: 1, publishedAfter: "2020-01-01", minRating: 4.0) {
title
rating
}
}

Aggregations and Computed Fields

Use annotations for database-level aggregations.

types.py
from django.db.models import Count, Avg
import strawberry_django
@strawberry_django.type(models.Author)
class Author:
id: auto
name: auto
book_count: int = strawberry_django.field(
annotate={'book_count': Count('books')}
)
avg_book_rating: float = strawberry_django.field(
annotate={'avg_book_rating': Avg('books__rating')}
)
query {
authors {
name
bookCount
avgBookRating
}
}

Best Practices

  1. Always enable the Query Optimizer - Add DjangoOptimizerExtension() to your schema
  2. Paginate list queries - Prevent loading thousands of records
  3. Use filters and ordering - Let clients control what data they need
  4. Add appropriate indexes - Index fields used in filters and ordering
  5. Use custom resolvers sparingly - Default resolvers are optimized
  6. Leverage annotations - Perform calculations in the database
  7. Test query performance - Monitor SQL queries during development

Complete Example

schema.py
import strawberry
import strawberry_django
from strawberry_django.optimizer import DjangoOptimizerExtension
from strawberry_django.pagination import OffsetPaginationInput
from . import models
from .types import Fruit, FruitFilter, FruitOrder
@strawberry.type
class Query:
# Single object query with PK filter
fruit: Fruit = strawberry_django.field()
# List query with filters, ordering, and pagination
@strawberry_django.field
def fruits(
self,
filters: FruitFilter | None = strawberry.UNSET,
order: FruitOrder | None = strawberry.UNSET,
pagination: OffsetPaginationInput | None = None,
) -> list[Fruit]:
qs = models.Fruit.objects.all()
# Filters and ordering are applied automatically
# when using strawberry_django.field
# Apply pagination
if pagination:
qs = qs[pagination.offset:pagination.offset + pagination.limit]
else:
qs = qs[:100] # Default limit
return qs
# Custom query with business logic
@strawberry_django.field
def featured_fruits(self) -> list[Fruit]:
return models.Fruit.objects.filter(
is_featured=True,
available=True
).order_by('-created_at')[:10]
# Enable Query Optimizer
schema = strawberry.Schema(
query=Query,
extensions=[
DjangoOptimizerExtension(),
]
)
query {
# Get single fruit
fruit(pk: "1") {
id
name
price
}
# List with filters and ordering
fruits(
filters: { name: { iContains: "berry" } }
order: { price: DESC }
pagination: { offset: 0, limit: 10 }
) {
id
name
price
}
# Custom query
featuredFruits {
id
name
}
}

See Also