Skip to content

Latest commit

 

History

History
514 lines (394 loc) · 10.9 KB

File metadata and controls

514 lines (394 loc) · 10.9 KB
title Queries

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.

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.

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
@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.

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

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

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.

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.

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.

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)
@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.

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.

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

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