DataLoaders
DataLoaders help solve the N+1 query problem by batching and caching database queries. For basic information about DataLoaders, see the Strawberry DataLoaders documentation .
Tip
Strawberry Djangoβs Query Optimizer handles most optimization scenarios automatically. Use DataLoaders when you need custom batching logic or are working with external data sources.
Using DataLoaders with Django
Basic Example
Hereβs a basic DataLoader for fetching Django models:
from strawberry.dataloader import DataLoaderfrom asgiref.sync import sync_to_async
from . import models
async def load_authors(keys: list[int]) -> list[models.Author | None]: """Batch load authors by their IDs.""" # Build map from async queryset iteration author_map = { author.id: author async for author in models.Author.objects.filter(id__in=keys) } return [author_map.get(key) for key in keys] Sharing DataLoaders Across a Request
Important: DataLoaders must be instantiated once per request and shared across all resolvers to enable batching. Store them in the GraphQL context:
from strawberry.dataloader import DataLoaderfrom .dataloaders import load_authorsfrom . import models
class GraphQLContext: def __init__(self, request): self.request = request self._author_loader: DataLoader[int, models.Author | None] | None = None
@property def author_loader(self) -> DataLoader[int, models.Author | None]: if self._author_loader is None: self._author_loader = DataLoader(load_fn=load_authors) return self._author_loader
def get_context(request): return GraphQLContext(request) from django.urls import pathfrom strawberry.django.views import AsyncGraphQLViewfrom .schema import schemafrom .context import get_context
urlpatterns = [ path('graphql/', AsyncGraphQLView.as_view( schema=schema, context_getter=get_context )),] Using the DataLoader
import strawberry_djangofrom strawberry import fieldfrom strawberry.types import Info
@strawberry_django.type(models.Book)class Book: id: strawberry.auto title: strawberry.auto author_id: strawberry.Private[int]
@field async def author(self, info: Info) -> "Author": return await info.context.author_loader.load(self.author_id) Common Patterns
One-to-Many Relationships
from collections import defaultdict
async def load_books_by_author(author_ids: list[int]) -> list[list[models.Book]]: """Load all books for multiple authors.""" books_by_author = defaultdict(list) async for book in models.Book.objects.filter(author_id__in=author_ids): books_by_author[book.author_id].append(book)
return [books_by_author[author_id] for author_id in author_ids] Many-to-Many Relationships
async def load_tags(article_ids: list[int]) -> list[list[models.Tag]]: """Load tags for multiple articles.""" tags_by_article = { article.id: await sync_to_async(list)(article.tags.all()) async for article in models.Article.objects.filter( id__in=article_ids ).prefetch_related('tags') }
return [tags_by_article.get(article_id, []) for article_id in article_ids] See Also
- Strawberry DataLoaders Docs - Official Strawberry documentation
- Query Optimizer - Automatic query optimization