Prisma.js
Arquivos
invest-app-prismajs-simple
├── package-lock.json
├── package.json
├── prisma
│ ├── dev.db
│ ├── migrations
│ │ ├── 20230826214323_init
│ │ │ └── migration.sql
│ │ └── migration_lock.toml
│ ├── schema.prisma
│ ├── seed.js
│ └── seeders.json
├── public
│ ├── css
│ │ └── style.css
│ ├── index.html
│ └── js
│ ├── index.js
│ ├── lib
│ │ └── format.js
│ └── services
│ └── api.js
├── requests.http
└── src
├── database
│ └── database.js
├── index.js
├── models
│ └── Investment.js
└── routes.js
Arquivos
invest-app-prismajs-simple
├── package-lock.json
├── package.json
├── prisma
│ ├── dev.db
│ ├── migrations
│ │ ├── 20230826214323_init
│ │ │ └── migration.sql
│ │ └── migration_lock.toml
│ ├── schema.prisma
│ ├── seed.js
│ └── seeders.json
├── public
│ ├── css
│ │ └── style.css
│ ├── index.html
│ └── js
│ ├── index.js
│ ├── lib
│ │ └── format.js
│ └── services
│ └── api.js
├── requests.http
└── src
├── database
│ └── database.js
├── index.js
├── models
│ └── Investment.js
└── routes.js
Migration
$ npm remove sqlite-async
$ npm install prisma
$ npx prisma init --datasource-provider sqlite
$ npm remove sqlite-async
$ npm install prisma
$ npx prisma init --datasource-provider sqlite
Para visualizar o schema.prisma
use o plugin prisma.
/codes/expressjs/invest-app-prismajs-simple/prisma/schema.prisma
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}
model Investment {
id String @id @default(uuid())
name String
value Int
}
/codes/expressjs/invest-app-prismajs-simple/prisma/schema.prisma
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}
model Investment {
id String @id @default(uuid())
name String
value Int
}
Tabela investment
:
/codes/expressjs/invest-app-prismajs-simple/.env.example
# Environment variables declared in this file are automatically made available to Prisma.
# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema
# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings
DATABASE_URL="file:./dev.db"
/codes/expressjs/invest-app-prismajs-simple/.env.example
# Environment variables declared in this file are automatically made available to Prisma.
# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema
# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings
DATABASE_URL="file:./dev.db"
$ cp .env.example .env
$ cp .env.example .env
$ npx prisma migrate dev --name init
$ npx prisma studio
$ npx prisma migrate dev --name init
$ npx prisma studio
Seed
/codes/expressjs/invest-app-prismajs-simple/prisma/seeders.json
{
"investments": [
{
"name": "Tesouro Selic 2029",
"value": 100000
},
{
"name": "Tesouro Selic 2029",
"value": 50000
}
]
}
/codes/expressjs/invest-app-prismajs-simple/prisma/seeders.json
{
"investments": [
{
"name": "Tesouro Selic 2029",
"value": 100000
},
{
"name": "Tesouro Selic 2029",
"value": 50000
}
]
}
/codes/expressjs/invest-app-prismajs-simple/prisma/seed.js
import { resolve } from 'node:path';
import { readFileSync } from 'node:fs';
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
async function main() {
const file = resolve('prisma', 'seeders.json');
const seed = JSON.parse(readFileSync(file));
await prisma.investment.createMany({
data: seed.investments,
});
}
main()
.then(async () => {
await prisma.$disconnect();
})
.catch(async (e) => {
console.error(e);
await prisma.$disconnect();
process.exit(1);
});
/codes/expressjs/invest-app-prismajs-simple/prisma/seed.js
import { resolve } from 'node:path';
import { readFileSync } from 'node:fs';
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
async function main() {
const file = resolve('prisma', 'seeders.json');
const seed = JSON.parse(readFileSync(file));
await prisma.investment.createMany({
data: seed.investments,
});
}
main()
.then(async () => {
await prisma.$disconnect();
})
.catch(async (e) => {
console.error(e);
await prisma.$disconnect();
process.exit(1);
});
/codes/expressjs/invest-app-prismajs-simple/package.json
{
"name": "invest-app",
"type": "module",
"scripts": {
"start": "node src/index.js",
"dev": "node --watch src/index.js"
},
"prisma": {
"seed": "node prisma/seed.js"
},
"dependencies": {
"@prisma/client": "^5.19.0",
"cors": "^2.8.5",
"express": "^4.18.2",
"express-async-errors": "^3.1.1",
"morgan": "^1.10.0",
"prisma": "^5.19.0"
}
}
/codes/expressjs/invest-app-prismajs-simple/package.json
{
"name": "invest-app",
"type": "module",
"scripts": {
"start": "node src/index.js",
"dev": "node --watch src/index.js"
},
"prisma": {
"seed": "node prisma/seed.js"
},
"dependencies": {
"@prisma/client": "^5.19.0",
"cors": "^2.8.5",
"express": "^4.18.2",
"express-async-errors": "^3.1.1",
"morgan": "^1.10.0",
"prisma": "^5.19.0"
}
}
$ npx prisma db seed
$ npx prisma studio
$ npx prisma db seed
$ npx prisma studio
Model
/codes/expressjs/invest-app-prismajs-simple/src/database/database.js
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient({
log: ['query', 'info', 'warn', 'error'],
});
export default prisma;
/codes/expressjs/invest-app-prismajs-simple/src/database/database.js
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient({
log: ['query', 'info', 'warn', 'error'],
});
export default prisma;
/codes/expressjs/invest-app-prismajs-simple/src/models/Investment.js
import prisma from '../database/database.js';
async function create({ name, value }) {
if (name && value) {
const createdInvestment = await prisma.investment.create({
data: { name, value },
});
return createdInvestment;
} else {
throw new Error('Unable to create investment');
}
}
async function read(where) {
if (where?.name) {
where.name = {
contains: where.name,
};
}
const investments = await prisma.investment.findMany({ where });
if (investments.length === 1 && where) {
return investments[0];
}
return investments;
}
async function readById(id) {
if (id) {
const investment = await prisma.investment.findUnique({
where: {
id,
},
});
return investment;
} else {
throw new Error('Unable to find investment');
}
}
async function update({ id, name, value }) {
if (name && value && id) {
const updatedInvestment = await prisma.investment.update({
where: {
id,
},
data: { name, value },
});
return updatedInvestment;
} else {
throw new Error('Unable to update investment');
}
}
async function remove(id) {
if (id) {
await prisma.investment.delete({
where: {
id,
},
});
return true;
} else {
throw new Error('Unable to remove investment');
}
}
export default { create, read, readById, update, remove };
/codes/expressjs/invest-app-prismajs-simple/src/models/Investment.js
import prisma from '../database/database.js';
async function create({ name, value }) {
if (name && value) {
const createdInvestment = await prisma.investment.create({
data: { name, value },
});
return createdInvestment;
} else {
throw new Error('Unable to create investment');
}
}
async function read(where) {
if (where?.name) {
where.name = {
contains: where.name,
};
}
const investments = await prisma.investment.findMany({ where });
if (investments.length === 1 && where) {
return investments[0];
}
return investments;
}
async function readById(id) {
if (id) {
const investment = await prisma.investment.findUnique({
where: {
id,
},
});
return investment;
} else {
throw new Error('Unable to find investment');
}
}
async function update({ id, name, value }) {
if (name && value && id) {
const updatedInvestment = await prisma.investment.update({
where: {
id,
},
data: { name, value },
});
return updatedInvestment;
} else {
throw new Error('Unable to update investment');
}
}
async function remove(id) {
if (id) {
await prisma.investment.delete({
where: {
id,
},
});
return true;
} else {
throw new Error('Unable to remove investment');
}
}
export default { create, read, readById, update, remove };
View
/codes/expressjs/invest-app-prismajs-simple/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-prismajs-simple/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-prismajs-simple/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-prismajs-simple/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();