API RESTful assíncrona para gerenciar contas correntes, depósitos e saques, com autenticação JWT.
- FastAPI — framework web assíncrono
- SQLAlchemy 2.0 (async) + aiosqlite — ORM e driver async para SQLite
- Pydantic v2 + pydantic-settings — schemas, validação e config via
.env - python-jose — emissão e verificação de JWT
- bcrypt — hash de senhas
- Uvicorn — servidor ASGI
- Cadastro e autenticação de usuários (senha com hash bcrypt + token JWT)
- CRUD de contas correntes (uma conta pertence a um usuário)
- Transações: depósito e saque com validações de valor e saldo
- Extrato da conta com histórico completo de transações
- Mensagens de erro em português (incluindo erros de validação do Pydantic)
- Documentação OpenAPI / Swagger automática em
/docs
- Python 3.11 ou superior (testado em 3.14)
# 1. Entrar na pasta do projeto
cd desafioFastApi
# 2. Criar e ativar um ambiente virtual
python -m venv .venv
# Windows (PowerShell)
.venv\Scripts\Activate.ps1
# Linux / macOS
source .venv/bin/activate
# 3. Instalar as dependências
pip install -r requirements.txt
# 4. Configurar variáveis de ambiente
# Windows
copy .env.example .env
# Linux / macOS
cp .env.example .env
# Em seguida, abra o .env e troque SECRET_KEY por um valor seguro:
python -c "import secrets; print(secrets.token_hex(32))"Qualquer uma das opções abaixo sobe o servidor em http://127.0.0.1:8000:
# Opção 1 — pelo script (usa o bloco __main__ do entrypoint)
python desafioFastApi.py
# Opção 2 — via uvicorn diretamente (com auto-reload em mudanças)
uvicorn desafioFastApi:app --reloadO banco SQLite (desafio.db) é criado automaticamente no primeiro start.
A forma mais rápida é pelo Swagger UI:
- Abra
http://127.0.0.1:8000/docs - Em
POST /auth/register→ "Try it out" → criar um usuário - Em
POST /auth/token→ fazer login → copiar oaccess_token - Clicar no cadeado Authorize no topo → colar o token → Authorize
POST /accountscom{"number": "0001-1"}→ criar a contaPOST /accounts/1/transactionscom{"type": "deposit", "amount": "500.00"}POST /accounts/1/transactionscom{"type": "withdraw", "amount": "150.00"}GET /accounts/1/extract→ verifica saldo350.00e o histórico
| Método | Rota | Descrição | Auth |
|---|---|---|---|
| GET | / |
Health check | não |
| POST | /auth/register |
Cadastra um novo usuário | não |
| POST | /auth/token |
Login (form-data); retorna o JWT | não |
| POST | /accounts |
Cria uma conta para o usuário | sim |
| GET | /accounts |
Lista as contas do usuário | sim |
| GET | /accounts/{id} |
Detalha uma conta | sim |
| POST | /accounts/{id}/transactions |
Depósito ou saque | sim |
| GET | /accounts/{id}/extract |
Extrato (saldo + transações) | sim |
desafioFastApi/
├── desafioFastApi.py # entrypoint: cria o app, monta routers, registra handlers
├── config.py # Settings via pydantic-settings (lê o .env)
├── database.py # engine async, AsyncSession, Base, init_db
├── auth.py # hash bcrypt, JWT e dependência get_current_user
├── errors_pt.py # handler global p/ traduzir erros do Pydantic para PT
├── models/ # SQLAlchemy (User, Account, Transaction)
├── schemas/ # Pydantic (request/response)
├── routers/ # endpoints (auth, accounts, transactions)
├── requirements.txt
├── .env.example
└── .gitignore
User (1) ────< (N) Account (1) ────< (N) Transaction
- User:
id,username(único),email(único),full_name,hashed_password - Account:
id,number(único),balance(Numeric 14,2),owner_id→ User - Transaction:
id,account_id→ Account,type(deposit|withdraw),amount(Numeric 14,2)
amountde depósito e saque deve ser maior que zero (Pydanticgt=0)- Saque exige saldo suficiente — caso contrário, retorna 400 "Saldo insuficiente para saque"
- Endpoints de conta/transação retornam 404 se a conta não pertencer ao usuário autenticado (isolamento por usuário)
usernameeemailsão únicos — cadastro duplicado retorna 409- Acesso sem token retorna 401 "Não autenticado"
| Variável | Padrão | Descrição |
|---|---|---|
DATABASE_URL |
sqlite+aiosqlite:///./desafio.db |
URL do banco (SQLAlchemy) |
SECRET_KEY |
(defina no .env) |
Chave para assinar o JWT |
ALGORITHM |
HS256 |
Algoritmo do JWT |
ACCESS_TOKEN_EXPIRE_MINUTES |
60 |
Tempo de vida do token, em minutos |