API RESTful para gerenciamento de livros, autores e contas de usuário, construída com FastAPI, SQLAlchemy (async), Alembic e autenticação JWT.
- Requisitos
- Instalação
- Execução
- Estrutura do Projeto
- Modelos de Dados
- Endpoints da API
- Autenticação e Segurança
- Testes
- Comandos Úteis
- Python: 3.13+
- Poetry: Para gerenciamento de dependências
- Banco de Dados: SQLite (padrão) ou outro compatível com SQLAlchemy
git clone <repo-url>
cd maddr-apipoetry installCrie um arquivo .env na raiz do projeto:
DATABASE_URL=sqlite+aiosqlite:///./maddr_db.db
SECRET_KEY=sua_chave_secreta_super_segura_aqui
ALGORITHM=HS256
ACCESS_TOKEN_EXPIRE_MINUTES=30poetry run alembic upgrade headpoetry run fastapi dev src/maddr_api/app.pyO servidor estará disponível em: http://localhost:8000
- Swagger UI:
http://localhost:8000/docs - ReDoc:
http://localhost:8000/redoc
maddr-api/
├── src/
│ └── maddr_api/
│ ├── __init__.py
│ ├── app.py # Aplicação principal FastAPI
│ ├── config/
│ │ ├── database.py # Configuração do banco de dados
│ │ └── settings.py # Configurações da aplicação
│ ├── models/ # Modelos SQLAlchemy
│ │ ├── __init__.py
│ │ ├── account.py # Modelo de conta de usuário
│ │ ├── author.py # Modelo de autor
│ │ └── book.py # Modelo de livro
│ ├── schemas/ # Schemas Pydantic
│ │ ├── __init__.py
│ │ ├── account.py # Schemas de conta
│ │ ├── author.py # Schemas de autor
│ │ ├── book.py # Schemas de livro
│ │ └── token.py # Schemas de token
│ ├── routers/ # Rotas da API
│ │ ├── __init__.py
│ │ ├── account.py # Endpoints de conta
│ │ ├── author.py # Endpoints de autor
│ │ ├── book.py # Endpoints de livro
│ │ └── token.py # Endpoints de autenticação
│ ├── services/ # Lógica de negócio
│ │ ├── __init__.py
│ │ ├── main.py # BaseCRUD genérico
│ │ ├── account.py # Serviços de conta
│ │ ├── author.py # Serviços de autor
│ │ ├── book.py # Serviços de livro
│ │ └── token.py # Serviços de token
│ ├── security/ # Segurança e autenticação
│ │ ├── access_token.py # Criação de tokens JWT
│ │ ├── get_current_user.py # Middleware de autenticação
│ │ └── hash_password.py # Hash de senhas (Argon2)
│ └── utils/ # Utilitários
│ └── sanitization.py # Sanitização de strings
├── migrations/ # Migrações Alembic
│ ├── versions/
│ └── env.py
├── tests/ # Testes automatizados
│ ├── conftest.py
│ ├── test_account.py
│ ├── test_author.py
│ ├── test_book.py
│ ├── test_security.py
│ └── test_token.py
├── pyproject.toml # Configuração do projeto
├── alembic.ini # Configuração do Alembic
└── README.md
{
"id": int, # ID único (gerado automaticamente)
"username": str, # Nome de usuário (único)
"email": str, # Email (único)
"password": str, # Senha (hash Argon2)
"created_at": datetime, # Data de criação
"updated_at": datetime # Data de atualização
}{
"id": int, # ID único (gerado automaticamente)
"name": str, # Nome do autor
"created_at": datetime, # Data de criação
"updated_at": datetime, # Data de atualização
"books": list[Book] # Lista de livros (relacionamento)
}{
"id": int, # ID único (gerado automaticamente)
"title": str, # Título do livro
"author_id": int, # ID do autor (FK)
"publish_year": int, # Ano de publicação
"created_at": datetime, # Data de criação
"updated_at": datetime, # Data de atualização
"author": Author # Relação com autor
}- Descrição: Endpoint de boas-vindas
- Autenticação: Não requerida
- Resposta:
200 OK
{
"message": "Welcome to the MADDR API!"
}Gera um token de acesso JWT para autenticação.
-
Autenticação: Não requerida
-
Content-Type:
application/x-www-form-urlencoded -
Corpo da Requisição:
username(string): Nome de usuáriopassword(string): Senha
-
Resposta:
200 OK
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer"
}- Exemplo cURL:
curl -X POST "http://localhost:8000/token/" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=seu_usuario&password=sua_senha"Renova o token de acesso existente.
-
Autenticação: Não requerida
-
Content-Type:
application/x-www-form-urlencoded -
Corpo da Requisição:
username(string): Nome de usuáriopassword(string): Senha
-
Resposta:
200 OK
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer"
}Cria uma nova conta de usuário.
- Autenticação: Não requerida
- Corpo da Requisição:
{
"username": "usuario_exemplo",
"email": "usuario@exemplo.com",
"password": "senha_segura123"
}- Resposta:
201 CREATED
{
"id": 1,
"username": "usuario_exemplo",
"email": "usuario@exemplo.com"
}-
Validações:
- Username e email devem ser únicos
- Senha será armazenada com hash Argon2
-
Exemplo cURL:
curl -X POST "http://localhost:8000/account/" \
-H "Content-Type: application/json" \
-d '{
"username": "usuario_exemplo",
"email": "usuario@exemplo.com",
"password": "senha_segura123"
}'Recupera os dados de uma conta pelo ID.
-
Autenticação: Não requerida
-
Parâmetros:
account_id(path, int): ID da conta
-
Resposta:
200 OK
{
"id": 1,
"username": "usuario_exemplo",
"email": "usuario@exemplo.com"
}- Erros:
404 NOT FOUND: Conta não encontrada
Atualiza os dados de uma conta.
-
Autenticação: Requerida (Bearer Token)
-
Parâmetros:
account_id(path, int): ID da conta
-
Corpo da Requisição:
{
"username": "novo_usuario",
"email": "novo@exemplo.com",
"password": "nova_senha123"
}- Resposta:
200 OK
{
"id": 1,
"username": "novo_usuario",
"email": "novo@exemplo.com"
}-
Validações:
- Apenas o próprio usuário pode atualizar sua conta
- Username e email devem permanecer únicos
-
Exemplo cURL:
curl -X PUT "http://localhost:8000/account/1" \
-H "Authorization: Bearer seu_token_aqui" \
-H "Content-Type: application/json" \
-d '{
"username": "novo_usuario",
"email": "novo@exemplo.com",
"password": "nova_senha123"
}'Remove uma conta do sistema.
-
Autenticação: Requerida (Bearer Token)
-
Parâmetros:
account_id(path, int): ID da conta
-
Resposta:
200 OK
{
"message": "Account deleted successfully."
}- Validações:
- Apenas o próprio usuário pode deletar sua conta
Cria um novo autor.
- Autenticação: Requerida (Bearer Token)
- Corpo da Requisição:
{
"name": "J.K. Rowling"
}- Resposta:
201 CREATED
{
"id": 1,
"name": "J.K. Rowling"
}-
Validações:
- Nome do autor deve ser único
- Nome é sanitizado automaticamente
-
Erros:
409 CONFLICT: Autor com esse nome já existe
-
Exemplo cURL:
curl -X POST "http://localhost:8000/author/" \
-H "Authorization: Bearer seu_token_aqui" \
-H "Content-Type: application/json" \
-d '{"name": "J.K. Rowling"}'Recupera os dados de um autor pelo ID.
-
Autenticação: Requerida (Bearer Token)
-
Parâmetros:
author_id(path, int): ID do autor
-
Resposta:
200 OK
{
"id": 1,
"name": "J.K. Rowling"
}- Erros:
404 NOT FOUND: Autor não encontrado
Atualiza os dados de um autor.
-
Autenticação: Requerida (Bearer Token)
-
Parâmetros:
author_id(path, int): ID do autor
-
Corpo da Requisição:
{
"name": "Novo Nome do Autor"
}- Resposta:
200 OK
{
"id": 1,
"name": "Novo Nome do Autor"
}- Validações:
- Nome deve permanecer único
Remove um autor do sistema.
-
Autenticação: Requerida (Bearer Token)
-
Parâmetros:
author_id(path, int): ID do autor
-
Resposta:
200 OK
{
"message": "Author deleted successfully."
}- Nota: Livros relacionados ao autor também serão afetados pelo relacionamento configurado
Cria um novo livro.
- Autenticação: Requerida (Bearer Token)
- Corpo da Requisição:
{
"title": "Harry Potter e a Pedra Filosofal",
"author_id": 1,
"publish_year": 1997
}- Resposta:
201 CREATED
{
"id": 1,
"title": "Harry Potter e a Pedra Filosofal",
"author_id": 1,
"publish_year": 1997
}-
Validações:
- Título deve ser único
- Author_id deve existir no banco de dados
- Título é sanitizado automaticamente
-
Erros:
409 CONFLICT: Livro com esse título já existe
-
Exemplo cURL:
curl -X POST "http://localhost:8000/book/" \
-H "Authorization: Bearer seu_token_aqui" \
-H "Content-Type: application/json" \
-d '{
"title": "Harry Potter e a Pedra Filosofal",
"author_id": 1,
"publish_year": 1997
}'Recupera os dados de um livro pelo ID.
-
Autenticação: Requerida (Bearer Token)
-
Parâmetros:
book_id(path, int): ID do livro
-
Resposta:
200 OK
{
"id": 1,
"title": "Harry Potter e a Pedra Filosofal",
"author_id": 1,
"publish_year": 1997
}- Erros:
404 NOT FOUND: Livro não encontrado
Lista todos os livros com filtros opcionais e paginação.
-
Autenticação: Requerida (Bearer Token)
-
Parâmetros de Query:
title(opcional, string): Filtrar por título (busca parcial)publish_year(opcional, int): Filtrar por ano de publicaçãolimit(opcional, int, padrão: 20): Número máximo de resultadosskip(opcional, int, padrão: 0): Número de registros a pular
-
Resposta:
200 OK
[
{
"id": 1,
"title": "Harry Potter e a Pedra Filosofal",
"author_id": 1,
"publish_year": 1997
},
{
"id": 2,
"title": "Harry Potter e a Câmara Secreta",
"author_id": 1,
"publish_year": 1998
}
]- Exemplos cURL:
Listar todos os livros (primeiros 20):
curl -X GET "http://localhost:8000/book/" \
-H "Authorization: Bearer seu_token_aqui"Filtrar por título:
curl -X GET "http://localhost:8000/book/?title=Harry%20Potter" \
-H "Authorization: Bearer seu_token_aqui"Filtrar por ano de publicação:
curl -X GET "http://localhost:8000/book/?publish_year=1997" \
-H "Authorization: Bearer seu_token_aqui"Paginação (pular 10, retornar 5):
curl -X GET "http://localhost:8000/book/?skip=10&limit=5" \
-H "Authorization: Bearer seu_token_aqui"Filtros combinados:
curl -X GET "http://localhost:8000/book/?title=Harry&publish_year=1997&limit=10&skip=0" \
-H "Authorization: Bearer seu_token_aqui"A API utiliza JSON Web Tokens (JWT) para autenticação:
- Obtenha um token: Faça login via
POST /token/com suas credenciais - Use o token: Inclua o header
Authorization: Bearer {token}nas requisições - Renove o token: Use
POST /token/refresh-tokenquando necessário
- Senhas são armazenadas usando Argon2 (via
pwdlib) - Hash seguro com salt automático
- Verificação de senha protegida contra timing attacks
- Todos os campos de texto (títulos, nomes) são sanitizados antes de serem salvos
- Proteção contra caracteres especiais e espaços em branco desnecessários
Os seguintes endpoints requerem autenticação:
- Todos os endpoints de
/author/(exceto root) - Todos os endpoints de
/book/(exceto root) PUT /account/{account_id}DELETE /account/{account_id}
Os seguintes endpoints não requerem autenticação:
POST /account/(criar conta)GET /account/{account_id}(ler conta pública)POST /token/(obter token)POST /token/refresh-token(renovar token)
poetry run task testEste comando irá:
- Executar o lint (ruff check)
- Rodar todos os testes com pytest
- Gerar relatório de cobertura
- Criar relatório HTML em
htmlcov/
# Apenas testes de account
poetry run pytest tests/test_account.py -v
# Apenas testes de author
poetry run pytest tests/test_author.py -v
# Apenas testes de book
poetry run pytest tests/test_book.py -v
# Apenas testes de segurança
poetry run pytest tests/test_security.py -v# Abrir relatório HTML
open htmlcov/index.html# Rodar servidor de desenvolvimento
poetry run task run
# Ou diretamente
poetry run fastapi dev src/maddr_api/app.py# Verificar problemas de código
poetry run task lint
# Corrigir problemas automaticamente
poetry run ruff check --fix
# Formatar código
poetry run task format# Criar nova migração
poetry run alembic revision --autogenerate -m "descrição da migração"
# Aplicar migrações
poetry run alembic upgrade head
# Reverter última migração
poetry run alembic downgrade -1
# Ver histórico de migrações
poetry run alembic history# Adicionar nova dependência
poetry add nome-do-pacote
# Adicionar dependência de desenvolvimento
poetry add --group dev nome-do-pacote
# Atualizar dependências
poetry update
# Ver dependências instaladas
poetry showcurl -X POST "http://localhost:8000/account/" \
-H "Content-Type: application/json" \
-d '{
"username": "meu_usuario",
"email": "meu@email.com",
"password": "senha_segura123"
}'curl -X POST "http://localhost:8000/token/" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=meu_usuario&password=senha_segura123"Resposta:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer"
}Salve o token em uma variável:
TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."curl -X POST "http://localhost:8000/author/" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "J.K. Rowling"}'curl -X POST "http://localhost:8000/book/" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"title": "Harry Potter e a Pedra Filosofal",
"author_id": 1,
"publish_year": 1997
}'curl -X GET "http://localhost:8000/book/" \
-H "Authorization: Bearer $TOKEN"curl -X GET "http://localhost:8000/book/?title=Harry&publish_year=1997" \
-H "Authorization: Bearer $TOKEN"| Categoria | Tecnologia | Versão |
|---|---|---|
| Framework | FastAPI | 0.119.0+ |
| ORM | SQLAlchemy | 2.0.44+ |
| Migrações | Alembic | 1.17.0+ |
| Validação | Pydantic | (via FastAPI) |
| Autenticação | PyJWT | 2.10.1+ |
| Hash de Senha | pwdlib[argon2] | 0.2.1+ |
| Testes | pytest | 8.4.2+ |
| Cobertura | pytest-cov | 7.0.0+ |
| Lint/Format | ruff | 0.14.1+ |
| Task Runner | taskipy | 1.14.1+ |
Este projeto está sob licença [escolha sua licença].
lucas.harmatiuk