
FastAPI se tornou a escolha padrão para novas APIs Python, e por um bom motivo: documentos OpenAPI automáticos, suporte assíncrono real e validação de solicitação fornecida por Pydantic que detecta entradas incorretas antes que elas afetem sua lógica de negócios. Este tutorial cria uma API REST funcional a partir de uma pasta vazia para um serviço CRUD baseado em banco de dados com estrutura pronta para autenticação.
📋 Table of Contents
Índice
- Configuração do Projeto
- Sua primeira rota
- Modelos de solicitação e resposta com Pydantic
- Adicionando um banco de dados com SQLModel
- Construindo endpoints CRUD completos
- Tratamento de erros
- Documentos Automáticos
- Em execução em produção
- Perguntas frequentes
Configuração do Projeto
Crie um ambiente virtual e instale os pacotes principais:
mkdir fastapi-todo && cd fastapi-todo
python3 -m venv venv
source venv/bin/activate
pip install fastapi uvicorn[standard] sqlmodel
uvicorné o servidor ASGI que realmente executa seu aplicativo;sqlmodelcombina SQLAlchemy e Pydantic para uma camada de banco de dados que compartilha modelos com seus esquemas de API.
Sua primeira rota
Criarmain.py:
from fastapi import FastAPI
app = FastAPI(title="Todo API")
@app.get("/")
def read_root():
return {"message": "Todo API is running"}
Execute-o com recarga a quente:
uvicorn main:app --reload
Visitehttp://127.0.0.1:8000e você verá a resposta JSON. Visite/docse você já tem uma UI interativa do Swagger – gerada apenas a partir de assinaturas de funções.
Modelos de solicitação e resposta com Pydantic
Em vez de verificar manualmenterequest.json()para campos ausentes, defina um modelo e deixe o FastAPI validar para você:
from pydantic import BaseModel
class TodoCreate(BaseModel):
title: str
done: bool = False
@app.post("/todos")
def create_todo(todo: TodoCreate):
return {"title": todo.title, "done": todo.done}
Enviar uma solicitação faltandotitlee FastAPI retorna um erro 422 estruturado automaticamente – sem necessidade de código extra. Essa camada de validação é o maior motivo pelo qual as equipes migraram do Flask.
Adicionando um banco de dados com SQLModel
SQLModel permite que uma classe sirva como tabela de banco de dados e esquema de API:
from sqlmodel import SQLModel, Field, create_engine, Session
class Todo(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
title: str
done: bool = False
engine = create_engine("sqlite:///todos.db")
def init_db():
SQLModel.metadata.create_all(engine)
def get_session():
with Session(engine) as session:
yield session
Ligueinit_db()na inicialização usando o manipulador de vida útil do FastAPI:
from contextlib import asynccontextmanager
@asynccontextmanager
async def lifespan(app: FastAPI):
init_db()
yield
app = FastAPI(title="Todo API", lifespan=lifespan)
Construindo endpoints CRUD completos
Com o modelo e a dependência de sessão implementados, as quatro operações CRUD são curtas e legíveis:
from fastapi import Depends, HTTPException
from sqlmodel import select
@app.post("/todos", response_model=Todo)
def create_todo(todo: Todo, session: Session = Depends(get_session)):
session.add(todo)
session.commit()
session.refresh(todo)
return todo
@app.get("/todos", response_model=list[Todo])
def list_todos(session: Session = Depends(get_session)):
return session.exec(select(Todo)).all()
@app.get("/todos/{todo_id}", response_model=Todo)
def get_todo(todo_id: int, session: Session = Depends(get_session)):
todo = session.get(Todo, todo_id)
if not todo:
raise HTTPException(status_code=404, detail="Todo not found")
return todo
@app.put("/todos/{todo_id}", response_model=Todo)
def update_todo(todo_id: int, data: Todo, session: Session = Depends(get_session)):
todo = session.get(Todo, todo_id)
if not todo:
raise HTTPException(status_code=404, detail="Todo not found")
todo.title = data.title
todo.done = data.done
session.add(todo)
session.commit()
session.refresh(todo)
return todo
@app.delete("/todos/{todo_id}")
def delete_todo(todo_id: int, session: Session = Depends(get_session)):
todo = session.get(Todo, todo_id)
if not todo:
raise HTTPException(status_code=404, detail="Todo not found")
session.delete(todo)
session.commit()
return {"ok": True}
AvisoDepends(get_session)— Sistema de injeção de dependência do FastAPI. Correget_sessionpara cada solicitação, entrega o resultado à sua função e limpa-o posteriormente. O mesmo padrão é dimensionado para autenticação, limitação de taxa e paginação sem repetir o padrão em cada manipulador.
Tratamento de erros
HTTPExceptioncobre a maioria dos casos, mas para erros específicos do domínio, registre um manipulador de exceções personalizado para que as respostas permaneçam consistentes em toda a API:
from fastapi.responses import JSONResponse
from fastapi import Request
class TodoLimitError(Exception):
pass
@app.exception_handler(TodoLimitError)
def limit_handler(request: Request, exc: TodoLimitError):
return JSONResponse(status_code=400, content={"error": "todo limit reached"})
Documentos Automáticos
Cada rota que você escreveu já está documentada em/docs(Arrogância) e/redoc(ReDoc), gerado a partir de seus modelos Pydantic e dicas de tipo. Adicione descrições com uma docstring ouField(description=...)e eles aparecem no esquema gerado automaticamente – sem OpenAPI YAML separado para manter manualmente.
Em execução em produção
Para produção, execute o Uvicorn atrás do Gunicorn com vários trabalhadores ou use o modo multi-trabalhador do próprio Uvicorn:
gunicorn main:app -w 4 -k uvicorn.workers.UvicornWorker --bind 0.0.0.0:8000
Coloque-o atrás do Nginx ou de um balanceador de carga gerenciado para terminação TLS e troque SQLite por PostgreSQL alterando ocreate_enginestring de conexão — a API do SQLModel não muda.
Se você estiver combinando esta API com um front-end moderno, nossoGuia Next.js 15 e PostgreSQLcobre o lado do cliente e para fundamentos assíncronos em JavaScript, consultenosso mergulho profundo em async/await.
Perguntas frequentes
O FastAPI é mais rápido que o Flask?
O suporte assíncrono do FastAPI e a base Starlette o tornam substancialmente mais rápido sob carga simultânea, especialmente para endpoints vinculados a E/S, como chamadas de banco de dados ou solicitações externas de API.
Preciso de definição assíncrona para cada rota?
Não. FastAPI é executado de forma síncronadefroteia em um pool de threads automaticamente. Usarasync defquando você chama bibliotecas assíncronas (drivers de banco de dados assíncronos, httpx) para obter benefícios reais de simultaneidade.
Qual é a diferença entre Pydantic e SQLModel?
Pydantic valida formatos de dados para solicitações e respostas. SQLModel estende modelos Pydantic para que a mesma classe também possa mapear para uma tabela de banco de dados, evitando definições de esquema duplicadas.
Como adiciono autenticação?
Use FastAPIsOAuth2PasswordBearercom injeção de dependência — aget_current_usera dependência valida um JWT e é adicionada a qualquer rota que precise de proteção, mantendo a lógica de autenticação em um só lugar.
FastAPI pode lidar com WebSockets?
Sim, nativamente. Defina uma rota com@app.websocket("/ws")e umasync defmanipulador que aguardawebsocket.receive_text()em um loop.
Pronto para construir?
Clone o código acima em um novo projeto, executeuvicorn main:app --reloade comece a estender o modelo Todo com campos como datas de vencimento ou prioridade. O padrão de injeção de dependência que você usou para a sessão de banco de dados é o mesmo que você usará posteriormente com autenticação, armazenamento em cache e limitação de taxa.
🔗 Share this article
✍️ Leave a Comment