API com FastAPI: Guia Completo
Aprenda a criar APIs com FastAPI em Python. Rotas, validação com Pydantic, banco de dados, autenticação e documentação automática
Introdução
FastAPI é um framework moderno para criar APIs em Python, conhecido por sua velocidade, validação automática e documentação interativa gerada automaticamente. Criado por Sebastián Ramírez, ele combina type hints do Python com Pydantic para oferecer uma experiência de desenvolvimento produtiva e segura.
Neste guia, vamos criar uma API RESTful completa com FastAPI, cobrindo rotas, validação, banco de dados e autenticação.
Por que FastAPI?
- Desempenho: tão rápido quanto Node.js e Go, graças ao suporte assíncrono nativo
- Validação automática: usa type hints e Pydantic para validar dados de entrada e saída
- Documentação automática: gera Swagger UI e ReDoc sem configuração adicional
- Editor friendly: autocompletar funciona perfeitamente por causa dos type hints
Instalação
Crie um ambiente virtual e instale o FastAPI:
python3 -m venv venv
source venv/bin/activate
pip install "fastapi[standard]"
O pacote fastapi[standard] inclui o Uvicorn (servidor ASGI) e outras dependências comuns.
Sua primeira API
Crie main.py:
from fastapi import FastAPI
app = FastAPI(title="API de Tarefas", version="1.0.0")
@app.get("/")
def raiz():
return {"mensagem": "Bem-vindo a API de Tarefas"}
@app.get("/saude")
def verificar_saude():
return {"status": "ok"}
Execute o servidor:
uvicorn main:app --reload
Acesse http://127.0.0.1:8000 para ver a resposta JSON. A documentação interativa está em http://127.0.0.1:8000/docs (Swagger UI) e http://127.0.0.1:8000/redoc (ReDoc).
Modelos com Pydantic
O Pydantic valida os dados automaticamente usando type hints:
from pydantic import BaseModel, Field
from datetime import datetime
class TarefaCriar(BaseModel):
titulo: str = Field(..., min_length=1, max_length=200)
descricao: str = ""
prioridade: int = Field(default=1, ge=1, le=5)
class TarefaResposta(BaseModel):
id: int
titulo: str
descricao: str
prioridade: int
concluida: bool
criada_em: datetime
model_config = {"from_attributes": True}
O Field permite definir validações como comprimento mínimo/máximo e intervalos numéricos.
Rotas CRUD completas
from fastapi import FastAPI, HTTPException, status
app = FastAPI(title="API de Tarefas")
# Banco de dados em memoria (para exemplo)
tarefas_db = {}
contador_id = 0
@app.post("/tarefas", response_model=TarefaResposta, status_code=status.HTTP_201_CREATED)
def criar_tarefa(tarefa: TarefaCriar):
global contador_id
contador_id += 1
nova_tarefa = {
"id": contador_id,
"titulo": tarefa.titulo,
"descricao": tarefa.descricao,
"prioridade": tarefa.prioridade,
"concluida": False,
"criada_em": datetime.now(),
}
tarefas_db[contador_id] = nova_tarefa
return nova_tarefa
@app.get("/tarefas", response_model=list[TarefaResposta])
def listar_tarefas(concluida: bool | None = None):
tarefas = list(tarefas_db.values())
if concluida is not None:
tarefas = [t for t in tarefas if t["concluida"] == concluida]
return tarefas
@app.get("/tarefas/{tarefa_id}", response_model=TarefaResposta)
def obter_tarefa(tarefa_id: int):
if tarefa_id not in tarefas_db:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Tarefa nao encontrada"
)
return tarefas_db[tarefa_id]
@app.put("/tarefas/{tarefa_id}", response_model=TarefaResposta)
def atualizar_tarefa(tarefa_id: int, tarefa: TarefaCriar):
if tarefa_id not in tarefas_db:
raise HTTPException(status_code=404, detail="Tarefa nao encontrada")
tarefas_db[tarefa_id].update({
"titulo": tarefa.titulo,
"descricao": tarefa.descricao,
"prioridade": tarefa.prioridade,
})
return tarefas_db[tarefa_id]
@app.patch("/tarefas/{tarefa_id}/concluir")
def concluir_tarefa(tarefa_id: int):
if tarefa_id not in tarefas_db:
raise HTTPException(status_code=404, detail="Tarefa nao encontrada")
tarefas_db[tarefa_id]["concluida"] = True
return {"mensagem": "Tarefa concluida"}
@app.delete("/tarefas/{tarefa_id}", status_code=status.HTTP_204_NO_CONTENT)
def excluir_tarefa(tarefa_id: int):
if tarefa_id not in tarefas_db:
raise HTTPException(status_code=404, detail="Tarefa nao encontrada")
del tarefas_db[tarefa_id]
Query parameters e filtros
O FastAPI converte parâmetros de função em query parameters automaticamente:
@app.get("/tarefas")
def listar_tarefas(
concluida: bool | None = None,
prioridade: int | None = None,
busca: str | None = None,
pagina: int = 1,
limite: int = 10,
):
tarefas = list(tarefas_db.values())
if concluida is not None:
tarefas = [t for t in tarefas if t["concluida"] == concluida]
if prioridade is not None:
tarefas = [t for t in tarefas if t["prioridade"] == prioridade]
if busca:
tarefas = [t for t in tarefas if busca.lower() in t["titulo"].lower()]
inicio = (pagina - 1) * limite
return tarefas[inicio:inicio + limite]
Acesse: http://127.0.0.1:8000/tarefas?concluida=false&prioridade=3&pagina=1
Banco de dados com SQLAlchemy
Para persistir dados, use SQLAlchemy. Instale:
pip install sqlalchemy
Crie database.py:
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, DeclarativeBase
SQLALCHEMY_DATABASE_URL = "sqlite:///./tarefas.db"
engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False})
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
class Base(DeclarativeBase):
pass
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
Crie models.py:
from sqlalchemy import Column, Integer, String, Boolean, DateTime
from datetime import datetime
from database import Base
class Tarefa(Base):
__tablename__ = "tarefas"
id = Column(Integer, primary_key=True, index=True)
titulo = Column(String(200), nullable=False)
descricao = Column(String, default="")
prioridade = Column(Integer, default=1)
concluida = Column(Boolean, default=False)
criada_em = Column(DateTime, default=datetime.utcnow)
Use o Depends para injetar a sessão do banco:
from fastapi import Depends
from sqlalchemy.orm import Session
from database import get_db
import models
@app.post("/tarefas", response_model=TarefaResposta, status_code=201)
def criar_tarefa(tarefa: TarefaCriar, db: Session = Depends(get_db)):
db_tarefa = models.Tarefa(**tarefa.model_dump())
db.add(db_tarefa)
db.commit()
db.refresh(db_tarefa)
return db_tarefa
Middleware e CORS
Para permitir que frontends em outros domínios acessem a API:
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:3000"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
Tratamento de erros
Crie handlers personalizados para exceções:
from fastapi.responses import JSONResponse
@app.exception_handler(ValueError)
async def valor_invalido_handler(request, exc):
return JSONResponse(
status_code=400,
content={"detalhe": str(exc)},
)
Estrutura de projeto recomendada
Para projetos maiores, organize em módulos:
meu-projeto/
app/
__init__.py
main.py
database.py
models.py
schemas.py
routers/
tarefas.py
usuarios.py
tests/
pyproject.toml
Use APIRouter para separar rotas em arquivos diferentes.
Conclusão
FastAPI combina velocidade, segurança de tipos e documentação automática de forma que nenhum outro framework Python oferece. A validação com Pydantic elimina uma classe inteira de bugs, e a documentação interativa facilita o consumo da API por frontends e equipes. Se você está criando APIs em Python, o FastAPI é a escolha mais moderna e produtiva disponível atualmente.