GraphQL en Django con strawberry-django frente a DRF

Avanzado
Django
Django
Actualizado: 19/04/2026

REST frente a GraphQL: cuando aporta

DRF expone recursos (/api/productos/, /api/pedidos/{id}/). El cliente recibe siempre la representacion completa del recurso, y si necesita combinar productos con sus pedidos hace dos llamadas. Para una app movil con red lenta, esos round-trips suman.

GraphQL plantea un unico endpoint (/graphql/) al que el cliente envia una query declarando que campos quiere y de que tipos relacionados.

query {
  producto(id: "42") {
    id
    nombre
    precio
    categoria { nombre }
    resenas(first: 3) {
      edges { node { puntuacion comentario } }
    }
  }
}

Una sola peticion HTTP, exactamente los campos solicitados. Para clientes moviles es una victoria de latencia y bateria.

GraphQL gana en: clientes moviles, agregadores que combinan multiples recursos, frontends que evolucionan rapido sin tocar backend. REST gana en: caching HTTP estandar (CDN), endpoints publicos con SLAs por recurso, integraciones simples y herramientas universales (cURL, Postman, OpenAPI).

Por que strawberry-django y no graphene

graphene-django lleva anos siendo el estandar de facto pero su mantenimiento ha decaido y su API basada en clases con mucho boilerplate ya no encaja con el Django moderno. strawberry-django (2024+) usa dataclasses + type hints como FastAPI o Pydantic, soporta async nativo y tiene una integracion mucho mas natural con los modelos.

pip install strawberry-graphql-django
# settings.py
INSTALLED_APPS = [
    "django.contrib.auth",
    "rest_framework",
    "strawberry_django",
    "tienda",
]

Definir tipos a partir de modelos Django

# tienda/types.py
import strawberry
import strawberry_django
from strawberry_django import auto
from typing import List
from tienda.models import Categoria, Producto

@strawberry_django.type(Categoria)
class CategoriaType:
    id:     auto
    nombre: auto
    slug:   auto

@strawberry_django.type(Producto)
class ProductoType:
    id:         auto
    nombre:     auto
    precio:     auto
    stock:      auto
    categoria:  CategoriaType

auto infiere el tipo desde el campo del modelo (CharField -> str, DecimalField -> Decimal, ForeignKey -> tipo relacionado). El esquema se genera automaticamente.

Queries

# tienda/schema.py
import strawberry
import strawberry_django
from typing import List
from tienda.models import Producto
from tienda.types import ProductoType

@strawberry.type
class Query:
    productos:        List[ProductoType] = strawberry_django.field()
    producto:         ProductoType       = strawberry_django.field()

schema = strawberry.Schema(query=Query)
# core/urls.py
from django.urls import path
from strawberry.django.views import GraphQLView
from tienda.schema import schema

urlpatterns = [
    path("graphql/", GraphQLView.as_view(schema=schema)),
]

Lanzas runserver y entras a http://localhost:8000/graphql/. strawberry incluye GraphiQL integrado para probar queries.

Filtros y ordering

import strawberry_django
from strawberry_django import filters, ordering

@strawberry_django.filter(Producto, lookups=True)
class ProductoFilter:
    nombre:   auto
    precio:   auto
    categoria: auto

@strawberry_django.ordering.order(Producto)
class ProductoOrder:
    precio:    auto
    nombre:    auto

@strawberry_django.type(Producto, filters=ProductoFilter, order=ProductoOrder)
class ProductoType:
    id:        auto
    nombre:    auto
    precio:    auto
    categoria: CategoriaType

Permite consultas como:

query {
  productos(
    filters: { precio: { gt: 100 }, categoria: { nombre: { iEndsWith: "phone" } } }
    order:   { precio: ASC }
  ) {
    id
    nombre
  }
}

Paginacion Relay (first/after)

@strawberry.type
class Query:
    productos: strawberry_django.relay.ListConnectionWithTotalCount[ProductoType] = (
        strawberry_django.connection()
    )
