ULC Lab · implementação reproduzível
Laboratório: API REST de Usuários com FastAPI, autenticação JWT e SQLite
Você implementará uma API REST completa para cadastro e autenticação de usuários, protegida por JWT, usando FastAPI e SQLite. Em cerca de 2 horas, será possível criar, autenticar e consultar usuários com validação de segurança básica.

- Duração estimada
- 120 min
- Passos
- 10
- Validações
- 5
- Versões da documentação consultada
- FastAPI a versão estável atual · Uvicorn a versão estável atual · SQLAlchemy a versão estável atual · Pydantic a versão estável atual
O objetivo é construir uma API RESTful do zero com FastAPI, incluindo endpoints públicos e protegidos, autenticação JWT, integração com banco de dados SQLite via SQLAlchemy, e documentação automática OpenAPI. Você entenderá o fluxo completo: modelagem de dados, rotas, autenticação e testes básicos.
Topologia
Uma máquina local (192.168.56.10) executa o FastAPI e SQLite. Um cliente HTTP (curl ou httpie) acessa a API via localhost. Não há dependência de rede externa além de downloads de pacotes.
+-------------------+
| Cliente HTTP |
| (curl/httpie) |
+--------+----------+
|
| localhost:8000
+--------v----------+
| FastAPI App |
| + SQLite DB |
+-------------------+
Equipamentos e software
| Item | Requisito, licença ou versão |
|---|---|
| PC ou VM | 1 vCPU, 1 GB RAM, 1 porta livre (8000), Ubuntu 22.04 ou superior |
| FastAPI | MIT · a versão estável atual |
| Uvicorn | BSD · a versão estável atual |
| SQLAlchemy | MIT · a versão estável atual |
| Pydantic | MIT · a versão estável atual |
Roteiro
- Preparar ambiente Python isolado
Evite conflitos de dependências criando um ambiente virtual.
python3 -m venv venv source venv/bin/activateO que você deve ver: Prompt do terminal com o prefixo (venv)
- Instalar dependências
Instale FastAPI, Uvicorn, SQLAlchemy e Pydantic. São essenciais para API, servidor, ORM e validação.
pip install fastapi uvicorn sqlalchemy pydanticO que você deve ver: Mensagens de instalação concluída sem erros
- Criar o modelo de usuário (SQLAlchemy)
Defina a estrutura da tabela de usuários para persistência.
cat > models.py <<EOF from sqlalchemy import Column, Integer, String from sqlalchemy.ext.declarative import declarative_base Base = declarative_base() class User(Base): __tablename__ = 'users' id = Column(Integer, primary_key=True, index=True) username = Column(String, unique=True, index=True, nullable=False) hashed_password = Column(String, nullable=False) EOFO que você deve ver: Arquivo models.py criado
- Configurar o banco SQLite e sessão SQLAlchemy
Implemente a conexão e a criação automática do banco.
cat > database.py <<EOF from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from models import Base SQLALCHEMY_DATABASE_URL = 'sqlite:///./test.db' engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) def init_db(): Base.metadata.create_all(bind=engine) EOFO que você deve ver: Arquivo database.py criado
- Modelos Pydantic para entrada e saída
Separe o modelo de dados do ORM dos dados expostos na API.
cat > schemas.py <<EOF from pydantic import BaseModel class UserCreate(BaseModel): username: str password: str class UserOut(BaseModel): id: int username: str class Config: orm_mode = True EOFO que você deve ver: Arquivo schemas.py criado
- Implementar utilitário de hash de senha
Nunca armazene senhas em texto claro. Use hash seguro.
cat > security.py <<EOF from passlib.context import CryptContext pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") def hash_password(password: str) -> str: return pwd_context.hash(password) def verify_password(plain_password: str, hashed_password: str) -> bool: return pwd_context.verify(plain_password, hashed_password) EOF pip install passlib[bcrypt]O que você deve ver: Arquivo security.py criado e dependência instalada
- Implementar geração e validação de JWT
JWT permite autenticação stateless. Implemente funções para criar e validar tokens.
cat > jwt_utils.py <<EOF from datetime import datetime, timedelta from jose import JWTError, jwt SECRET_KEY = "supersecretkey" # Para produção, use variável de ambiente ALGORITHM = "HS256" ACCESS_TOKEN_EXPIRE_MINUTES = 30 def create_access_token(data: dict, expires_delta: timedelta = None): to_encode = data.copy() expire = datetime.utcnow() + (expires_delta or timedelta(minutes=15)) to_encode.update({"exp": expire}) encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM) return encoded_jwt def verify_token(token: str): try: payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) return payload except JWTError: return None EOF pip install python-joseO que você deve ver: Arquivo jwt_utils.py criado e dependência instalada
- Criar o app FastAPI e endpoints
Implemente rotas para cadastro, login e consulta de perfil protegido.
cat > main.py <<EOF from fastapi import FastAPI, Depends, HTTPException, status from sqlalchemy.orm import Session from database import SessionLocal, init_db from models import User from schemas import UserCreate, UserOut from security import hash_password, verify_password from jwt_utils import create_access_token, verify_token from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm init_db() app = FastAPI() oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/login") def get_db(): db = SessionLocal() try: yield db finally: db.close() @app.post("/register", response_model=UserOut) def register(user: UserCreate, db: Session = Depends(get_db)): db_user = db.query(User).filter(User.username == user.username).first() if db_user: raise HTTPException(status_code=400, detail="Usuário já existe") hashed = hash_password(user.password) new_user = User(username=user.username, hashed_password=hashed) db.add(new_user) db.commit() db.refresh(new_user) return new_user @app.post("/login") def login(form_data: OAuth2PasswordRequestForm = Depends(), db: Session = Depends(get_db)): user = db.query(User).filter(User.username == form_data.username).first() if not user or not verify_password(form_data.password, user.hashed_password): raise HTTPException(status_code=401, detail="Credenciais inválidas") token = create_access_token({"sub": user.username}) return {"access_token": token, "token_type": "bearer"} @app.get("/me", response_model=UserOut) def read_users_me(token: str = Depends(oauth2_scheme), db: Session = Depends(get_db)): payload = verify_token(token) if not payload: raise HTTPException(status_code=401, detail="Token inválido") username = payload.get("sub") user = db.query(User).filter(User.username == username).first() if user is None: raise HTTPException(status_code=404, detail="Usuário não encontrado") return user EOFO que você deve ver: Arquivo main.py criado
- Iniciar o servidor FastAPI
Coloque a API no ar para testes.
uvicorn main:app --reloadO que você deve ver: Mensagem 'Uvicorn running on http://127.0.0.1:8000'
- Testar documentação automática OpenAPI
Acesse a interface Swagger para explorar e testar a API.
Acesse http://127.0.0.1:8000/docs no navegadorO que você deve ver: Interface Swagger interativa com endpoints /register, /login, /me
Como saber que funcionou
| Teste | Comando | Critério |
|---|---|---|
| Registrar novo usuário | curl -X POST http://127.0.0.1:8000/register -H 'Content-Type: application/json' -d '{"username": "alice", "password": "senha123"}' | Retorno JSON com id e username |
| Login e obtenção de token JWT | curl -X POST http://127.0.0.1:8000/login -d 'username=alice&password=senha123' -H 'Content-Type: application/x-www-form-urlencoded' | Retorno JSON com access_token |
| Acessar endpoint protegido com token | curl -H 'Authorization: Bearer <access_token>' http://127.0.0.1:8000/me | Retorno JSON com id e username |
| Tentar acessar /me sem token | curl http://127.0.0.1:8000/me | Retorno HTTP 401 |
| Registrar usuário já existente | curl -X POST http://127.0.0.1:8000/register -H 'Content-Type: application/json' -d '{"username": "alice", "password": "outra"}' | Retorno HTTP 400 |
Troubleshooting
| Sintoma | Causa provável | Correção |
|---|---|---|
| Erro 'no such table: users' | Banco SQLite não inicializado | Verifique se init_db() foi chamado antes de iniciar o app |
| Erro 'ModuleNotFoundError' ao importar módulos | Dependências não instaladas ou ambiente virtual não ativado | Ative o venv e rode pip install novamente |
| Token JWT inválido ou expirado | Token malformado ou tempo de expiração atingido | Faça login novamente para obter novo token |
| Senha não é aceita mesmo correta | Problema no hash ou dependência do passlib/bcrypt | Confirme instalação do passlib[bcrypt] e reinicie o app |
Segurança e hardening
- Nunca use SECRET_KEY hardcoded em produção; utilize variável de ambiente
- Tokens JWT devem ter expiração curta
- Senhas sempre com hash forte (bcrypt)
- Limite tentativas de login para evitar brute force
- Não exponha stack trace detalhado em produção
Checklist final
Referências
- FastAPI - Tutorial · FastAPI (fastapi.tiangolo.com)
- Pydantic - Documentação · Pydantic-docs.helpmanual (pydantic-docs.helpmanual.io)
- SQLAlchemy - ORM Tutorial · Docs.sqlalchemy (docs.sqlalchemy.org)
- Publicado
- 09/09/2026
- Última revisão
- 09/09/2026
- Versão do conteúdo
- 1.0
- Referências
- 3 oficiais
- Revisão técnica
- redação e revisão por IA, URLs conferidas na publicação
- Status
- Atual
