Construção de API

Back-end Web

Rotas do Invest API

MétodoCaminhoParâmetroStatusResposta
POST/api/investmentsBody201, 400Cria um novo investimento
GET/api/investments-200Retorna todas os investimentos
GET/api/investments?name=SaladaQuery200Retorna todas os investimentos com o nome Tesouro
GET/api/investments/XRoute200, 404Retorna o investimento de ID X
PUT/api/investments/XBody, Route200, 400, 404Atualiza o investimento de ID X
DELETE/api/investments/XRoute204, 404Exclui o investimento de ID X

HTTP Status Codes

Classes

Códigos do Invest API

CódigoNomeSignificado
200OkSolicitação gerada com sucesso
201CreatedSolicitação gerada com sucesso e um novo recurso foi criado como resultado
204No ContentSolicitação gerada com sucesso e não há conteúdo para ser enviado
304Not ModifiedO conteúdo da requisição já existe em cache e não foi modificado
400Bad RequestSolicitação não compreendida por motivos de erro
401UnauthorizedA solicitação requer autenticação do usuário
404Not FoundO servidor não pode encontrar o recurso solicitado
500Internal Server ErrorO servidor encontrou uma situação com a qual não sabe lidar.

API de Investimentos

Open in GitHub Open in Codespaces

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

Open in GitHub Open in Codespaces

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',
  });
}
 

Editar esta página