Criando API GraphQL com Python — 2025 | Python Brasil
Aprenda a criar APIs GraphQL com Python usando Strawberry e FastAPI. Tutorial com queries, mutations, subscriptions e boas praticas.
GraphQL e uma linguagem de consulta para APIs que permite ao cliente solicitar exatamente os dados que precisa. Diferente de REST, onde cada endpoint retorna uma estrutura fixa, GraphQL oferece flexibilidade total nas consultas. Neste guia, a gente vai construir uma API GraphQL completa com Python usando Strawberry e FastAPI.
Por Que GraphQL?
Em APIs REST tradicionais, dois problemas sao comuns: overfetching (receber dados demais) e underfetching (precisar de multiplas requisicoes). GraphQL resolve ambos com uma unica requisicao flexivel.
Por exemplo, para obter o nome de um usuario e seus ultimos posts, em REST voce precisaria de duas requisicoes. Com GraphQL, uma so basta.
Configurando o Projeto
Instale as dependencias:
pip install strawberry-graphql[fastapi] fastapi uvicorn
Estrutura do projeto:
graphql-api/
main.py
schema.py
models.py
resolvers.py
Definindo os Tipos
No GraphQL, tudo comeca com a definicao dos tipos. Com Strawberry, usamos classes Python com decoradores:
# models.py
import strawberry
from typing import Optional
from datetime import datetime
@strawberry.type
class Usuario:
id: int
nome: str
email: str
bio: Optional[str] = None
criado_em: datetime = strawberry.field(default_factory=datetime.now)
@strawberry.type
class Post:
id: int
titulo: str
conteudo: str
publicado: bool
autor_id: int
criado_em: datetime = strawberry.field(default_factory=datetime.now)
@strawberry.type
class Comentario:
id: int
texto: str
autor_id: int
post_id: int
criado_em: datetime = strawberry.field(default_factory=datetime.now)
Banco de Dados Simulado
Para o tutorial, vamos usar dados em memoria:
# resolvers.py
from models import Usuario, Post, Comentario
from datetime import datetime
# Banco de dados em memoria
USUARIOS = [
Usuario(id=1, nome="Ana Silva", email="ana@email.com", bio="Desenvolvedora Python"),
Usuario(id=2, nome="Bruno Costa", email="bruno@email.com", bio="Engenheiro de dados"),
Usuario(id=3, nome="Carla Souza", email="carla@email.com", bio=None),
]
POSTS = [
Post(id=1, titulo="Introducao ao GraphQL", conteudo="GraphQL e uma linguagem...",
publicado=True, autor_id=1),
Post(id=2, titulo="Python e Dados", conteudo="Python e excelente para...",
publicado=True, autor_id=2),
Post(id=3, titulo="Rascunho", conteudo="Trabalho em progresso...",
publicado=False, autor_id=1),
]
COMENTARIOS = [
Comentario(id=1, texto="Otimo artigo!", autor_id=2, post_id=1),
Comentario(id=2, texto="Muito util!", autor_id=3, post_id=1),
]
Criando Queries
Queries sao operacoes de leitura no GraphQL:
# schema.py
import strawberry
from typing import Optional
from models import Usuario, Post, Comentario
from resolvers import USUARIOS, POSTS, COMENTARIOS
@strawberry.type
class Query:
@strawberry.field
def usuarios(self) -> list[Usuario]:
"""Retorna todos os usuarios."""
return USUARIOS
@strawberry.field
def usuario(self, id: int) -> Optional[Usuario]:
"""Busca usuario por ID."""
for u in USUARIOS:
if u.id == id:
return u
return None
@strawberry.field
def posts(self, publicado: Optional[bool] = None) -> list[Post]:
"""Retorna posts, opcionalmente filtrados por status."""
if publicado is not None:
return [p for p in POSTS if p.publicado == publicado]
return POSTS
@strawberry.field
def post(self, id: int) -> Optional[Post]:
"""Busca post por ID."""
for p in POSTS:
if p.id == id:
return p
return None
@strawberry.field
def buscar_posts(self, termo: str) -> list[Post]:
"""Busca posts por termo no titulo ou conteudo."""
termo_lower = termo.lower()
return [
p for p in POSTS
if termo_lower in p.titulo.lower() or termo_lower in p.conteudo.lower()
]
Relacionamentos entre Tipos
A forca do GraphQL esta nos relacionamentos. Vamos conectar usuarios com seus posts:
import strawberry
from typing import Optional
from datetime import datetime
from resolvers import USUARIOS, POSTS, COMENTARIOS
@strawberry.type
class ComentarioType:
id: int
texto: str
criado_em: datetime
@strawberry.field
def autor(self) -> "UsuarioType":
for u in USUARIOS:
if u.id == self.autor_id:
return UsuarioType(
id=u.id, nome=u.nome, email=u.email, bio=u.bio
)
autor_id: strawberry.Private[int]
post_id: strawberry.Private[int]
@strawberry.type
class PostType:
id: int
titulo: str
conteudo: str
publicado: bool
criado_em: datetime
@strawberry.field
def autor(self) -> "UsuarioType":
for u in USUARIOS:
if u.id == self.autor_id:
return UsuarioType(
id=u.id, nome=u.nome, email=u.email, bio=u.bio
)
@strawberry.field
def comentarios(self) -> list[ComentarioType]:
return [
ComentarioType(
id=c.id, texto=c.texto, autor_id=c.autor_id,
post_id=c.post_id, criado_em=c.criado_em
)
for c in COMENTARIOS if c.post_id == self.id
]
autor_id: strawberry.Private[int]
@strawberry.type
class UsuarioType:
id: int
nome: str
email: str
bio: Optional[str] = None
@strawberry.field
def posts(self, publicado: Optional[bool] = None) -> list[PostType]:
resultado = [p for p in POSTS if p.autor_id == self.id]
if publicado is not None:
resultado = [p for p in resultado if p.publicado == publicado]
return [
PostType(
id=p.id, titulo=p.titulo, conteudo=p.conteudo,
publicado=p.publicado, autor_id=p.autor_id, criado_em=p.criado_em
)
for p in resultado
]
Criando Mutations
Mutations sao operacoes de escrita:
@strawberry.input
class CriarPostInput:
titulo: str
conteudo: str
autor_id: int
publicado: bool = False
@strawberry.input
class AtualizarPostInput:
titulo: Optional[str] = None
conteudo: Optional[str] = None
publicado: Optional[bool] = None
@strawberry.type
class Mutation:
@strawberry.mutation
def criar_post(self, input: CriarPostInput) -> PostType:
"""Cria um novo post."""
novo_id = max(p.id for p in POSTS) + 1
novo_post = Post(
id=novo_id,
titulo=input.titulo,
conteudo=input.conteudo,
publicado=input.publicado,
autor_id=input.autor_id
)
POSTS.append(novo_post)
return PostType(
id=novo_post.id, titulo=novo_post.titulo,
conteudo=novo_post.conteudo, publicado=novo_post.publicado,
autor_id=novo_post.autor_id, criado_em=novo_post.criado_em
)
@strawberry.mutation
def atualizar_post(self, id: int, input: AtualizarPostInput) -> Optional[PostType]:
"""Atualiza um post existente."""
for p in POSTS:
if p.id == id:
if input.titulo is not None:
p.titulo = input.titulo
if input.conteudo is not None:
p.conteudo = input.conteudo
if input.publicado is not None:
p.publicado = input.publicado
return PostType(
id=p.id, titulo=p.titulo, conteudo=p.conteudo,
publicado=p.publicado, autor_id=p.autor_id,
criado_em=p.criado_em
)
return None
@strawberry.mutation
def deletar_post(self, id: int) -> bool:
"""Deleta um post por ID."""
for i, p in enumerate(POSTS):
if p.id == id:
POSTS.pop(i)
return True
return False
Montando a Aplicacao
# main.py
import strawberry
from fastapi import FastAPI
from strawberry.fastapi import GraphQLRouter
# Importar Query e Mutation
schema = strawberry.Schema(query=Query, mutation=Mutation)
# Criar app FastAPI com GraphQL
app = FastAPI(title="API GraphQL com Python")
graphql_app = GraphQLRouter(schema)
app.include_router(graphql_app, prefix="/graphql")
@app.get("/")
def index():
return {"mensagem": "Acesse /graphql para usar a API GraphQL"}
Execute com:
uvicorn main:app --reload
Acesse http://localhost:8000/graphql para abrir o GraphiQL, a interface interativa do GraphQL.
Exemplos de Consultas
# Buscar usuario com seus posts
query {
usuario(id: 1) {
nome
email
posts(publicado: true) {
titulo
comentarios {
texto
autor {
nome
}
}
}
}
}
# Criar um novo post
mutation {
criarPost(input: {
titulo: "Novo Post"
conteudo: "Conteudo do post..."
autorId: 1
publicado: true
}) {
id
titulo
autor {
nome
}
}
}
Tratamento de Erros
import strawberry
@strawberry.type
class ErroType:
mensagem: str
codigo: str
PostResultado = strawberry.union("PostResultado", [PostType, ErroType])
@strawberry.type
class MutationComErros:
@strawberry.mutation
def criar_post(self, input: CriarPostInput) -> PostResultado:
autor = next((u for u in USUARIOS if u.id == input.autor_id), None)
if not autor:
return ErroType(mensagem="Autor nao encontrado", codigo="AUTOR_NAO_ENCONTRADO")
# ... criar post normalmente
Boas Praticas
Ao construir APIs GraphQL com Python, siga estas recomendacoes:
- Use DataLoaders para evitar o problema N+1 em relacionamentos
- Implemente paginacao com cursor-based pagination para listas grandes
- Adicione autenticacao e autorizacao nas mutations
- Limite a profundidade das queries para prevenir abusos
- Documente os tipos e campos com descricoes claras
- Use input types para mutations com muitos parametros
- Implemente rate limiting para proteger a API
Conclusao
GraphQL com Python usando Strawberry e FastAPI e uma combinacao moderna e produtiva para construir APIs flexiveis. Com tipos seguros, queries aninhadas e mutations bem definidas, voce oferece aos consumidores da API total controle sobre os dados que recebem. Comece com um schema simples e evolua conforme as necessidades do seu projeto.
Equipe Python Brasil
Contribuidor do Python Brasil — Aprenda Python em Português