Guia

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

4 min de leitura

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.