Comece com Python FastAPI: seu primeiro passo concreto hoje
Python FastAPI: o essencial em um artigo — código real, diagramas e etapas concretas, extraídos de um curso de 32 lições.
A melhor forma de aprender Python FastAPI é fazendo. Este artigo te dá o pontapé inicial com trechos práticos extraídos de um curso de 32 lições — o suficiente para obter um primeiro resultado já hoje.
- Introdução e instalação
- Rotas e métodos
- Pydantic e validação
- Injeção de dependências
- Banco de dados
Por que FastAPI?
FastAPI em uma frase
Framework Python moderno para construir APIs REST rápidas, tipadas e com documentação automática, baseado em Starlette (ASGI) e Pydantic.
Os 4 superpoderes
| Poder | Detalhe |
|---|---|
| Performance | Async nativo via ASGI/uvicorn, comparável a Node/Go |
| Type safety | Type hints Python -> validação em runtime via Pydantic |
| Docs auto | Swagger UI + ReDoc gerados automaticamente |
| Dev experience | Auto-completar no IDE, erros claros, dependências injetáveis |
Comparação Flask vs FastAPI vs Django
| Critério | Flask | FastAPI | Django |
|---|---|---|---|
| Estilo | Micro | Micro (foco em API) | Full-stack |
| Async nativo | Não (extensões) | SIM | Parcial (DRF não) |
| Type hints | Não | SIM (Pydantic) | Não |
| Docs auto | Não | SIM (Swagger) | Via DRF (manual) |
| ORM incluso | Não | Não | SIM (Django ORM) |
| Aprendizado | Muito fácil | Fácil | Moderado |
| Performance (req/s) | ~5k | ~20-30k | ~3k |
Pequeno exemplo: "Hello World"
# Flask from flask import Flask app = Flask(__name__) @app.route("/items/<int:item_id>") def get_item(item_id): return {"id": item_id}
# FastAPI from fastapi import FastAPI app = FastAPI() @app.get("/items/{item_id}") def get_item(item_id: int): return {"id": item_id} # + validação automática, doc automática, type hints
Quando escolher FastAPI?
- Backend API REST (microsserviços)
- Servir um modelo de ML
- Necessidade de performance (async)
- Equipe que aprecia tipagem
- Documentação OpenAPI exigida
- App full-stack com templates HTML pesados (Django)
- Pequeno script pontual (Flask basta)
- Equipe já produtiva com outro framework
Quem usa FastAPI?
O ecossistema
| Componente | Papel |
|---|---|
| Starlette | Framework ASGI subjacente |
| Pydantic | Validação e serialização |
| uvicorn | Servidor ASGI (dev) |
| gunicorn | Process manager (prod) |
| SQLAlchemy | ORM (comum) |
| Alembic | Migrações de BD |
Resumo
- FastAPI = performance + type safety + docs automáticas
- Baseado em Starlette (ASGI) e Pydantic
- Ideal para APIs e microsserviços modernos
- Async nativo, ~5x mais rápido que Flask
Próxima etapa: Parte 2 — Instalar o FastAPI
Deploy Docker e testes
Projeto final • Docker • testes • CI/CD
Testes de integração chave
# tests/test_posts.py import pytest def test_create_post_requires_auth(client): r = client.post("/posts", json={"title": "Test", "content": "Hello world"}) assert r.status_code == 401 def test_create_post(client, auth_headers): r = client.post("/posts", headers=auth_headers, json={ "title": "Meu primeiro post", "content": "Conteúdo interessante aqui", "tags": ["python", "fastapi"], "published": True }) assert r.status_code == 201 data = r.json() assert data["slug"] == "meu-primeiro-post" assert len(data["tags"]) == 2 def test_list_posts_only_published(client, auth_headers): client.post("/posts", headers=auth_headers, json={"title": "Rascunho", "content": "...", "published": False}) client.post("/posts", headers=auth_headers, json={"title": "Publicado", "content": "...", "published": True}) r = client.get("/posts") assert len(r.json()["items"]) == 1 def test_only_author_can_delete(client): h1 = make_user_and_login(client, "alice", "alice@x.com") h2 = make_user_and_login(client, "bob", "bob@x.com") r = client.post("/posts", headers=h1, json={"title": "Meu", "content": "...", "published": True}) slug = r.json()["slug"] # Bob tenta excluir o post da Alice r = client.delete(f"/posts/{slug}", headers=h2) assert r.status_code == 403 # Alice consegue r = client.delete(f"/posts/{slug}", headers=h1) assert r.status_code == 204
conftest.py final
import pytest from fastapi.testclient import TestClient from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from app.main import app from app.db import Base, get_db TestEngine = create_engine("sqlite:///./test.db", connect_args={"check_same_thread": False}) TestSession = sessionmaker(bind=TestEngine) def override_db(): db = TestSession() try: yield db finally: db.close() app.dependency_overrides[get_db] = override_db @pytest.fixture(autouse=True) def reset_db(): Base.metadata.create_all(bind=TestEngine) yield Base.metadata.drop_all(bind=TestEngine) @pytest.fixture def client(): return TestClient(app) @pytest.fixture def auth_headers(client): client.post("/auth/register", json={ "email": "test@x.com", "username": "test", "password": "secret123" }) r = client.post("/auth/login", data={ "username": "test@x.com", "password": "secret123" }) return {"Authorization": f'Bearer {r.json()["access_token"]}'}
Dockerfile
FROM python:3.11-slim AS builder WORKDIR /build COPY requirements.txt . RUN pip install --user --no-cache-dir -r requirements.txt FROM python:3.11-slim RUN useradd -m app USER app WORKDIR /home/app ENV PATH=/home/app/.local/bin:$PATH COPY --from=builder /root/.local /home/app/.local COPY --chown=app:app . . EXPOSE 8000 HEALTHCHECK CMD curl -fsS http://localhost:8000/health || exit 1 ENTRYPOINT ["./entrypoint.sh"] CMD ["gunicorn", "-c", "gunicorn.conf.py", "app.main:app"]
docker-compose.yml
services:
api:
build: .
ports:
- "8000:8000"
environment:
DATABASE_URL: postgresql://blog:pwd@db/blogdb
SECRET_KEY: change-me-in-prod
REFRESH_SECRET_KEY: change-me-too
depends_on:
db: {condition: service_healthy}
restart: unless-stopped
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: blog
POSTGRES_PASSWORD: pwd
POSTGRES_DB: blogdb
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U blog"]
interval: 5s
retries: 5
volumes:
pgdata:CI/CD GitHub Actions
# .github/workflows/ci.yml name: CI on: push: branches: [main] pull_request: jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: {python-version: "3.11"} - run: pip install -r requirements.txt - run: pip install pytest pytest-cov httpx - run: pytest --cov=app --cov-report=xml - uses: codecov/codecov-action@v4 build: needs: test runs-on: ubuntu-latest if: github.ref == "refs/heads/main" steps: - uses: actions/checkout@v4 - uses: docker/setup-buildx-action@v3 - uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - uses: docker/build-push-action@v5 with: push: true tags: ghcr.io/${{ github.repository }}:latest
Executar em produção
# Local completo docker-compose up -d docker-compose logs -f api docker-compose exec api alembic upgrade head # Visitar # http://localhost:8000/docs # http://localhost:8000/redoc
WebSockets com FastAPI
Hello WebSocket
from fastapi import FastAPI, WebSocket, WebSocketDisconnect app = FastAPI() @app.websocket("/ws") async def websocket_endpoint(ws: WebSocket): await ws.accept() try: while True: data = await ws.receive_text() await ws.send_text(f"Echo: {data}") except WebSocketDisconnect: print("Cliente desconectado")
Teste rápido com wscat
npm install -g wscat
wscat -c ws://localhost:8000/ws
> Hello
< Echo: HelloConnection Manager
class ConnectionManager: def __init__(self): self.active: list[WebSocket] = [] async def connect(self, ws: WebSocket): await ws.accept() self.active.append(ws) def disconnect(self, ws: WebSocket): self.active.remove(ws) async def broadcast(self, message: str): for connection in self.active: try: await connection.send_text(message) except: pass manager = ConnectionManager()
Chat broadcast
@app.websocket("/chat/{username}") async def chat(ws: WebSocket, username: str): await manager.connect(ws) await manager.broadcast(f">> {username} entrou") try: while True: msg = await ws.receive_text() await manager.broadcast(f"[{username}] {msg}") except WebSocketDisconnect: manager.disconnect(ws) await manager.broadcast(f"<< {username} saiu")
Enviar JSON
@app.websocket("/json") async def json_ws(ws: WebSocket): await ws.accept() try: while True: data = await ws.receive_json() await ws.send_json({"echo": data, "server_time": str(datetime.now())}) except WebSocketDisconnect: pass
Autenticação WebSocket
from fastapi import WebSocketException, status @app.websocket("/ws") async def ws(ws: WebSocket, token: str): try: payload = decode_token(token) user_id = payload["sub"] except: raise WebSocketException(code=status.WS_1008_POLICY_VIOLATION) await ws.accept() await ws.send_text(f"Conectado como {user_id}") ... # Cliente: ws://localhost:8000/ws?token=eyJ...
Este artigo cobre os trechos mais úteis — o curso completo Python FastAPI (10 capítulos, 32 lições, exercícios corrigidos e projeto final) leva você até o fim.
./acessar-o-curso-completo curso gratuito: Vibe CodingFAQ
Quanto tempo para aprender Python FastAPI?
Precisa de pré-requisitos?
Por onde começar na prática?
📬 Quer receber este tipo de guia toda semana? Inscreva-se gratuitamente — código real, zero enrolação.