Finances API é uma REST API para controle de orçamento familiar que visa auxiliar o usuário a gerenciar suas receitas e despesas de forma clara e segura.
A aplicação possui endpoints para gerenciar e manipular os recursos Usuário (User), Receitas (Income), Despesas (Expense), Autenticação (Auth), Resumos (Summary), Categorias (Category), que são protegidos e requerem autenticação por JWT (Json Web Token) para serem manipulados.
O projeto foi proposto pela Alura no Challenge Backend 2ª edição.
-
Cadastrar usuário
: Cadastro de usuários através de um POST /api/auth/signup com as informações name, email, password e confirm em um JSON no corpo da requisição.- O password é salvo criptografado no banco de dados usando BCryp.
Segue abaixo um exemplo do corpo da requisição.
{ "name": "John Doe", "email": "john.doe@email.com", "password": "1234567890", "confirmPassword": "1234567890" }
Em caso de sucesso a resposta tem status 201 com um JSON no corpo da resposta contendo id, name, email, status e createdAt do usuário cadastrado.
Segue abaixo um exemplo do corpo da resposta.
{ "id": 150, "name": "John Doe", "email": "john.doe@email.com" }
-
Logar usuário
: Sign in de usuários através de um POST /api/auth/signin com as informações de autenticação do usuário (email e senha) em um JSON no corpo da requisição.Segue abaixo um exemplo do corpo da requisição.
{ "email": "john.doe@email.com", "password": "1234567890" }
Em caso de sucesso a resposta tem status 200 com um JSON no corpo da resposta contendo token e type, onde token é um JWT que deve ser enviado em todas as requisições que requerem usuário autenticado, e type é o tipo do token, no caso desse sistema é o tipo Bearer.
Segue abaixo um exemplo do corpo da resposta.
{ "token": "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI1IiwiaWF0IjoxNzE4MTk1OTQ5LCJleHAiOjE3MTgyODIzNDksImlzcyI6IkZpbmFuY2VzIEFQSSJ9.MEN5dWRe5XZ1F1j6A24loW9lzD3U_SvRMFlOG5TtLuM", "type": "Bearer" }
-
Buscar usuários
: Busca paginada de usuários através de um GET /api/users, apenas usuário ADMIN tem permissão para usar essa funcionalidade.- Caso o cliente queira outra página da pesquisa deve passar a query param page, o valor padrão de page é 0.
- Caso o cliente queira outro tamanho de página deve passar a query param size, o valor padrão de size é 10.
- Os usuários vem ordenados por id em ordem crescente.
Em caso de sucesso a resposta tem status 200 e um JSON no corpo da resposta com as informações paginadas dos usuários cadastrados.
Segue abaixo um exemplo do corpo da resposta.
{ "totalElements": 2, "totalPages": 1, "pageable": { "pageNumber": 0, "pageSize": 10, "sort": { "sorted": true, "empty": false, "unsorted": false }, "offset": 0, "paged": true, "unpaged": false }, "size": 10, "content": [ { "id": 1, "name": "John Doe", "email": "john@email.com" }, { "id": 3, "name": "Jane Doe", "email": "jane@email.com" } ], "number": 0, "sort": { "sorted": true, "empty": false, "unsorted": false }, "numberOfElements": 2, "first": true, "last": true, "empty": false }
-
Atualizar senha
: Atualizar password através de um POST /api/users/password com as informações oldPassword, newPassword e confirm em um JSON no corpo da requisição.Segue abaixo um exemplo do corpo da requisição.
{ "oldPassword": "1234567890", "newPassword": "0987654321", "confirm": "0987654321" }
Em caso de sucesso a resposta tem status 204.
-
Deletar usuário
: Deletar usuário através de um DELETE /api/users/{id}, apenas usuário ADMIN tem permissão para usar essa funcionalidade.Em caso de sucesso a resposta tem status 204.
-
Cadastrar receita
: Cadastrar receitas através de um POST /api/incomes com as informações description, date e value em um JSON no corpo da requisição.Segue abaixo um exemplo do corpo da requisição.
{ "description": "Salário", "date": "2024-01-04", "value": 3240.59 }
Em caso de sucesso a resposta tem status 201 e um JSON no corpo da resposta.
Segue abaixo um exemplo do corpo da resposta.
{ "id": 1 "description": "Salário", "date": "2024-01-04", "value": 3240.59 }
-
Buscar receitas
: Busca paginada de receitas através de um GET /api/incomes, opcional buscar por description.- Caso o cliente queira outra página deve passar a query param page, o valor padrão de page é 0.
- Caso o cliente queira outro tamanho de página deve passar a query param size, o valor padrão de size é 10.
- Caso o cliente queira receitas com uma dada description deve passar a query param description, esse parâmetro é opcional.
- As receitas vem ordenados por date da mais recente para menos recente.
Em caso de sucesso a resposta tem status 200 com um JSON no corpo da resposta.
Segue abaixo um exemplo do corpo da resposta para a requisição GET /api/incomes.
{ "totalElements": 1, "totalPages": 1, "pageable": { "pageNumber": 0, "pageSize": 10, "sort": { "sorted": true, "empty": false, "unsorted": false }, "offset": 0, "paged": true, "unpaged": false }, "size": 10, "content": [ { "id": 2, "description": "Salário", "date": "2024-01-04", "value": 3240.59 } ], "number": 0, "sort": { "sorted": true, "empty": false, "unsorted": false }, "numberOfElements": 1, "first": true, "last": true, "empty": false }
-
Buscar receita por id
: Busca receita por id através de um GET /api/incomes/{id}, onde id é o identificador da receita.- Usuário só tem permissão para buscar receitas que pertencem a si mesmo.
Em caso de sucesso a resposta tem status 200 com um JSON no corpo da resposta.
Segue abaixo um exemplo do corpo da resposta para a requisição GET /api/incomes/2
{ "id": 2, "description": "Salário", "date": "2024-01-04", "value": 3240.59 }
-
Buscar receitas por ano e mês
: Busca paginada de receitas por ano e mês, através de um GET /api/incomes{year}/{month}.- Caso o cliente queira outra página deve passar a query param page, o valor padrão de page é 0.
- Caso o cliente queira outro tamanho de página deve passar a query param size, o valor padrão de size é 10.
- As receitas vem ordenadas por date da mais recente para menos recente.
Em caso de sucesso a resposta tem status 200 com um JSON no corpo da resposta.
Segue abaixo um exemplo do corpo da resposta para a requisição GET /api/incomes/2024/1.
{ "totalElements": 1, "totalPages": 1, "pageable": { "pageNumber": 0, "pageSize": 10, "sort": { "sorted": true, "empty": false, "unsorted": false }, "offset": 0, "paged": true, "unpaged": false }, "size": 10, "content": [ { "id": 2, "description": "Salário", "date": "2024-01-04", "value": 3240.59 } ], "number": 0, "sort": { "sorted": true, "empty": false, "unsorted": false }, "numberOfElements": 1, "first": true, "last": true, "empty": false }
-
Atualizar receita
: Atualizar receita por id através de um PUT /api/incomes/{id}, onde id é o identificador da receita, e enviando as novas informações da receita (description, date e value) em um JSON no corpo da requisição.- Usuário só tem permissão de atualizar receitas que pertencem a si mesmo.
Segue abaixo um exemplo do corpo da requisição.
{ "description": "Salário", "date": "2024-01-05", "value": 3800.00 }
Em caso de sucesso a resposta tem status 200, com um JSON no corpo da resposta.
Segue abaixo um exemplo do corpo da resposta.
{ "id": 2, "description": "Salário", "date": "2024-01-05", "value": 3800.00 }
-
Deletar receita
: Deletar receita por id através de um DELETE /api/incomes/{id}, onde id é o identificador da receita a ser deletada.- Usuário só tem permissão de deletar receitas que pertencem a si mesmo.
Em caso de sucesso a resposta tem status 204.
-
Cadastrar despesa
: Cadastrar despesas através de um POST /api/expenses com as informações description, date, value e category (opcional), em um JSON no corpo da requisição.- O campo category é opcional e caso não seja enviado, a despesa é salva com a category OUTRAS.
Segue abaixo um exemplo do corpo da requisição.
{ "description": "Mercado", "date": "2024-01-15", "value": 271.94, "category": "ALIMENTACAO" }
Em caso de sucesso a resposta tem status 201, e um JSON no corpo da resposta.
Segue abaixo um exemplo do corpo da resposta.
{ "id": 5, "description": "Mercado", "date": "2024-01-15", "value": 271.94, "category": "ALIMENTACAO" }
-
Buscar despesas
: Busca paginada de despesas através de um GET /api/expenses, opcional buscar por description.- Caso o cliente queira outra página deve passar a query param page, o valor padrão de page é 0.
- Caso o cliente queira outro tamanho de página deve passar a query param size, o valor padrão de size é 10.
- Caso o cliente queira despesas com uma dada description deve passar a query param description, esse parâmetro é opcional.
- As despesas vem ordenadas por date da mais recente para menos recente.
Em caso de sucesso a resposta tem status 200, e um JSON no corpo da resposta.
Segue abaixo um exemplo do corpo da resposta.
{ "totalElements": 1, "totalPages": 1, "pageable": { "pageNumber": 0, "pageSize": 10, "sort": { "sorted": true, "empty": false, "unsorted": false }, "offset": 0, "paged": true, "unpaged": false }, "size": 10, "content": [ { "id": 5, "description": "Mercado", "date": "2024-01-15", "value": 271.94, "category": "ALIMENTACAO" } ], "number": 0, "sort": { "sorted": true, "empty": false, "unsorted": false }, "numberOfElements": 1, "first": true, "last": true, "empty": false }
-
Buscar despesa por id
: Busca de despesa por id através de um GET para /api/expenses/{id}, onde id é o identificador da despesa.- Usuário só tem permissão para buscar despesas que pertencem a si mesmo.
Em caso de sucesso a resposta tem status 200 com um JSON no corpo da resposta.
Segue abaixo um exemplo do corpo da resposta para a requisição GET /api/expenses/5
{ "id": 5, "description": "Mercado", "date": "2024-01-15", "value": 271.94, "category": "ALIMENTACAO" }
-
Buscar despesas por ano e mês
: Busca paginada de despesas por ano e mês através de um GET /api/expenses/{year}/{month}.- Caso o cliente queira outra página deve passar a query param page, o valor padrão de page é 0.
- Caso o cliente queira outro tamanho de página deve passar a query param size, o valor padrão de size é 10.
- As despesas vem ordenados por date da mais recente para menos recente.
Em caso de sucesso a resposta tem status 200 com um JSON no corpo da resposta.
Segue abaixo um exemplo do corpo da resposta para a requisição GET /api/expenses/2024/1.
{ "totalElements": 1, "totalPages": 1, "pageable": { "pageNumber": 0, "pageSize": 10, "sort": { "sorted": true, "empty": false, "unsorted": false }, "offset": 0, "paged": true, "unpaged": false }, "size": 10, "content": [ { "id": 5, "description": "Mercado", "date": "2024-01-15", "value": 271.94, "category": "ALIMENTACAO" } ], "number": 0, "sort": { "sorted": true, "empty": false, "unsorted": false }, "numberOfElements": 1, "first": true, "last": true, "empty": false }
-
Atualizar despesa
: Atualizar despesa por id através de um PUT /api/expenses/{id}, onde id é o identificador da despesa e enviando as novas informações da despesa (description, date, value e category) em um JSON no corpo da requisição.- Usuário só tem permissão de atualizar despesas que pertencem a si mesmo.
Segue abaixo um exemplo do corpo da requisição.
{ "description": "Mercado", "date": "2024-01-20", "value": 300.00, "category": "ALIMENTACAO" }
Em caso de sucesso a resposta tem status 201, e um JSON no corpo da resposta.
Segue abaixo um exemplo do corpo da resposta.
{ "id": 5, "description": "Mercado", "date": "2024-01-20", "value": 300.00, "category": "ALIMENTACAO" }
-
Deletar despesa
: Deletar despesa por id através de um DELETE /api/expenses/{id}, onde id é o identificador da despesa a ser deletada.- Usuário só tem permissão de deletar despesas que pertencem a si mesmo.
Em caso de sucesso a resposta tem status 204.
-
Buscar categorias
: Buscar categorias através de um GET /api/categories.Em caso de sucesso a resposta tem status 200, e um JSON no corpo da resposta.
Segue abaixo um exemplo do corpo da resposta.
[ { "category": "ALIMENTACAO" }, { "category": "SAUDE" }, { "category": "MORADIA" }, { "category": "TRANSPORTE" }, { "category": "EDUCACAO" }, { "category": "LAZER" }, { "category": "IMPREVISTOS" }, { "category": "OUTRAS" } ]
-
Buscar resumo do mês
: Buscar resumo do mês através de um GET /api/summaries/{year}/{month}, o usuário tem acesso ao total de receitas e despesas de um dado ano e mês, assim como o saldo do mês e o total de despesas por categoria.Em caso de sucesso a resposta tem status 200 e um JSON no corpo da resposta.
Segue abaixo um exemplo do corpo da resposta.
{ "incomeTotalValue": 3240.59, "expenseTotalValue": 271.94, "finalBalance": 2968.65, "valuesByCategory": [ { "category": "ALIMENTACAO", "value": 271.94 } ] }
---
title: Database Schema
---
erDiagram
USER {
bigserial id PK
varchar(100) name
varchar(150) email UK
varchar(255) password
}
ROLE {
serial id PK
varchar(50) name UK
}
USER_ROLES {
bigint user_id FK
int roles_id FK
}
INCOME {
bigserial id PK
varchar(255) description
numeric value
date date
bigint user_id FK
}
EXPENSE {
bigserial id PK
varchar(255) description
numeric value
varchar(25) category
date date
bigint user_id FK
}
USER }o--o{ USER_ROLES : has
ROLE }o--o{ USER_ROLES : allows
USER ||--o{ INCOME : has
USER ||--o{ EXPENSE : has
O deploy da aplicação foi realizado no Heroku, você pode testar/brincar/usar aqui
OBS: As aplicações que usam conta gratuita do heroku adormecem se ficarem inativas, então pode ser que a primeira requisição demore um pouco (até uns 60 segundos), apenas seja paciente 😉.
Documentação feita com Springdoc openapi, gerando Swagger UI e Api docs
- Configuração de CORS
- Mais relatórios, tipo relatório anual, ou por categoria em dados mês e ano.
- Adicionar algum recurso para monitorar e gerenciar a aplicação, tipo o Actuator
- Endpoint para listar todas as categorias.
- Remoção da regra de negócio que não permitia receitas/despesas com a mesmo DESCRIÇÃO no mesmo MÊS e ANO.
- Atualizar versão do Spring boot (2.7.* ou 3.3.*)