Construção de API
Back-end Web
Rotas do Invest API
Método | Caminho | Parâmetro | Status | Resposta |
---|---|---|---|---|
POST | /api/investments | Body | 201 , 400 | Cria um novo investimento |
GET | /api/investments | - | 200 | Retorna todas os investimentos |
GET | /api/investments?name=Salada | Query | 200 | Retorna todas os investimentos com o nome Tesouro |
GET | /api/investments/X | Route | 200 , 404 | Retorna o investimento de ID X |
PUT | /api/investments/X | Body, Route | 200 , 400 , 404 | Atualiza o investimento de ID X |
DELETE | /api/investments/X | Route | 204 , 404 | Exclui o investimento de ID X |
HTTP Status Codes
- Respostas de informação (100-199)
- Respostas de sucesso (200-299)
- Redirecionamentos (300-399)
- Erros do cliente (400-499)
- Erros do servidor (500-599)
Códigos do Invest API
Código | Nome | Significado |
---|---|---|
200 | Ok | Solicitação gerada com sucesso |
201 | Created | Solicitação gerada com sucesso e um novo recurso foi criado como resultado |
204 | No Content | Solicitação gerada com sucesso e não há conteúdo para ser enviado |
304 | Not Modified | O conteúdo da requisição já existe em cache e não foi modificado |
400 | Bad Request | Solicitação não compreendida por motivos de erro |
401 | Unauthorized | A solicitação requer autenticação do usuário |
404 | Not Found | O servidor não pode encontrar o recurso solicitado |
500 | Internal Server Error | O servidor encontrou uma situação com a qual não sabe lidar. |
API de Investimentos
Arquivos
invest-app-api-back-end
├── package-lock.json
├── package.json
├── requests.http
└── src
├── data
│ └── investments.js
├── index.js
└── routes.js
Arquivos
invest-app-api-back-end
├── package-lock.json
├── package.json
├── requests.http
└── src
├── data
│ └── investments.js
├── index.js
└── routes.js
$ npm install express morgan cors express-async-errors uuid
$ npm install express morgan cors express-async-errors uuid
/codes/expressjs/invest-app-api-back-end/.gitignore
node_modules
/codes/expressjs/invest-app-api-back-end/.gitignore
node_modules
/codes/expressjs/invest-app-api-back-end/package.json
{
"name": "invest-app",
"type": "module",
"scripts": {
"start": "node src/index.js",
"dev": "node --watch src/index.js"
},
"dependencies": {
"cors": "^2.8.5",
"express": "^4.18.2",
"express-async-errors": "^3.1.1",
"morgan": "^1.10.0",
"uuid": "^9.0.0"
}
}
/codes/expressjs/invest-app-api-back-end/package.json
{
"name": "invest-app",
"type": "module",
"scripts": {
"start": "node src/index.js",
"dev": "node --watch src/index.js"
},
"dependencies": {
"cors": "^2.8.5",
"express": "^4.18.2",
"express-async-errors": "^3.1.1",
"morgan": "^1.10.0",
"uuid": "^9.0.0"
}
}
/codes/expressjs/invest-app-api-back-end/src/data/investments.js
export const investments = [
{
id: '9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d',
name: 'Tesouro Selic 2029',
value: 10000,
},
{
id: '1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed',
name: 'CDB Banco X',
value: 5000,
},
];
/codes/expressjs/invest-app-api-back-end/src/data/investments.js
export const investments = [
{
id: '9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d',
name: 'Tesouro Selic 2029',
value: 10000,
},
{
id: '1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed',
name: 'CDB Banco X',
value: 5000,
},
];
/codes/expressjs/invest-app-api-back-end/src/routes.js
import express from 'express';
import { v4 as uuidv4 } from 'uuid';
import { investments } from './data/investments.js';
class HttpError extends Error {
constructor(message, code = 400) {
super(message);
this.code = code;
}
}
const router = express.Router();
router.post('/investments', (req, res) => {
const { name, value } = req.body;
if (!name || !value) {
throw new HttpError('Error when passing parameters');
}
const id = uuidv4();
const newInvestment = { name, value, id };
investments.push(newInvestment);
return res.status(201).json(newInvestment);
});
router.get('/investments', (req, res) => {
const { name } = req.query;
if (name) {
const filteredInvestments = investments.filter((investment) =>
investment.name.includes(name)
);
return res.json(filteredInvestments);
}
return res.json(investments);
});
router.get('/investments/:id', (req, res) => {
const id = req.params.id;
const index = investments.findIndex((investment) => investment.id === id);
if (!investments[index]) {
throw new HttpError('Investment not found', 404);
}
return res.json(investments[index]);
});
router.put('/investments/:id', (req, res) => {
const { name, value } = req.body;
const { id } = req.params;
if (!name || !value) {
throw new HttpError('Error when passing parameters');
}
const newInvestment = { name, value, id };
const index = investments.findIndex((investment) => investment.id === id);
if (!investments[index]) {
throw new HttpError('Investment not found', 404);
}
investments[index] = newInvestment;
return res.json(newInvestment);
});
router.delete('/investments/:id', (req, res) => {
const { id } = req.params;
const index = investments.findIndex((investment) => investment.id === id);
if (!investments[index]) {
throw new HttpError('Investment not found', 404);
}
investments.splice(index, 1);
return res.sendStatus(204);
});
// 404 handler
router.use((req, res, next) => {
res.status(404).json({ message: 'Content not found!' });
});
// Error handler
router.use((err, req, res, next) => {
// console.error(err.stack);
if (err instanceof HttpError) {
return res.status(err.code).json({ message: err.message });
}
return res.status(500).json({ message: 'Something broke!' });
});
export default router;
/codes/expressjs/invest-app-api-back-end/src/routes.js
import express from 'express';
import { v4 as uuidv4 } from 'uuid';
import { investments } from './data/investments.js';
class HttpError extends Error {
constructor(message, code = 400) {
super(message);
this.code = code;
}
}
const router = express.Router();
router.post('/investments', (req, res) => {
const { name, value } = req.body;
if (!name || !value) {
throw new HttpError('Error when passing parameters');
}
const id = uuidv4();
const newInvestment = { name, value, id };
investments.push(newInvestment);
return res.status(201).json(newInvestment);
});
router.get('/investments', (req, res) => {
const { name } = req.query;
if (name) {
const filteredInvestments = investments.filter((investment) =>
investment.name.includes(name)
);
return res.json(filteredInvestments);
}
return res.json(investments);
});
router.get('/investments/:id', (req, res) => {
const id = req.params.id;
const index = investments.findIndex((investment) => investment.id === id);
if (!investments[index]) {
throw new HttpError('Investment not found', 404);
}
return res.json(investments[index]);
});
router.put('/investments/:id', (req, res) => {
const { name, value } = req.body;
const { id } = req.params;
if (!name || !value) {
throw new HttpError('Error when passing parameters');
}
const newInvestment = { name, value, id };
const index = investments.findIndex((investment) => investment.id === id);
if (!investments[index]) {
throw new HttpError('Investment not found', 404);
}
investments[index] = newInvestment;
return res.json(newInvestment);
});
router.delete('/investments/:id', (req, res) => {
const { id } = req.params;
const index = investments.findIndex((investment) => investment.id === id);
if (!investments[index]) {
throw new HttpError('Investment not found', 404);
}
investments.splice(index, 1);
return res.sendStatus(204);
});
// 404 handler
router.use((req, res, next) => {
res.status(404).json({ message: 'Content not found!' });
});
// Error handler
router.use((err, req, res, next) => {
// console.error(err.stack);
if (err instanceof HttpError) {
return res.status(err.code).json({ message: err.message });
}
return res.status(500).json({ message: 'Something broke!' });
});
export default router;
/codes/expressjs/invest-app-api-back-end/src/index.js
import 'express-async-errors';
import express from 'express';
import cors from 'cors';
import morgan from 'morgan';
import router from './routes.js';
const server = express();
server.use(morgan('tiny'));
server.use(
cors({
origin: '*',
methods: 'GET,HEAD,OPTIONS,PUT,PATCH,POST,DELETE',
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true,
preflightContinue: false,
})
);
server.use(express.json());
server.use('/api', router);
server.listen(3000, () => {
console.log('Server is running on port 3000');
});
/codes/expressjs/invest-app-api-back-end/src/index.js
import 'express-async-errors';
import express from 'express';
import cors from 'cors';
import morgan from 'morgan';
import router from './routes.js';
const server = express();
server.use(morgan('tiny'));
server.use(
cors({
origin: '*',
methods: 'GET,HEAD,OPTIONS,PUT,PATCH,POST,DELETE',
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true,
preflightContinue: false,
})
);
server.use(express.json());
server.use('/api', router);
server.listen(3000, () => {
console.log('Server is running on port 3000');
});
/codes/expressjs/invest-app-api-back-end/requests.http
@host = http://localhost:3000/api
@createdInvestmentId = {{createdInvestment.response.body.$.id}}
### Create Investment
# @name createdInvestment
POST {{host}}/investments
Content-Type: application/json
{
"name": "Tesouro Selic 2029",
"value": 20000
}
### Create Investment without value
POST {{host}}/investments
Content-Type: application/json
{
"name": "Tesouro Selic 2029"
}
### Create Investment without investment values
POST {{host}}/investments
### Read Investments
GET {{host}}/investments
### Read Investments by id
GET {{host}}/investments/{{createdInvestmentId}}
### Read Investments with invalid id
GET {{host}}/investments/a
### Read Investments by Name
GET {{host}}/investments?name=Selic
### Update Investment
PUT {{host}}/investments/{{createdInvestmentId}}
Content-Type: application/json
{
"name": "Tesouro Selic 2029",
"value": 25000
}
### Update Investment without value
PUT {{host}}/investments/{{createdInvestmentId}}
Content-Type: application/json
{
"name": "Tesouro Selic 2029"
}
### Update Investment with invalid id
PUT {{host}}/investments/a
Content-Type: application/json
{
"name": "Tesouro Selic 2029",
"value": 25000
}
### Delete Investment
DELETE {{host}}/investments/{{createdInvestmentId}}
### Delete Investment with invalid id
DELETE {{host}}/investments/a
/codes/expressjs/invest-app-api-back-end/requests.http
@host = http://localhost:3000/api
@createdInvestmentId = {{createdInvestment.response.body.$.id}}
### Create Investment
# @name createdInvestment
POST {{host}}/investments
Content-Type: application/json
{
"name": "Tesouro Selic 2029",
"value": 20000
}
### Create Investment without value
POST {{host}}/investments
Content-Type: application/json
{
"name": "Tesouro Selic 2029"
}
### Create Investment without investment values
POST {{host}}/investments
### Read Investments
GET {{host}}/investments
### Read Investments by id
GET {{host}}/investments/{{createdInvestmentId}}
### Read Investments with invalid id
GET {{host}}/investments/a
### Read Investments by Name
GET {{host}}/investments?name=Selic
### Update Investment
PUT {{host}}/investments/{{createdInvestmentId}}
Content-Type: application/json
{
"name": "Tesouro Selic 2029",
"value": 25000
}
### Update Investment without value
PUT {{host}}/investments/{{createdInvestmentId}}
Content-Type: application/json
{
"name": "Tesouro Selic 2029"
}
### Update Investment with invalid id
PUT {{host}}/investments/a
Content-Type: application/json
{
"name": "Tesouro Selic 2029",
"value": 25000
}
### Delete Investment
DELETE {{host}}/investments/{{createdInvestmentId}}
### Delete Investment with invalid id
DELETE {{host}}/investments/a
Create
/codes/expressjs/invest-api/src/routes.js
router.post('/investments', (req, res) => {
const { name, value } = req.body;
if (!name || !value) {
throw new HttpError('Error when passing parameters');
}
const id = uuidv4();
const newInvestment = { name, value, id };
investments.push(newInvestment);
return res.status(201).json(newInvestment);
});
/codes/expressjs/invest-api/src/routes.js
router.post('/investments', (req, res) => {
const { name, value } = req.body;
if (!name || !value) {
throw new HttpError('Error when passing parameters');
}
const id = uuidv4();
const newInvestment = { name, value, id };
investments.push(newInvestment);
return res.status(201).json(newInvestment);
});
/codes/expressjs/invest-api/requests.http
@host = http://localhost:3000
### Create Investment
# @name createdInvestment
POST {{host}}/investments
Content-Type: application/json
{
"name": "Tesouro Selic 2029",
"value": 10000
}
/codes/expressjs/invest-api/requests.http
@host = http://localhost:3000
### Create Investment
# @name createdInvestment
POST {{host}}/investments
Content-Type: application/json
{
"name": "Tesouro Selic 2029",
"value": 10000
}
Read
/codes/expressjs/invest-api/src/routes.js
router.get('/investments', (req, res) => {
const { name } = req.query;
if (name) {
const filteredInvestments = investments.filter((investment) =>
investment.name.includes(name)
);
return res.json(filteredInvestments);
}
return res.json(investments);
});
/codes/expressjs/invest-api/src/routes.js
router.get('/investments', (req, res) => {
const { name } = req.query;
if (name) {
const filteredInvestments = investments.filter((investment) =>
investment.name.includes(name)
);
return res.json(filteredInvestments);
}
return res.json(investments);
});
/codes/expressjs/invest-api/requests.http
### Read Investments
GET {{host}}/investments
/codes/expressjs/invest-api/requests.http
### Read Investments
GET {{host}}/investments
Update
/codes/expressjs/invest-api/src/routes.js
router.put('/investments/:id', (req, res) => {
const { name, value } = req.body;
const { id } = req.params;
if (!name || !value) {
throw new HttpError('Error when passing parameters');
}
const newInvestment = { name, value, id };
const index = investments.findIndex((investment) => investment.id === id);
if (!investments[index]) {
throw new HttpError('Investment not found', 404);
}
investments[index] = newInvestment;
return res.json(newInvestment);
});
/codes/expressjs/invest-api/src/routes.js
router.put('/investments/:id', (req, res) => {
const { name, value } = req.body;
const { id } = req.params;
if (!name || !value) {
throw new HttpError('Error when passing parameters');
}
const newInvestment = { name, value, id };
const index = investments.findIndex((investment) => investment.id === id);
if (!investments[index]) {
throw new HttpError('Investment not found', 404);
}
investments[index] = newInvestment;
return res.json(newInvestment);
});
/codes/expressjs/invest-api/requests.http
@host = http://localhost:3000
@createdInvestmentId = {{createdInvestment.response.body.$.id}}
### Update Investment
PUT {{host}}/investments/{{createdInvestmentId}}
Content-Type: application/json
{
"name": "Tesouro Selic 2029",
"value": 25000
}
/codes/expressjs/invest-api/requests.http
@host = http://localhost:3000
@createdInvestmentId = {{createdInvestment.response.body.$.id}}
### Update Investment
PUT {{host}}/investments/{{createdInvestmentId}}
Content-Type: application/json
{
"name": "Tesouro Selic 2029",
"value": 25000
}
Delete
/codes/expressjs/invest-api/src/routes.js
router.delete('/investments/:id', (req, res) => {
const { id } = req.params;
const index = investments.findIndex((investment) => investment.id === id);
if (!investments[index]) {
throw new HttpError('Investment not found', 404);
}
investments.splice(index, 1);
return res.sendStatus(204);
});
/codes/expressjs/invest-api/src/routes.js
router.delete('/investments/:id', (req, res) => {
const { id } = req.params;
const index = investments.findIndex((investment) => investment.id === id);
if (!investments[index]) {
throw new HttpError('Investment not found', 404);
}
investments.splice(index, 1);
return res.sendStatus(204);
});
/codes/expressjs/invest-api/requests.http
@host = http://localhost:3000
@createdInvestmentId = {{createdInvestment.response.body.$.id}}
### Delete Investment
DELETE {{host}}/investments/{{createdInvestmentId}}
/codes/expressjs/invest-api/requests.http
@host = http://localhost:3000
@createdInvestmentId = {{createdInvestment.response.body.$.id}}
### Delete Investment
DELETE {{host}}/investments/{{createdInvestmentId}}
Front-end Web
Arquivos
invest-app-api
├── package-lock.json
├── package.json
├── public
│ ├── css
│ │ └── style.css
│ ├── index.html
│ └── js
│ ├── index.js
│ ├── lib
│ │ └── format.js
│ └── services
│ └── api.js
├── requests.http
└── src
├── data
│ └── investments.js
├── index.js
└── routes.js
Arquivos
invest-app-api
├── package-lock.json
├── package.json
├── public
│ ├── css
│ │ └── style.css
│ ├── index.html
│ └── js
│ ├── index.js
│ ├── lib
│ │ └── format.js
│ └── services
│ └── api.js
├── requests.http
└── src
├── data
│ └── investments.js
├── index.js
└── routes.js
/codes/expressjs/invest-app-api/src/index.js
import 'express-async-errors';
import express from 'express';
import cors from 'cors';
import morgan from 'morgan';
import router from './routes.js';
const server = express();
server.use(morgan('tiny'));
server.use(
cors({
origin: '*',
methods: 'GET,HEAD,OPTIONS,PUT,PATCH,POST,DELETE',
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true,
preflightContinue: false,
})
);
server.use(express.json());
server.use(express.static('public'));
server.use('/api', router);
server.listen(3000, () => {
console.log('Server is running on port 3000');
});
/codes/expressjs/invest-app-api/src/index.js
import 'express-async-errors';
import express from 'express';
import cors from 'cors';
import morgan from 'morgan';
import router from './routes.js';
const server = express();
server.use(morgan('tiny'));
server.use(
cors({
origin: '*',
methods: 'GET,HEAD,OPTIONS,PUT,PATCH,POST,DELETE',
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true,
preflightContinue: false,
})
);
server.use(express.json());
server.use(express.static('public'));
server.use('/api', router);
server.listen(3000, () => {
console.log('Server is running on port 3000');
});
/codes/expressjs/invest-app-api/public/js/services/api.js
const domain = '/api';
async function create(resource, data) {
const url = `${domain}${resource}`;
const config = {
method: 'POST',
mode: 'cors',
body: JSON.stringify(data),
headers: {
'Content-Type': 'application/json; charset=UTF-8',
},
};
const res = await fetch(url, config);
return await res.json();
}
async function read(resource) {
const url = `${domain}${resource}`;
const res = await fetch(url);
return await res.json();
}
async function update(resource, data) {
const url = `${domain}${resource}`;
const config = {
method: 'PUT',
mode: 'cors',
body: JSON.stringify(data),
headers: {
'Content-Type': 'application/json; charset=UTF-8',
},
};
const res = await fetch(url, config);
return await res.json();
}
async function remove(resource) {
const url = `${domain}${resource}`;
const config = {
method: 'DELETE',
mode: 'cors',
};
await fetch(url, config);
}
export default { create, read, update, remove };
/codes/expressjs/invest-app-api/public/js/services/api.js
const domain = '/api';
async function create(resource, data) {
const url = `${domain}${resource}`;
const config = {
method: 'POST',
mode: 'cors',
body: JSON.stringify(data),
headers: {
'Content-Type': 'application/json; charset=UTF-8',
},
};
const res = await fetch(url, config);
return await res.json();
}
async function read(resource) {
const url = `${domain}${resource}`;
const res = await fetch(url);
return await res.json();
}
async function update(resource, data) {
const url = `${domain}${resource}`;
const config = {
method: 'PUT',
mode: 'cors',
body: JSON.stringify(data),
headers: {
'Content-Type': 'application/json; charset=UTF-8',
},
};
const res = await fetch(url, config);
return await res.json();
}
async function remove(resource) {
const url = `${domain}${resource}`;
const config = {
method: 'DELETE',
mode: 'cors',
};
await fetch(url, config);
}
export default { create, read, update, remove };
/codes/expressjs/invest-app-api/public/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Invest App</title>
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD"
crossorigin="anonymous"
/>
<link rel="stylesheet" href="css/style.css" />
<script src="https://code.iconify.design/3/3.1.0/iconify.min.js"></script>
</head>
<body class="pb-5">
<nav class="navbar navbar-expand-lg bg-body-tertiary">
<div class="container">
<a class="navbar-brand" href="#">Invest App</a>
<button
class="navbar-toggler"
type="button"
data-bs-toggle="collapse"
data-bs-target="#navbarSupportedContent"
aria-controls="navbarSupportedContent"
aria-expanded="false"
aria-label="Toggle navigation"
>
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="#">Home</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="container">
<h1 class="text-center my-5">Investimentos</h1>
<div id="investment-grid" class="row row-cols-1 row-cols-md-3 g-4"></div>
</div>
<div>
<button
class="btn btn-secondary create-investment"
style="position: fixed; bottom: 24px; right: 24px"
type="button"
data-bs-toggle="offcanvas"
data-bs-target="#offcanvasRight"
aria-controls="offcanvasRight"
>
+
</button>
</div>
<div
class="offcanvas offcanvas-end"
tabindex="-1"
id="offcanvasRight"
aria-labelledby="offcanvasRightLabel"
>
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="offcanvasRightLabel">
Cadastro de Investimento
</h5>
<button
type="button"
id="offcanvas-close"
class="btn-close"
data-bs-dismiss="offcanvas"
aria-label="Close"
></button>
</div>
<div class="offcanvas-body">
<form>
<div class="mb-3">
<label for="name" class="form-label">Nome</label>
<input
type="name"
class="form-control"
id="name"
name="name"
placeholder="Ex: Tesouro Selic"
/>
</div>
<div class="mb-3">
<label for="value" class="form-label">Valor</label>
<input
type="number"
class="form-control"
id="value"
name="value"
step="0.01"
placeholder="100"
/>
</div>
<div class="mb-3">
<input
type="submit"
class="form-control btn btn-secondary"
id="submit"
placeholder="100"
/>
</div>
</form>
</div>
</div>
<div class="modal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Remover Investimento</h5>
<button
type="button"
class="btn-close"
data-bs-dismiss="modal"
aria-label="Close"
></button>
</div>
<div class="modal-body">
<p>Deseja remover o investimento?</p>
</div>
<div class="modal-footer">
<button
type="button"
class="btn btn-secondary"
data-bs-dismiss="modal"
>
Fechar
</button>
<button type="button" class="btn btn-primary">Confirmar</button>
</div>
</div>
</div>
</div>
<script
src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"
integrity="sha384-w76AqPfDkMBDXo30jS1Sgez6pr3x5MlQ1ZAGC+nuZB+EYdgRZgiwxhTBTkF7CXvN"
crossorigin="anonymous"
></script>
<script src="./js/index.js" type="module"></script>
</body>
</html>
/codes/expressjs/invest-app-api/public/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Invest App</title>
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD"
crossorigin="anonymous"
/>
<link rel="stylesheet" href="css/style.css" />
<script src="https://code.iconify.design/3/3.1.0/iconify.min.js"></script>
</head>
<body class="pb-5">
<nav class="navbar navbar-expand-lg bg-body-tertiary">
<div class="container">
<a class="navbar-brand" href="#">Invest App</a>
<button
class="navbar-toggler"
type="button"
data-bs-toggle="collapse"
data-bs-target="#navbarSupportedContent"
aria-controls="navbarSupportedContent"
aria-expanded="false"
aria-label="Toggle navigation"
>
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="#">Home</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="container">
<h1 class="text-center my-5">Investimentos</h1>
<div id="investment-grid" class="row row-cols-1 row-cols-md-3 g-4"></div>
</div>
<div>
<button
class="btn btn-secondary create-investment"
style="position: fixed; bottom: 24px; right: 24px"
type="button"
data-bs-toggle="offcanvas"
data-bs-target="#offcanvasRight"
aria-controls="offcanvasRight"
>
+
</button>
</div>
<div
class="offcanvas offcanvas-end"
tabindex="-1"
id="offcanvasRight"
aria-labelledby="offcanvasRightLabel"
>
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="offcanvasRightLabel">
Cadastro de Investimento
</h5>
<button
type="button"
id="offcanvas-close"
class="btn-close"
data-bs-dismiss="offcanvas"
aria-label="Close"
></button>
</div>
<div class="offcanvas-body">
<form>
<div class="mb-3">
<label for="name" class="form-label">Nome</label>
<input
type="name"
class="form-control"
id="name"
name="name"
placeholder="Ex: Tesouro Selic"
/>
</div>
<div class="mb-3">
<label for="value" class="form-label">Valor</label>
<input
type="number"
class="form-control"
id="value"
name="value"
step="0.01"
placeholder="100"
/>
</div>
<div class="mb-3">
<input
type="submit"
class="form-control btn btn-secondary"
id="submit"
placeholder="100"
/>
</div>
</form>
</div>
</div>
<div class="modal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Remover Investimento</h5>
<button
type="button"
class="btn-close"
data-bs-dismiss="modal"
aria-label="Close"
></button>
</div>
<div class="modal-body">
<p>Deseja remover o investimento?</p>
</div>
<div class="modal-footer">
<button
type="button"
class="btn btn-secondary"
data-bs-dismiss="modal"
>
Fechar
</button>
<button type="button" class="btn btn-primary">Confirmar</button>
</div>
</div>
</div>
</div>
<script
src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"
integrity="sha384-w76AqPfDkMBDXo30jS1Sgez6pr3x5MlQ1ZAGC+nuZB+EYdgRZgiwxhTBTkF7CXvN"
crossorigin="anonymous"
></script>
<script src="./js/index.js" type="module"></script>
</body>
</html>
/codes/expressjs/invest-app-api/public/css/style.css
.card-header .icon-trash:hover,
.card-header .icon-pencil:hover {
cursor: pointer;
}
.card-header .icon-trash,
.card-header .icon-pencil {
font-size: 1.2rem;
}
/codes/expressjs/invest-app-api/public/css/style.css
.card-header .icon-trash:hover,
.card-header .icon-pencil:hover {
cursor: pointer;
}
.card-header .icon-trash,
.card-header .icon-pencil {
font-size: 1.2rem;
}
/codes/expressjs/invest-app-api/public/js/index.js
import API from './services/api.js';
import { formatCurrency } from './lib/format.js';
let removedHostId;
const bsOffcanvas = new bootstrap.Offcanvas('.offcanvas');
const confirmModal = new bootstrap.Modal('.modal');
function InvestmentCard(investment) {
return `<div class="col" id="investment-${investment.id}">
<div class="card">
<div class="card-header">
<span class="investment-name">
${investment.name}
</span>
<span class="float-end">
<span class="icon-trash" >
<span
class="iconify"
data-icon="solar:trash-bin-minimalistic-broken"
>
</span>
</span>
<span class="icon-pencil">
<span
class="iconify"
data-icon="tabler:pencil"
>
</span>
</span>
</span>
</div>
<div class="card-body">
<div>
<span class="fw-bold">Valor:</span>
<span class="investment-value">
${formatCurrency(investment.value / 100)}
</span>
</div>
</div>
</div>
</div>`;
}
function createInvestmentCard(investment) {
const investmentContainer = document.querySelector(`#investment-grid`);
investmentContainer.insertAdjacentHTML(
'beforeend',
InvestmentCard(investment)
);
loadHandleConfirmModal(investment.id);
loadHandleUpdateInvestment(investment.id);
}
async function loadInvestmentCards() {
const investments = await API.read('/investments');
for (const investment of investments) {
createInvestmentCard(investment);
}
}
function updateInvestmentCard({ id, name, value }) {
document.querySelector(`#investment-${id} .investment-name`).innerText = name;
document.querySelector(`#investment-${id} .investment-value`).innerText =
formatCurrency(value / 100);
}
function loadHandleFormSubmit(type, id) {
const form = document.querySelector('form');
form.onsubmit = async (event) => {
event.preventDefault();
const investment = Object.fromEntries(new FormData(form));
investment.value = Number(investment.value) * 100;
if (type === 'create') {
const createdInvestment = await API.create('/investments', investment);
createInvestmentCard(createdInvestment);
} else if (type === 'update') {
const updatedInvestment = await API.update(
`/investments/${id}`,
investment
);
updateInvestmentCard(updatedInvestment);
}
form.reset();
document.querySelector('#offcanvas-close').click();
};
}
function loadHandleCreateInvestment() {
const button = document.querySelector('.btn.create-investment');
button.onclick = () => {
bsOffcanvas.show();
loadHandleFormSubmit('create');
};
}
function loadHandleUpdateInvestment(id) {
const iconPencil = document.querySelector(`#investment-${id} .icon-pencil`);
iconPencil.onclick = async () => {
const investment = await API.read(`/investments/${id}`);
const { name, value } = investment;
document.querySelector('form #name').value = name;
document.querySelector('form #value').value = value / 100;
bsOffcanvas.show();
loadHandleFormSubmit('update', id);
};
}
function loadHandleConfirmModal(id) {
const iconTrash = document.querySelector(`#investment-${id} .icon-trash`);
iconTrash.onclick = () => {
removedHostId = id;
confirmModal.show();
};
}
function loadHandleRemoveInvestment() {
const confirmBtn = document.querySelector('.modal .btn-primary');
confirmBtn.onclick = () => {
API.remove(`/investments/${removedHostId}`);
document.querySelector(`#investment-${removedHostId}`).remove();
confirmModal.hide();
};
}
loadInvestmentCards();
loadHandleCreateInvestment();
loadHandleRemoveInvestment();
/codes/expressjs/invest-app-api/public/js/index.js
import API from './services/api.js';
import { formatCurrency } from './lib/format.js';
let removedHostId;
const bsOffcanvas = new bootstrap.Offcanvas('.offcanvas');
const confirmModal = new bootstrap.Modal('.modal');
function InvestmentCard(investment) {
return `<div class="col" id="investment-${investment.id}">
<div class="card">
<div class="card-header">
<span class="investment-name">
${investment.name}
</span>
<span class="float-end">
<span class="icon-trash" >
<span
class="iconify"
data-icon="solar:trash-bin-minimalistic-broken"
>
</span>
</span>
<span class="icon-pencil">
<span
class="iconify"
data-icon="tabler:pencil"
>
</span>
</span>
</span>
</div>
<div class="card-body">
<div>
<span class="fw-bold">Valor:</span>
<span class="investment-value">
${formatCurrency(investment.value / 100)}
</span>
</div>
</div>
</div>
</div>`;
}
function createInvestmentCard(investment) {
const investmentContainer = document.querySelector(`#investment-grid`);
investmentContainer.insertAdjacentHTML(
'beforeend',
InvestmentCard(investment)
);
loadHandleConfirmModal(investment.id);
loadHandleUpdateInvestment(investment.id);
}
async function loadInvestmentCards() {
const investments = await API.read('/investments');
for (const investment of investments) {
createInvestmentCard(investment);
}
}
function updateInvestmentCard({ id, name, value }) {
document.querySelector(`#investment-${id} .investment-name`).innerText = name;
document.querySelector(`#investment-${id} .investment-value`).innerText =
formatCurrency(value / 100);
}
function loadHandleFormSubmit(type, id) {
const form = document.querySelector('form');
form.onsubmit = async (event) => {
event.preventDefault();
const investment = Object.fromEntries(new FormData(form));
investment.value = Number(investment.value) * 100;
if (type === 'create') {
const createdInvestment = await API.create('/investments', investment);
createInvestmentCard(createdInvestment);
} else if (type === 'update') {
const updatedInvestment = await API.update(
`/investments/${id}`,
investment
);
updateInvestmentCard(updatedInvestment);
}
form.reset();
document.querySelector('#offcanvas-close').click();
};
}
function loadHandleCreateInvestment() {
const button = document.querySelector('.btn.create-investment');
button.onclick = () => {
bsOffcanvas.show();
loadHandleFormSubmit('create');
};
}
function loadHandleUpdateInvestment(id) {
const iconPencil = document.querySelector(`#investment-${id} .icon-pencil`);
iconPencil.onclick = async () => {
const investment = await API.read(`/investments/${id}`);
const { name, value } = investment;
document.querySelector('form #name').value = name;
document.querySelector('form #value').value = value / 100;
bsOffcanvas.show();
loadHandleFormSubmit('update', id);
};
}
function loadHandleConfirmModal(id) {
const iconTrash = document.querySelector(`#investment-${id} .icon-trash`);
iconTrash.onclick = () => {
removedHostId = id;
confirmModal.show();
};
}
function loadHandleRemoveInvestment() {
const confirmBtn = document.querySelector('.modal .btn-primary');
confirmBtn.onclick = () => {
API.remove(`/investments/${removedHostId}`);
document.querySelector(`#investment-${removedHostId}`).remove();
confirmModal.hide();
};
}
loadInvestmentCards();
loadHandleCreateInvestment();
loadHandleRemoveInvestment();
/codes/expressjs/invest-app-api/public/js/lib/format.js
export function formatCurrency(value) {
return Number(value).toLocaleString('pt-br', {
style: 'currency',
currency: 'BRL',
});
}
/codes/expressjs/invest-app-api/public/js/lib/format.js
export function formatCurrency(value) {
return Number(value).toLocaleString('pt-br', {
style: 'currency',
currency: 'BRL',
});
}