query {
  productos(first: 5, after: "cursor-base64") {
    totalCount
    edges {
      cursor
      node { id nombre }
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}

Relay pagination es el estandar GraphQL. Cursor en base64 evita el problema de paginas inconsistentes cuando se inserta entre paginas.

Mutaciones

import strawberry_django
from strawberry_django.mutations import mutations

@strawberry.type
class Mutation:
    crear_producto:    ProductoType = mutations.create(ProductoInput)
    actualizar_producto: ProductoType = mutations.update(ProductoInputPartial)
    borrar_producto:   bool          = mutations.delete()
@strawberry_django.input(Producto)
class ProductoInput:
    nombre:    auto
    precio:    auto
    categoria: auto
mutation {
  crearProducto(data: { nombre: "Mando PS6", precio: "59.90", categoria: 3 }) {
    id
    nombre
  }
}

Resolver el N+1 con DataLoader

Si la query pide producto.categoria para 100 productos, sin precaucion strawberry hace 100 queries SQL. La solucion es DataLoader: agrupa todas las llamadas dentro del mismo tick del event loop y emite una unica query SQL con IN (...).

from strawberry.dataloader import DataLoader

async def cargar_categorias(ids):
    qs = Categoria.objects.filter(id__in=ids).in_bulk()
    return [qs.get(i) for i in ids]

categoria_loader = DataLoader(load_fn=cargar_categorias)

@strawberry_django.type(Producto)
class ProductoType:
    @strawberry.field
    async def categoria(self, root) -> CategoriaType:
        return await categoria_loader.load(root.categoria_id)

strawberry-django incluye desde su version 0.20+ un optimizer automatico que aplica select_related y prefetch_related en funcion de los campos pedidos en la query. Activalo con OptimizerExtension.

Autenticacion JWT

from strawberry.django.views import GraphQLView
from rest_framework_simplejwt.authentication import JWTAuthentication
from django.contrib.auth.models import AnonymousUser

class AuthGraphQLView(GraphQLView):
    def get_context(self, request, response):
        auth = JWTAuthentication()
        try:
            user, _ = auth.authenticate(request) or (AnonymousUser(), None)
        except Exception:
            user = AnonymousUser()
        return {"request": request, "user": user}
@strawberry.type
class Query:
    @strawberry.field
    async def yo(self, info) -> str | None:
        user = info.context["user"]
        return user.username if user.is_authenticated else None

REST y GraphQL conviviendo

Nada impide tener /api/... con DRF y /graphql/ con strawberry en el mismo proyecto. Patron habitual:

  • REST para operaciones publicas con caching CDN, webhooks salientes, integraciones B2B.
  • GraphQL para el frontend interno (web, movil) que necesita combinar muchos recursos.

Ambos comparten modelos, autenticacion (JWT con simplejwt) y logica de negocio en services. Lo unico que cambia es la capa de exposicion.

flowchart LR
    subgraph Cliente
        WEB[Web React]
        MOB[App movil]
        B2B[Cliente B2B]
    end
    subgraph Django
        REST[/api/ DRF]
        GQL[/graphql/ strawberry]
        SVC[Servicios + ORM]
    end
    WEB --> GQL
    MOB --> GQL
    B2B --> REST
    REST --> SVC
    GQL --> SVC

Limitaciones a tener en cuenta

  • Caching HTTP es mas dificil con GraphQL (todo va por POST a /graphql/). Cliente: usa Apollo Client o urql con su propio cache.
  • Rate limiting por endpoint no funciona; necesitas analizar la complejidad de la query (depth limit, query cost). strawberry trae MaxAliasesLimiter y QueryDepthLimiter.
  • Permisos se aplican a nivel de campo, no de URL. Usa strawberry_django.permission o decoradores propios.
Alan Sastre - Autor del tutorial

Alan Sastre

Ingeniero de Software y formador, CEO en CertiDevs

Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Django es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.

Más tutoriales de Django

Explora más contenido relacionado con Django y continúa aprendiendo con nuestros tutoriales gratuitos.

Aprendizajes de esta lección

Comparar GraphQL frente a REST y entender cuando aporta. Instalar strawberry-django y exponer un endpoint /graphql/. Definir tipos a partir de modelos Django con strawberry_django.type. Crear queries y mutations tipadas. Pagina con Relay (first/after). Optimizar N+1 con DataLoader. Autenticar el endpoint con JWT. Integrar GraphQL y DRF en el mismo proyecto.

Cursos que incluyen esta lección

Esta lección forma parte de los siguientes cursos estructurados con rutas de aprendizaje