Para criar uma API com Django REST Framework (DRF), instale Django e DRF, modele o recurso, crie um serializer, exponha um ModelViewSet e registre-o em um router. Esse fluxo entrega operações CRUD e uma interface navegável com pouco código, mas uma API pronta para produção também exige validação, autenticação, permissões, paginação, testes e respostas de erro consistentes.
Este guia constrói uma API executável de tarefas. O exemplo usa SQLite localmente e autenticação por token, mantendo as responsabilidades claras para que banco ou autenticação possam evoluir depois. Se o framework ainda for novo para você, consulte primeiro o guia completo de Django.
Ambiente e projeto
Crie um ambiente isolado, instale as dependências e inicie o projeto:
python -m venv .venv
## Linux/macOS: source .venv/bin/activate
## Windows PowerShell: .venv\Scripts\Activate.ps1
python -m pip install Django djangorestframework
django-admin startproject config .
python manage.py startapp tasks
Ambientes virtuais evitam colisões; o guia de venv em Python explica o isolamento. Em config/settings.py, registre aplicações e configure defaults:
INSTALLED_APPS += [ # Preserva as aplicações geradas pelo Django.
"rest_framework",
"rest_framework.authtoken",
"tasks",
]
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": [
"rest_framework.authentication.TokenAuthentication",
],
"DEFAULT_PERMISSION_CLASSES": [
"rest_framework.permissions.IsAuthenticated",
],
"DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
"PAGE_SIZE": 20,
}
Defaults seguros evitam publicar acidentalmente uma view nova. Autenticação identifica o usuário; permissão decide se ele pode executar a ação. Elas não são equivalentes.
Modelo e migrações
Em tasks/models.py, represente propriedade explicitamente:
from django.conf import settings
from django.db import models
class Task(models.Model):
owner = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name="tasks",
)
title = models.CharField(max_length=200)
completed = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ["-created_at"]
def __str__(self) -> str:
return self.title
Execute python manage.py makemigrations e python manage.py migrate. O relacionamento com owner é uma barreira contra vazamento entre contas, desde que a consulta seja filtrada. Para produção, considere PostgreSQL com Python e índices orientados às consultas reais.
Serializer e validação
O serializer converte modelos para tipos renderizáveis, valida entrada e controla campos graváveis. Crie tasks/serializers.py:
from rest_framework import serializers
from .models import Task
class TaskSerializer(serializers.ModelSerializer):
class Meta:
model = Task
fields = ["id", "title", "completed", "created_at"]
read_only_fields = ["id", "created_at"]
def validate_title(self, value: str) -> str:
title = value.strip()
if len(title) < 3:
raise serializers.ValidationError(
"O título deve ter pelo menos 3 caracteres."
)
return title
Não exponha owner como campo gravável: o servidor o obtém de request.user. Também não use fields = "__all__" por conveniência, pois um campo sensível adicionado ao modelo pode aparecer na API sem revisão. Campos explícitos funcionam como documentação e como uma lista segura do contrato público.
ViewSet, isolamento e rotas
Crie tasks/views.py:
from rest_framework import viewsets
from .models import Task
from .serializers import TaskSerializer
class TaskViewSet(viewsets.ModelViewSet):
serializer_class = TaskSerializer
def get_queryset(self):
return Task.objects.filter(owner=self.request.user)
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
get_queryset() impede listar e acessar objetos de outro usuário, inclusive pela URL de detalhe. Filtrar apenas a listagem seria uma falha de autorização por objeto. Registre as rotas em config/urls.py:
from django.contrib import admin
from django.urls import include, path
from rest_framework.authtoken.views import obtain_auth_token
from rest_framework.routers import DefaultRouter
from tasks.views import TaskViewSet
router = DefaultRouter()
router.register("tasks", TaskViewSet, basename="task")
urlpatterns = [
path("admin/", admin.site.urls),
path("api/", include(router.urls)),
path("api/token/", obtain_auth_token, name="api-token"),
]
O router cria GET/POST /api/tasks/ e GET/PUT/PATCH/DELETE /api/tasks/{id}/. Ele também mantém nomes de URL consistentes, importantes para testes e clientes que resolvem rotas dinamicamente.
Autenticar e testar manualmente
Crie um usuário com python manage.py createsuperuser, inicie python manage.py runserver e obtenha um token:
curl -X POST http://127.0.0.1:8000/api/token/ \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"senha-local"}'
curl -X POST http://127.0.0.1:8000/api/tasks/ \
-H "Authorization: Token SEU_TOKEN" \
-H "Content-Type: application/json" \
-d '{"title":"Revisar a API"}'
No PowerShell, chame curl.exe explicitamente e use a crase para continuar a linha:
curl.exe -X POST http://127.0.0.1:8000/api/token/ `
-H "Content-Type: application/json" `
-d '{"username":"admin","password":"senha-local"}'
curl.exe -X POST http://127.0.0.1:8000/api/tasks/ `
-H "Authorization: Token SEU_TOKEN" `
-H "Content-Type: application/json" `
-d '{"title":"Revisar a API"}'
Use credenciais apenas locais no exemplo. Tokens devem trafegar por HTTPS, permanecer fora de logs e repositórios e poder ser revogados. A autenticação por token nativa é simples; requisitos de expiração, rotação ou clientes terceiros podem justificar OAuth2 ou outro mecanismo. A documentação de autenticação do DRF compara as opções.
Testes da API
Testes comprovam autenticação, validação e isolamento. Em tasks/tests.py:
from django.contrib.auth import get_user_model
from rest_framework import status
from rest_framework.test import APITestCase
from .models import Task
class TaskApiTests(APITestCase):
def setUp(self):
self.user = get_user_model().objects.create_user(
username="ana", password="senha-forte-local"
)
self.client.force_authenticate(self.user)
def test_create_task_assigns_owner(self):
response = self.client.post("/api/tasks/", {"title": "Testar endpoint"})
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertTrue(Task.objects.filter(owner=self.user).exists())
def test_rejects_short_title(self):
response = self.client.post("/api/tasks/", {"title": "x"})
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
Execute python manage.py test. Adicione casos sem credencial, acesso ao ID de outra conta, paginação e métodos proibidos. O guia de testes com pytest ajuda se o projeto preferir pytest.
Erros, desempenho e práticas de produção
O DRF transforma ValidationError em resposta 400 estruturada. Não capture Exception para sempre devolver 200, nem exponha traceback ao cliente. Retorne 401 para autenticação ausente ou inválida, 403 quando a identidade não tem permissão, 404 para recurso invisível e 400 para dados inválidos. Registre um identificador de correlação e detalhes internos, sem token ou senha; veja logging em Python.
Evite N+1 usando select_related() e prefetch_related() quando serializers atravessarem relações. Paginação limita custo, mas também defina throttling, limites no proxy e timeouts. Versione contratos quando houver consumidores externos. Use CORS somente para origens necessárias; CORS não substitui autenticação. Mantenha DEBUG=False, SECRET_KEY em variável de ambiente, ALLOWED_HOSTS restrito e HTTPS no deploy. As recomendações de implantação do Django oferecem uma lista de verificação.
Fontes oficiais
Perguntas frequentes
Serializer é o mesmo que formulário Django?
Não. Ambos validam dados, mas serializers representam recursos de API, negociam formatos e criam ou atualizam objetos a partir de payloads.
ViewSet ou APIView: qual escolher?
Use ModelViewSet para CRUD convencional. Escolha APIView quando o endpoint representar uma operação que não se encaixa naturalmente em um recurso CRUD.
TokenAuthentication é suficiente para produção?
Pode ser em sistemas simples se houver HTTPS, revogação e armazenamento seguro. Expiração, rotação e escopos podem exigir uma solução mais completa.
Como impedir que um usuário veja dados de outro?
Filtre sempre o queryset pela identidade e teste listagem e detalhe. Para regras adicionais, implemente permissões por objeto sem substituir esse isolamento básico.
Conclusão
Uma API DRF sólida nasce de limites explícitos: serializer controla entrada e saída, queryset restringe propriedade, autenticação identifica o cliente e permissões autorizam ações. O CRUD curto é apenas o começo. Com defaults fechados, validação, paginação, testes de isolamento, erros coerentes e configuração segura de produção, o mesmo projeto pode evoluir sem transformar conveniência em exposição de dados.