🌐 Detecting your location…

Como construir uma API REST com FastAPI

⏱️6 min read  ·  1,206 words

How to Build a REST API with FastAPI

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.

Índice

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.

PT
Equipe TechPulse
Publicado em 1º de agosto de 2026


MD Rafikul Islam

Written by

MD Rafikul Islam is a software developer and the editor of TechPulse. He writes about developer tooling, hardware, and the practical decisions that come up in day-to-day engineering work — which laptop to buy, which framework to commit to, why a build broke at 2am. He tests the tools he writes about and says plainly when something is not worth the money. Corrections and corrections requests are welcome at rony.yf25@gmail.com.

✍️ Leave a Comment

Your email address will not be published. Required fields are marked *

🌐 Read in:🇬🇧 English🇩🇪 Deutsch🇧🇷 Português🇸🇦 العربية🇮🇳 हिन्दी🇧🇩 বাংলা