0% acharam este documento útil (0 voto)
0 visualizações176 páginas

Express API

O documento apresenta o padrão de arquitetura de software MVC, que organiza aplicações em três camadas: Modelos, Views e Controladores, visando facilitar a manutenção do código. Ele também discute a evolução para Single-Page Applications (SPAs) e o uso de REST APIs, destacando as vantagens e desvantagens de cada abordagem. Por fim, menciona a implementação prática de um sistema SPA para uma loja virtual, utilizando React e uma API desenvolvida em conformidade com o padrão REST.

Enviado por

Gabriel Silva
Direitos autorais
© All Rights Reserved
Levamos muito a sério os direitos de conteúdo. Se você suspeita que este conteúdo é seu, reivindique-o aqui.
Formatos disponíveis
Baixe no formato PDF, TXT ou leia on-line no Scribd
0% acharam este documento útil (0 voto)
0 visualizações176 páginas

Express API

O documento apresenta o padrão de arquitetura de software MVC, que organiza aplicações em três camadas: Modelos, Views e Controladores, visando facilitar a manutenção do código. Ele também discute a evolução para Single-Page Applications (SPAs) e o uso de REST APIs, destacando as vantagens e desvantagens de cada abordagem. Por fim, menciona a implementação prática de um sistema SPA para uma loja virtual, utilizando React e uma API desenvolvida em conformidade com o padrão REST.

Enviado por

Gabriel Silva
Direitos autorais
© All Rights Reserved
Levamos muito a sério os direitos de conteúdo. Se você suspeita que este conteúdo é seu, reivindique-o aqui.
Formatos disponíveis
Baixe no formato PDF, TXT ou leia on-line no Scribd

Prof.

David Fernandes de Oliveira


Instituto de Computação
UFAM
O MVC

O MVC é um padrão de arquitetura de software que separa as
aplicações em 3 camadas: Modelos, Views e Controladores

O objetivo de separar a arquitetura nas três camadas é facilitar a
organização, compreensão e a manutenção do código

Frameworks Web MVC: Yii2, Laravel, Sails, Adonis, Django, etc

Controlador Modelo

View
O MVC
2
A requisição é roteada
1 Requisição Roteador para um controlador
HTTP

O controlador pede
3 dados para os modelos

Controlador Modelo

O controlador retorna Os modelos retornam os


a página construída 4 dados requisitados
para o usuário
6
5
O controlador usa as views
View e os dados do modelo para
gerar a página
O MVC
2
A requisição é roteada
1 Requisição Roteador para um controlador
HTTP

O controlador pede
3 dados para os modelos
O modelo MVC define que o conteúdo HTML e os dados devem
Controlador
estar separados em camadas, Modelo de uma
sendo orquestrados através
terceira camada chamada controller
O controlador retorna Os modelos retornam os
a página construída 4 dados requisitados
para o usuário
6
5
O controlador usa as views
View e os dados do modelo para
gerar a página
Views

As views são responsáveis por gerar automaticamente o
código HTML que é enviado pelo usuário a cada requisicão

Com o HTML gerado, ele é retornado para o cliente pelo controller

Existem muitas engines de views disponíveis para o Express,
dentre as quais destaca-se: EJS, Handlebars, Pug e Mustache
Sistemas MVC

Nos sistemas MVC, o servidor é responsável por executar a
maior parte da lógica da aplicacão

A cada requisicão ao sistema, o servidor precisa retornar o
todo o conteúdo HTML, CSS e JavaScript do recurso desejado
Servidor
Browser
[Link]
GET /index
Linha do tempo

Linha do tempo
Página HTML / CSS / JavaScript
GET /about

Página HTML / CSS / JavaScript


GET /login

Página HTML / CSS / JavaScript


Sistemas MVC

Nos sistemas MVC, o servidor é responsável por executar a
maior parte da lógica da aplicacão

A cada requisicão ao sistema, o servidor precisa retornar o
todo o conteúdo HTML, CSS e JavaScript do recurso desejado
Servidor
Browser
[Link]
GET /index
Linha do tempo

Linha do tempo
Note que, para carregar uma
Página única
HTML página,
/ CSS são feitas várias requisicões
/ JavaScript
GET ao servidor, uma para cada arquivo CSS, JavaScript, imagem, etc
GET /about

Página HTML / CSS / JavaScript


GET /login

Página HTML / CSS / JavaScript


Sistemas MVC

Nos sistemas MVC, o servidor é responsável por executar a
maior parte da lógica da aplicacão

A cada requisicão ao sistema, o servidor precisa retornar o
todo o conteúdo HTML, CSS e JavaScript do recurso desejado
Servidor
Browser
[Link]
GET /index
Linha do tempo

Linha do tempo
Note que, para carregar uma
Página única
HTML página,
/ CSS são feitas várias requisicões
/ JavaScript
Desta forma, a cada acesso a uma nova página do servidor,
GET ao servidor, uma para cada arquivo CSS, JavaScript, o usuário
imagem, etc
precisa esperar até que todoGET
o conteúdo
/about seja carregado em seu browser

Página HTML / CSS / JavaScript


GET /login

Página HTML / CSS / JavaScript


Uma revolucão chamada AJAX

Em 2005, Jesse Garrett introduziu o conceito de AJAX,
tornando possível a criacão de páginas muito mais dinâmicas

Um dos primeiros grandes sistemas a usar essa nova
tecnologia de forma realmente produtiva foi o Gmail
Uma revolucão chamada AJAX

Em 2005, Jesse Garrett introduziu o conceito de AJAX,
tornando possível a criacão de páginas muito mais dinâmicas

Um dos primeiros grandes sistemas a usar essa nova
tecnologia de forma realmente produtiva foi o Gmail

Utilizando requisições assíncronas e em background, o Gmail conseguiu


trazer uma excelente experiência realmente nova para os usuários
Uma revolucão chamada AJAX

Em 2005, Jesse Garrett introduziu o conceito de AJAX,
tornando possível a criacão de páginas muito mais dinâmicas

Um dos primeiros grandes sistemas a usar essa nova
tecnologia de forma realmente produtiva foi o Gmail

Utilizando requisicões assíncronas e em background, o Gmail conseguiu


Foi uma das primeiras vezes que se conseguiu trazer a sensacão
trazer uma excelente experiência realmente nova para os usuários
de uso de uma aplicacão desktop para dentro de um sistema Web -
e esse é um dos pilares das single-page applications
Single-Page Applications

Um SPA é uma aplicação web que roda em uma única página,
de uma forma similar à uma aplicacao desktop ou mobile

Executam a maior parte da lógica da aplicacão no browser,
comunicando-se com o servidor através de APIs
Servidor
Browser
[Link]
GET /index
Linha do tempo

Linha do tempo
Página HTML / CSS / JavaScript
Link clicado

JSON
Link clicado

JSON
Single-Page Applications

Um SPA é uma aplicação web que roda em uma única página,
de uma forma similar à uma aplicacao desktop ou mobile

Executam a maior parte da lógica da aplicacão no browser,
comunicando-se com o servidor através de APIs
Servidor
Browser
[Link]
GET /index
Linha do tempo

Linha do tempo
Nesse modelo, a aplicação não é /totalmente
Página HTML recarregada quanto o usuário
CSS / JavaScript
troca de página: o código javascript altera apenas as partes necessárias
Link clicado

JSON
Link clicado

JSON
Vantagens e Desvantagens

Vantagens das Single-Page Applications

Páginas mais reativas, interação com o usuário mais fluida

Alto desacoplamento entre backend e frontend

Vários frameworks para o frontend

Desvantagens

Requer uma política de Search Engine Optimization diferenciada

Carregamento inicial com muito mais código

Requer conhecimentos sólidos de programação JavaScript

Perigo de descontinuidade das bibliotecas usadas, ou geração de
novas versões incompatíveis com as anteriores
Vantagens e Desvantagens

Vantagens dos Sistemas Web Tradicionais

Técnicas mais consolidadas

Search Engine Optimization mais simples

Mais fácil de implementar

Menor acoplamento com código javascript no lado cliente

Desvantagens:

Experiência de usuário inferior, pois todo o conteudo da página é
recarregado a cada nova requisição

Forte acoplamento dentre frontend e backend

Arquitetura defasada
REST APIs

O REST – Representational State Transfer – é caracterizado
como um paradigma de desenvolvimento de software
semelhante aos webservices

Nesse paradigma, um serviço (normalmente chamado de API) é
fornecido para acesso e manipulação dos dados de uma aplicação

API – Application Programming Interface – é um conjunto de
rotinas usadas na comunicação entre duas partes de uma
aplicação
REST APIs

O REST – Representational State Transfer – é caracterizado
como um paradigma de desenvolvimento de software
semelhante aos webservices

Nesse paradigma, um serviço (normalmente chamado de API) é
fornecido para acesso e manipulação dos dados de uma aplicação
 OAPI
Front, que consomeProgramming
– Application os dados da API,Interface
pode ser desenvolvido através de
– é um conjunto
de vários tipos de frameworks frontend, como o React, Angular e o [Link]
rotinas usadas na comunicação entre duas partes de uma
aplicação
Sistema de Loja Virtual

Como prática de desenvolvimento nesta etapa do curso, cada
aluno irá desenvolver um sistema SPA para uma loja virtual

Além da API, que será desenvolvida no presente módulo, a loja
virtual também terá um frontend desenvolvido em React,
testes integrados, além de uma estratégia de CI/CD
Movendo para o padrão REST

Nas aplicações MVC, cada página é contruída através de uma
action de um dado controlador

Por exemplo a função about do controlador main tem por objetivo
construir e retornar o conteúdo HTML da página /about

Nas aplicações REST, por outro lado, as páginas de uma
aplicação são definidas no lado Front e não no lado Back

O Back nesse caso é responsável por responder a chamadas HTTP
do Front, realizando os processos de negócio e de persistência que
sejam pertinentes a cada situação
Movendo para o padrão REST
2
A requisição é roteada
1 Requisição Roteador para um controlador
HTTP
O controlador pede
dados para os serviços
3
Controlador Serviço

O controlador retorna 4
um JSON payload
para o client
5
O serviço faz consultas
ao banco usando os Modelo
modelos, além de
processar/formatar
os dados recebidos,
antes de enviá-los ao
controlador
Movendo para o padrão REST

Para o padrão REST, optamos por organizar os arquivos de
nossa aplicação usando o esquema abaixo

O diretório src terá todos os arquivos fontes da aplicação,
exceto os modelos
Movendo para o padrão REST

Para o padrão REST, optamos por organizar os arquivos dedos
Dentro
nossa aplicação usando o esquema abaixo subdiretórios
haverá um roteador,

O diretório src terá todos os arquivos fontes da aplicação,
um controlador, um
serviço e um arquivo
exceto os modelos O diretorio resources de tipos
terá um subdiretório
para cada entidade
da aplicação
Elementos de uma Requisição

O endpoint é o caminho usado para fazer uma requisição,
possuindo um resource e opcionalmente uma query string

[Link]
resource ou path query string

O método HTTP define o tipo de ação desejada pela
requisição, sendo que os métodos mais usados são:

Get, usado para buscar dados do servidor

Post, usado para enviar dados para o servidor

Put e Patch, usado para atualizar dados

Delete, usado para apagar registros no servidor
Elementos de uma Requisição

O body é o corpo da mensagem enviada na requisição, e é
usado apenas com os métodos POST, PUT e PATCH

Os HTTP status codes servem para indicar se uma requisição
HTTP foi corretamente concluída

Os principais códigos
utilizados para as respostas
de um endpoint são o 200
(OK), o 201 (CREATED), o
204 (NO CONTENT), o 404
(NOT FOUND) e o 400 (BAD
REQUEST).
Elementos de uma Requisição

O body é o corpo da mensagem enviada na requisição, e é
usado apenas com os métodos POST, PUT e PATCH
Descrição dos vários HTTP Status Code: [Link]

Os HTTP status codes servem para indicar se uma requisição
HTTP foi corretamente concluída

Os principais códigos
utilizados para as respostas
de um endpoint são o 200
(OK), o 201 (CREATED), o
204 (NO CONTENT), o 404
(NOT FOUND) e o 400 (BAD
REQUEST).
O Roteador

O Roteador de cada resource contém as rotas associadas ao
resource, referenciando as actions do controlador
// Arquivo src/resources/product/[Link]
import { Router } from 'express';
import productController from './[Link]';
const router = Router();
// Product controller
[Link]('/', [Link]);
[Link]('/', [Link]);
[Link]('/:id', [Link]);
[Link]('/:id', [Link]);
[Link]('/:id', [Link]);
export default router;
O Roteador

O Roteador de cada resource contém as rotas associadas ao
resource, referenciando as actions do controlador
// Arquivo src/resources/product/[Link]
import { Router } from 'express';
import productController from './[Link]';
const router = Router();
//Embora
Productnão faça parte do CRUD, o objetivo da rota
controller /product
é listar os produtos
[Link]('/', existentes
[Link]);
[Link]('/', [Link]);
[Link]('/:id', [Link]);
[Link]('/:id', [Link]);
[Link]('/:id', [Link]);
export default router;
O Roteador

O Roteador de cada resource contém as rotas associadas ao
resource, referenciando as actions do controlador
// Arquivo src/resources/product/[Link]
import { Router } from 'express';
import productController from './[Link]';
const router = Router();
//Embora
Productnão faça parte do CRUD, o objetivo da rota
controller /product
é Note
listar que as rotas
os produtos
[Link]('/', para read, update e remove terminam com
existentes
[Link]);
a string :id, [Link]);
[Link]('/', representa um parâmetro utilizado para
informar que produto
[Link]('/:id', se de deseja ler, atualizar ou apagar
[Link]);
[Link]('/:id', [Link]);
[Link]('/:id', [Link]);
export default router;
O Roteador

O Roteador de cada resource contém as rotas associadas ao
resource, referenciando as actions do controlador
// Arquivo src/resources/product/[Link]
import { Router } from 'express';
import productController from './[Link]';
// Arquivo src/router/[Link]
const router = Router();
import express from 'express';
//Embora
Product
import não faça parte
controller
productRouter from do CRUD, o objetivo da rota /product
'../resources/product/[Link]';
é Note
listar que
os as rotas
produtos
[Link]('/', para read, update e remove terminam com
existentes
[Link]);
constarouter
string :id, [Link]);
representa um parâmetro utilizado para
= [Link]();
[Link]('/',
informar que produto
[Link]('/:id',
[Link]('/product', se de deseja ler, atualizar ou apagar
[Link]);
productRouter);
[Link]('/:id', [Link]); O arquivo de
export default router; [Link]);
[Link]('/:id', rotas principal
irá importar as
export default router; rotas de cada
resource
O Roteador

Os parâmatros permitem passar dados informações adicionais
para o endpoint desejado

Por exemplo, na url [Link] o
valor do parâmetro id é 1234

Para ler o valor de id dentro de uma função, podemos usar o
atributo param de req (objeto da requisição do usuário):
async function read (req, res) {
const productId = [Link];
[Link](productId);
},
O Roteador

O Express possui um middleware chamado json(), que é usado
para extrair os dados do corpo da requisição ([Link])

Para usá-lo, basta inserir a linha abaixo no arquivo src/[Link]
antes da chamada ao middleware router:

// Arquivo src/[Link]
...
[Link]([Link]());
[Link](router);


Após isso, o [Link]() irá extrair os dados do request
body das requisições e copiá-los no objeto [Link]
github
ExpAPI
Crie uma API REST usando o framework Express contendo
os endpoints index, create, read, update e delete para o
resource productArray. Use um array product no controlador
para simular a existência de um banco de dados.
Esquema de banco de dados e ORM

Nossa aplicação usará o ORM Prisma, e os modelos serão
definidos no arquivo prisma/[Link]
Esquema de banco de dados e ORM

Nossa aplicação usará o ORM Prisma, e os modelos serão
definidos no arquivo prisma/[Link]

Descrição das Tabelas:


● User: dados dos usuários da aplicação

● UserType: determina se o usuário é cliente ou admin

● Purchase: compras feitas pelos clientes

● PurchaseItem: itens das compras feitas

● Product: produtos vendidos na loja


Object Relational Mapper - ORM

ORM é uma técnica que permite consultar e manipular dados
de um database usando o paradigma de orientação a objetos

Desta forma, o acesso aos dados não é feito através da
linguagem SQL, e sim através de objetos
Escolhendo o ORM
Escolhendo o ORM
Escolhendo o ORM

Sistema de ORM escolhido:


Escolhendo o ORM

Sistema de ORM escolhido:



Para seguirmos, vamos criar um container Docker com o SGBD
MySQL e outro com o PhpMyAdmin para acesso ao banco

Para que o PhpMyAdmin acesse ao banco, precisamos criar
uma rede Docker para que os dois containers se comuniquem
$ docker network create network-shop

Agora vamos criar um container Docker com o SGBD MySQL
$ docker run -d \
--name mysql-shop \
--network network-shop \
--restart always \
-p 3307:3306 \
-e MYSQL_ROOT_PASSWORD=senhasegura \
-e MYSQL_DATABASE=shop \
-v mysql-volume-shop:/var/lib/mysql \
mysql:latest


Os dados de acesso ao banco de dados criado são:

Porta: 3307

Nome do banco: shop

Host: [Link]

Senha do root: senhasegura

Para acesar o banco de dados criados, vamos criar um
container contendo uma instância do PhpMyAdmin
$ docker run -d \
--name phpmyadmin-shop \
--network network-shop \
--restart always \
-e PMA_HOST=mysql-shop \
-e PMA_PORT=3306 \
-e PMA_USER=root \
-e PMA_PASSWORD=senhasegura \
-p 8080:80 \
phpmyadmin/phpmyadmin

Para acesar o banco de dados criados, vamos criar um
container contendo uma instância do PhpMyAdmin

$ docker run -d \
--name phpmyadmin \
--network loja-network \
-e PMA_HOST=mysql-loja \
-e PMA_PORT=3306 \
-e PMA_USER=root \
-e PMA_PASSWORD=senhasegura \
-p 8080:80 \
phpmyadmin/phpmyadmin

Use os comandos abaixo para instalar o Prisma como
dependência de desenvolvimento
$ npm install -D prisma
$ npm install @prisma/client @prisma/adapter-mariadb


Após isso, execute o comando abaixo para que o Prisma crie os
arquivos iniciais de configuração
$ npx prisma init --datasource-provider mysql

Use os comandos abaixo para instalar o Prisma como
dependência de desenvolvimento
$ npm install -D prisma
$ npm install @prisma/client


Após isso, execute o comando abaixo para que o Prisma crie os
arquivos iniciais de configuração
$ npx prisma init --datasource-provider mysql

O comando prisma init adiciona uma variável DATABASE_URL
no arquivo .env, contendo uma string de conexão com o banco

Será preciso editar o valor dessa variável conforme a realidade
do banco de dados utilizado
DATABASE_URL="mysql://root:senhasegura@[Link]:3307/shop"

usuário senha nome


SGBD host porta do
do do
banco usuário banco

O comando prisma init também cria um arquivo prisma/
[Link], onde serão criados os modelos da aplicação

Nesse arquivo, é importante que provider seja mysql,
conforme mostrado abaixo

generator client {
O
provider = "prisma-client"
output = "../src/generated/prisma" provider
} precisa ser
mysql
datasource db {
provider = "mysql"
}

O comando prisma init também cria um arquivo prisma/
[Link], onde serão criados os modelos da aplicação

Nesse arquivo, é importante alterar o provider para mysql,
conforme mostrado abaixo

generator client {
provider = "prisma-client-js"
Para usar o Prisma,
} Mudar o
é altamente
provider
recomedado instalar
datasource db { para mysql Prisma,
a extensão
provider = "mysql"
do Visual Studio
url = env("DATABASE_URL")
}
Adicionando um modelo

Ainda no arquivo prisma/[Link], vamos adicionar o
primeiro modelo de nossa aplicação

model Product {
id String @id @default(uuid()) @[Link](36)
name String @unique @[Link](100)
price Decimal @[Link](10,2)
Stock Int @[Link]()
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
Adicionando um modelo

Após isso, usamos o comando npx prisma migrate dev --name
para gerar a migração e criar a tabela Product
Adicionando um modelo

Após isso, usamos o comando npx prisma migrate dev --name
para gerar a migração e criar a tabela Product
Adicionando um modelo

Após isso, usamos o comando npx prisma migrate dev --name
para gerar a migração e criar a tabela Product
Adicionando um modelo

Depois é preciso rodar npx prisma generate, para o prisma
cliente criar as classes e tipos usados para acessar o banco
Exercício

Instale e configure o Prisma em


sua aplicação. Após isso, crie o
modelo Product e rode a migration
para que a tabela majors seja
criada no banco de dados.
github
ExpAPI
Encontrando registros

Para recuperar registros no banco de dados:
import { PrismaClient, User } from "../generated/prisma/client";
import { PrismaMariaDb } from "@prisma/adapter-mariadb";
const adapter = new PrismaMariaDb([Link].DATABASE_URL!);
const prisma = new PrismaClient({ adapter });


Exemplos:
SELECT * FROM user WHERE nome = 'Carlos':

await [Link]({ where: { nome: 'Carlos' }});

SELECT nome,idade FROM user WHERE nome = 'Carlos':

await [Link]({
where: { nome: 'Carlos' },
select: { nome: true, idade: true }
});
Encontrando registros

Exemplos de consultas com o objeto [Link]
SELECT * FROM user WHERE idade < 30:

await [Link]({ where: { idade: { lt: 30 }}});

SELECT * FROM user WHERE idade >= 21:

await [Link]({ where: { idade: { gte: 21 }}});

SELECT * FROM user WHERE nome IN ('Carlos', 'Maria'):


await [Link]({
where: { nome: { in: ['Carlos', 'Maria'] }}
});

SELECT * FROM user WHERE nome NOT IN ('Carlos', 'Maria'):


await [Link]({
where: { nome: { notIn: ['Carlos', 'Maria'] }}
});
Encontrando registros

Selecionando usuários cujo nome possuem a string Carlos
SELECT * FROM user WHERE nome like '%Carlos%':
await [Link]({
where: { nome: { contains: 'Carlos' }}
});


Selecionando usuários cujo nome começam com Carlos
SELECT * FROM user WHERE nome like 'Carlos%':
await [Link]({
where: { nome: { startsWith: 'Carlos' }}
});


Selecionando usuários cujo nome terminam com Oliveira
SELECT * FROM user WHERE nome like '%Oliveira':
await [Link]({
where: { nome: { endsWith: 'Oliveira' }}
});
Paginação

As cláusulas skip e take podem ser usadas em conjunto para
contruir um sistema de paginação

Selecionando os usuários 1-10 com idade maior que 20
SELECT * FROM user WHERE idade > 20 LIMIT 10 OFFSET 0;
await [Link]({
where: { idade: { gt: 20 }},
skip: 0, take: 10
});


Selecionando os usuários 11-20 com idade maior que 20
SELECT * FROM user WHERE idade > 20 LIMIT 10 OFFSET 10;
await [Link]({
where: { idade: { gt: 20 }},
skip: 10 , take: 10
});
Ordenando Registros

Os resultados recuperados podem ser ordenados de forma
crescente (ASC) ou descrescente (DESC) por qualquer atributo

Usuários >=18 ordenados pelo nome em ordem crescente
SELECT * FROM user WHERE idade >= 18 ORDER BY nome ASC;
await [Link]({
where: { idade: { gte: 18 }}, orderBy: { nome, 'asc' }
});


Usuários >=18 ordenados pelo nome em ordem decrescente
SELECT * FROM user WHERE idade >= 18 ORDER BY nome DESC;
await [Link]({
where: { idade: { gte: 18 }}, orderBy: { nome, 'desc' }
});
Selecionando apenas um registro

O comando findMany() retorna um array com todos os
registros que atenderem os critérios da cláusula where

Note que, se apenas um registro atender aos critérios da cláusula
where, será retornado um array com uma única posição

Uma alternativa é o uso de findFirst(), que ao invés de
recuperar um array, retorna apenas um objeto
SELECT * FROM user WHERE idade >= 18;

await [Link]({ where: { idade: { lte: 18 }}})


Criando novos registros

Criação de novos registros no banco de dados:
await [Link](data: { initialValues } );


Exemplo:
INSERT INTO user (nome, idade) VALUES ('Carlos', 26);

await [Link]({ data: {nome:'Carlos', idade: 16}})


Atualizando registros existentes

Atualização de registros já existentes no banco de dados:
await [Link]({values},{criteria});


Exemplo:
UPDATE user SET idade=30 WHERE nome = 'Carlos';

await [Link](
{ idade: 30 },
{ where: { nome: 'Carlos' }}
);
Apagando registros existentes

Apagando registros no banco de dados:
await [Link](criteria);


Exemplos:
DELETE FROM user WHERE nome = 'Carlos';

await [Link]({
where: { nome: 'Carlos' }
});

DELETE FROM user WHERE id IN (3, 97);

await [Link]({
where: { id: { in: [3, 97] } }
Arquitetura da Aplicação
2
A requisição é roteada
1 Requisição Roteador para um controlador
HTTP
O controlador pede
dados para os serviços
3
Controlador Serviço

O controlador retorna 4
um JSON payload
para o client
5
O serviço faz consultas
ao banco usando os Modelo
modelos, além de
processar/formatar
os dados recebidos,
antes de enviá-los ao
controlador
Camada de Roteamento

Responsável por mapear as rotas HTTP (GET, POST, PUT,
DELETE) para os respectivos controladores
// Arquivo src/resources/product/[Link]
import { Router } from 'express';
import productController from './[Link]';
const router = Router();
// Product controller
[Link]('/', [Link]);
[Link]('/', [Link]);
[Link]('/:id', [Link]);
[Link]('/:id', [Link]);
[Link]('/:id', [Link]);
export default router;
Camada de Serviços

Os serviços têm como função orquestrar as regras de negócio
e servir de intermediários entre controladores e modelos

// Arquivo src/resources/product/[Link]
import { PrismaClient, Product } from '@prisma/client';
import { CreateProductDto } from './[Link]';
const prisma = new PrismaClient();

export async function getAllProducts(): Promise<Product[]> {


return await [Link]();
}

export async function createProduct(


product: CreateProductDto
): Promise<Product> {
return await [Link]({ data: product });
}
Camada de Serviços

Os serviços têm como função orquestrar as regras de negócio
e servir de intermediários entre controladores e modelos

// Arquivo src/resources/product/[Link]
import { PrismaClient, Product } from '@prisma/client';
import { CreateProductDto } from './[Link]';
Além das funções mostradas, o serviço de products precisa ter funções como:
const prisma = new PrismaClient();
const productAlreadyExists = async (name: string): Promise<boolean>
c
export
const async function
getProduct = async (id:getAllProducts(): Promise<Product[]> {
string): Promise<Product>
return await [Link]();
const
}
updateProduct = async (id: string, product: ProductCreateDto):
Promise<[affectedCount: number]>
const removeProduct
export = async
async function (id: string): Promise<number>
createProduct(
product: CreateProductDto
): Promise<Product> {
return await [Link]({ data: product });
}
Camada de Serviços

Os serviços têm como função orquestrar as regras de negócio
e servir de intermediários entre controladores e modelos

// Arquivo src/resources/product/[Link]
import { PrismaClient, Product } from '@prisma/client';
import { CreateProductDto } from './[Link]';
Além das funções mostradas, o serviço de products precisa ter funções como:
const prisma = new PrismaClient();
const
UmaproductAlreadyExists = async (name:
vantagem do uso de serviços string):
é que suas Promise<boolean>
funções podem ser
c
utilizadas
export
const em outras
async
getProduct partes
function
= async da aplicação,
(id:getAllProducts():
string): diminuindo a réplica de
Promise<Product[]>
Promise<Product> {
códigos
returnemawait
vá[Link]();
arquivos.
const
}
updateProduct = async (id: string, product: ProductCreateDto):
Promise<[affectedCount: number]>
const removeProduct
export = async
async function (id: string): Promise<number>
createProduct(
product: CreateProductDto
): Promise<Product> {
return await [Link]({ data: product });
}
Camada de Serviços

Os serviços têm como função orquestrar as regras de negócio
e servir de intermediários entre controladores e modelos

// Arquivo src/resources/product/[Link]
import { PrismaClient, Product } from '@prisma/client';
import { CreateProductDto } from './[Link]';
Além das funções mostradas, o serviço de products precisa ter funções como:
const prisma = new PrismaClient();
const
UmaproductAlreadyExists = async (name:
vantagem do uso de serviços string):
é que suas Promise<boolean>
funções podem ser
c
Outra
export
const vantagem
utilizadas em outras
async
getProduct dos
function
= async serviços
partes da é que, caso
aplicação,
(id:getAllProducts():
string): se queira
diminuindo amudar
réplicao de
ORM {
Promise<Product[]>
Promise<Product>
da aplicação,
códigos
returnemawait o esforço
vários será muito menor. Isso porque eles serão
arquivos.
[Link]();
const updateProduct = async (id: string, product: ProductCreateDto):
} os únicos arquivos que usam os recursos do ORM para recuperar,
Promise<[affectedCount:
atualizar e criar dados. number]>
const removeProduct
export = async
async function (id: string): Promise<number>
createProduct(
product: CreateProductDto
): Promise<Product> {
return await [Link]({ data: product });
}
Data Transfer Objects (DTO)

Os arquivos resources/**/*.[Link] possuem as interfaces e
types, em especial os DTOs, usados dentro do resource

DTO é uma interface ou type usado para representar os objetos
de dados que são trocados entre a API e as aplicações client

Por exemplo, para criar um novo produto, a aplicação cliente precisa
enviar para a API os dados desse novo produto, sendo o formato
desses dados é definido através de um DTO
Data Transfer Objects (DTO)

Os DTOs geralmente contêm um subconjunto dos atributos de
um dado modelo, e para gerá-los podemos usar o comando Pick
// Arquivo src/resources/product/[Link]
import { Product } from '@prisma/client';
type ProdCreateDto= Pick<Product,'name'|'price'|'stockQuantity'>;
type ProdUpdateDto= Pick<Product,'name'|'price'|'stockQuantity'>;

export default { ProdCreateDto, ProdUpdateDto}

Cria um novo type


contendo apenas
as propriedade
nome, preço e
estoque do modelo
Produto
Data Transfer Objects (DTO)

Os DTOs são usados principalmente na camada de serviço, mas
também podem ser utilizados nos controladores
// Arquivo src/resources/product/[Link]
import { PrismaClient, Product } from '@prisma/client';
import { CreateProdDto } from './[Link]';
const prisma = new PrismaClient();

export async function createProduct(


product: CreateProdDto
): Promise<Product> {
return await [Link]({ data: product });
}
Camada do Controlador

Os controladores são responsáveis por recepcionar e
responder as respostas dos usuários
// Arquivo src/resources/product/[Link]
import { Request, Response } from 'express';
import { createProduct, alreadyExists } from './[Link]';
async function create(req: Request, res: Response) {
const product = [Link];
try {
if (await alreadyExists([Link])) {
return [Link](400).json({ msg: 'Produto já existe' });
}
const newProduct = await createProduct(product);
[Link](201).json(newProduct);
} catch (err) {
[Link](500).json(err);
}
}
Camada do Controlador

Os controladores são responsáveis por recepcionar e
responder as respostas dos usuários
// Arquivo src/resources/product/[Link]

É importante notar que


import { Request, erros na} inserção
Response de um registro
from 'express';
import { createProduct, alreadyExists } from './[Link]';
disparam uma exceção no bloco try do Controlador, ocasionando
umasync
status 500 paracreate(req:
function a requisiçã[Link], res: Response) {
const product = [Link];
try {
if (await alreadyExists([Link])) {
return [Link](400).json({ msg: 'Produto já existe' });
}
const newProduct = await createProduct(product);
[Link](201).json(newProduct);
} catch (err) {
[Link](500).json(err);
}
}
Camada do Controlador

Para o cliente receber uma mensagem mais completa sobre o
erro ocorrido, podemos usar o seguinte código dentro do catch
if (err instanceof [Link]) {
[Link](400).json({
error: "Validation Error",
message: "The data provided is invalid. ",
});
} else if (err instanceof [Link]) {
[Link](400).json({
error: "Database Error",
message: [Link],
});
} else {
[Link](500).json({
error: "Internal Server Error",
message: "Something went wrong. Please try again later.",
});
}
Camada do Controlador

Pacotes como o http-status-codes podem ser úteis para
construir as respostas às aplicações
import { ReasonPhrases, StatusCodes } from "http-status-codes";
...
const create = async (req: Request, res: Response) => {
const newProduct = [Link] as CreateProductDto;
try {
if (!(await productAlreadyCreated([Link]))) {
const product = await createProduct(newProduct);
[Link]([Link]).json(product);
} else {
[Link]([Link]).send([Link]);
}
} catch (err) {
[Link](StatusCodes.INTERNAL_SERVER_ERROR).json(err);
}
};
HTTP Client Bruno

Existem várias ferramentas para testar os endpoints de uma
Api, como o Insomnia, o Postman e o Bruno
HTTP Client Bruno

Existem várias ferramentas para testar os endpoints de uma
Api, como o Insomnia, o Postman e o Bruno

Neste curso optamos por usar o Bruno, dado sua facilidade


e recursos
HTTP Client Bruno

Para começar a usar o Bruno, crie uma coleção nova para a API
sendo desenvolvida
HTTP Client Bruno

Para começar a usar o Bruno, crie uma coleção nova para a API
sendo desenvolvida

O Bruno permite a
criação de scripts
internos para
geração de dados
aleatórios. Para
detalhes, veja a
documentação
HTTP Client Bruno

Para começar a usar o Bruno, crie uma coleção nova para a API
sendo desenvolvida

O Bruno permite a
criação de scripts
internos para
geração de dados
aleatórios. Para
detalhes, veja a
documentação
HTTP Client Bruno

Para começar a usar o Bruno, crie uma coleção nova para a API
sendo desenvolvida

Exercício: Crie uma API REST usando o framework Express


contendo os endpoints index, create, read, update e delete
O Bruno permite a
para
criação deoscripts
resource product. Use o Insomnia para testar a API.
internos para
geração de dados github
aleatórios. Para
detalhes, veja a ExpAPI
documentação
Validação de Requests

Em APIs, é importate implementar alguma forma de proteção
contra dados inválidos

O Joi é uma biblioteca para validação de dados, usada para
garantir a integridade das solicitações em APIs

Com o Joi, podemos definir que campos são obrigatórios, que tipos
de dados são aceitáveis e quais regras os valores devem cumprir

Use o comando abaixo para adicionar o Joi na aplicação
$ npm install joi
Validação de Requests

Em APIs, é importate implementar alguma forma de proteção
contra dados inválidos

O Joi é uma biblioteca para validação de dados, usada para
garantir a integridade
Note que neste cursodas solicitações
o Joi será usadoem APIs
para validação das
requests no lado servidor, mas na maioria dos casos é

Com o Joi, podemos definir que campos são obrigatórios, que tipos
recomendado que a validação também seja feita do lado cliente
de dados são aceitáveis e quais regras os valores devem cumprir

Use o comando abaixo para adicionar o Joi na aplicação
$ npm install joi
Validação de Requests

Após isso, já podemos usar o joi para validar dados: Esquema
de
import Joi from 'joi'; validação
const productSchema = [Link]().keys({
name: [Link]().min(3).max(50).required(),
price: [Link]().min(0).required(),
stockQuantity: [Link]().min(0).integer().required()
});
const celularMoto = { Usamos
name: 'Smartphone Motorola Edge 30', o esquema
price: 1499.00, para validar
stockQuantity: 3 o dado
};
const result = [Link](celularMoto);
// [Link] == null significa válido
Validação de Requests

O Joi oferece uma variedade de validadores que podem ser
usados para definir esquemas de validação

string(), number(), boolean(), object(), array(), date(), required(),
min(), max(), email(), uri(), etc

Também oferece regras de validação mais complexas

pattern(): verifica se o valor corresponde a um padrão

forbidden(): especifica valores que não permitidos

valid(): define uma lista de valores válidos

custom(): permite a definição de funções de validação
personalizadas
Validação de Requests

Para validar as requests do resource produtos, vamos criar um
arquivo resources/produto/[Link]
// Arquivo src/resources/product/[Link]
import Joi from 'joi';
const schema = [Link]({
name: [Link]().min(3).max(100).required(),
price: [Link]().required(),
stockQuantity: [Link]().integer().required()
});
export default schema;
Validação de Requests

Adicionalmente, vamos criar um middleware [Link] para
validar os dados das requests (diretório src/middlewares)
// Arquivo src/middlewares/[Link]
import { Request, Response, NextFunction } from 'express';
import { Schema } from 'joi';
const validate = (schema: Schema) => {
return (req: Request, res: Response, next: NextFunction) => {
const { error } = [Link]([Link], {
abortEarly: false
});
if (error) [Link](422).json({ error: [Link] });
else next();
};
};
export default validate;
Validação de Requests

Adicionalmente, vamos criar um middleware [Link] para
validar os dados das requests (diretório src/middlewares)
// Arquivo src/middlewares/[Link]
Retorna todos
import { Request, Response, NextFunction } from 'express';
os erros
import { Schema } from 'joi';
encontrados,
const validate = (schema: Schema)e =>
não{apenas o
primeiro
return (req: Request, res: Response, next: NextFunction) => {
const { error } = [Link]([Link], {
abortEarly: false
});
if (error) [Link](422).json({ error: [Link] });
else next();
};
};
export default validate;
Validação de Requests

A partir desse momento, podemos usar o middleware de
validação no roteador do resource produtos
// Arquivo src/resources/product/[Link]
import { Router } from 'express';
import productController from './[Link]';
import schema from './[Link]';
import validate from '../../middlewares/validate';
const router = Router();
[Link]('/', [Link]);
[Link]('/', validate(schema), [Link]);
[Link]('/:id', [Link]);
[Link]('/:id', validate(schema), [Link]);
[Link]('/:id', [Link]);
export default router;
Validação de Requests

A partir desse momento, podemos usar o middleware de
validação no roteador do resource produtos
// Arquivo src/resources/product/[Link]
import { Router } from 'express';
import productController from './[Link]';
import schema from './[Link]';
import validate from '../../middlewares/validate';
const router = Router();
[Link]('/', [Link]);
[Link]('/', validate(schema), [Link]);
[Link]('/:id', [Link]);
[Link]('/:id', validate(schema), [Link]);
[Link]('/:id', [Link]);
export default router;
Validação de Requests

A partir desse momento, podemos usar o middleware de
validação no roteador do resource produtos
// Arquivo src/resources/product/[Link]
import { Router } from 'express';
import productController from './[Link]';
import schema from './[Link]';
import validate from '../../middlewares/validate';
const router = Router();

Exercício: Usando
[Link]('/', Joi, implemente um mecanismo de
[Link]);
validação dos endpoints
[Link]('/', do [Link]);
validate(schema), de produto.
[Link]('/:id', [Link]); github
[Link]('/:id', validate(schema), [Link]);
[Link]('/:id', [Link]); ExpAPI
export default router;
Cookies e Sessões

HTTP é um protocolo que não mantém estado, isto é não
mantém uma conexão

Cada requisição que um cliente (browser, insomnia, etc) faz à
API é independente das requisições anteriores

No entanto, muitas aplicações necessitam manter o estado do
usuário durante as requests feitas à API

Ex: carrinho de compras em sites de comércio eletrônico

As aplicações possuem duas opções para manter as
informações de estado dos clientes:

Cookies: mantém informações de estado no cliente (browser)

Sessões: mantém informações de estado no lado servidor
Cookies

Cookies são dados/variáveis enviados pelo servidor Web para
o cliente através do protocolo HTTP

Ficam armazenados no lado cliente

São enviados para o servidor em futuros acessos do cliente

Requisições do Cliente Respostas do Servidor

1 Get /auth HTTP1.1

HTTP1.1 200 OK
2 Set-Cookie: lang=pt-BR
Dados requisitados

Get /products - HTTP1.1


3
Cookie: lang=pt-BR

HTTP1.1 200 OK
4
Dados requisitados
Respostas do Cliente Respostas do Servidor

1 Get /auth HTTP1.1

HTTP1.1 200 OK
2 Set-Cookie: lang=pt-BR
Dados requisitados

Get /products HTTP1.1


3
Cookie: lang=pt-BR

HTTP1.1 200 OK
4
Dados requisitados

Get /language/change HTTP1.1


5
Cookie: lang=pt-BR

HTTP1.1 200 OK
6 Set-Cookie: lang=en-US
4
Dados requisitados

Get /user HTTP1.1


7
Cookie: lang=en-US

HTTP1.1 200 OK
8
Dados requisitados
Cookies

Para habilitar o uso de cookies por sua aplicação, é necessário
instalar o middleware cookie-parser
$ npm install cookie-parser
$ npm install -D @types/cookie-parser


Para usar o middleware, precisamos dar um import no módulo
e adicioná-lo em nossa aplicação com o método use
// Arquivo src/[Link]
import cookieParser from 'cookie-parser';
[Link](cookieParser());
Cookies

A partir deste momento, podemos i) criar novos cookies e ii)
identificar os cookies enviados pelo browser para o servidor

O array [Link] armazena os cookies enviados pelo cliente

O método [Link] é usado para enviar um pedido de criação de
um novo cookie no lado cliente (browser, insomnia, etc)
// Arquivo src/middlewares/setLangCookie
const setLangCookie = (req, res, next) => {
if (!('lang' in [Link])) [Link]('lang', 'pt-BR');
next();
};
export default setLangCookie
Cookies

Após criar o middleware, podemos adicioná-lo em src/[Link]
// Arquivo src/[Link]
...
import cookieParser from 'cookie-parser';
import { setLangCookie } from './middlewares/setLangCookie';
...
[Link](cookieParser());
[Link](setLangCookie);


Com isso, o cookie lang será criado no primeiro acesso do
usuário, com o valor default pt-BR
Cookies

Após criar o middleware, podemos adicioná-lo em src/[Link]
// Arquivo src/[Link] Clique para
... ver os
cookies
import cookieParser from 'cookie-parser';
import { setLangCookie } from './middlewares/setLangCookie';
...
[Link](cookieParser());
[Link](setLangCookie);


Com isso, o cookie lang será criado no primeiro acesso do
usuário, com o valor default pt-BR
Cookies

Após criar o middleware, podemos adicioná-lo em src/[Link]
// Arquivo src/[Link]
...
import cookieParser from 'cookie-parser';
import { setLangCookie } from './middlewares/setLangCookie';
...
[Link](cookieParser());
[Link](setLangCookie);


Com isso, o cookie lang será criado no primeiro acesso do
usuário, com o valor default pt-BR
Cookies

Podemos criar um resource para gerenciamento das
linguagens, contendo um endpoint para mudar a linguagem
// Arquivo src/resources/language/[Link]
import { Request, Response } from 'express';
function changeLanguage(req: Request, res: Response) {
const { lang } = [Link];
[Link]('lang', lang);
[Link]({ lang });
}
export default { changeLanguage };

// Arquivo src/resources/language/[Link]
import { Router } from 'express';
import languageController from './[Link]';
const router = Router();
[Link]('/change', [Link]);
export default router;
Cookies

Podemos criar um resource para gerenciamento das
linguagens, contendo um endpoint para mudar a linguagem
// Arquivo src/resources/languages/[Link]
import { Request, Response } from 'express';
function changeLanguage(req: Request, res: Response) {
const { lang } = [Link]; Requisição de
[Link]('lang', lang); mudança da
[Link]({ lang }); linguagem
}
export default { changeLanguage };

// Arquivo src/resources/languages/[Link]
import { Router } from 'express';
import languageController from './[Link]';
const router = Router();
[Link]('/change', [Link]);
export default router;
Cookies

Podemos criar um resource para gerenciamento das
linguagens, contendo um endpoint para mudar a linguagem
// Arquivo src/resources/languages/[Link]
import { Request, Response } from 'express';
function changeLanguage(req: Request, res: Response) {
const { lang } = [Link]; Requisição de
[Link]('lang', lang); mudança da
[Link]({ lang }); linguagem
}
export default { changeLanguage };

// Arquivo src/resources/languages/[Link]
import { Router } from 'express';
import languageController from './[Link]';
const router = Router();
[Link]('/change', [Link]);
export default router;
Cookies

Podemos criar um resource para gerenciamento das
linguagens, contendo um endpoint para mudar a linguagem
// Arquivo src/resources/languages/[Link]
import { Request, Response } from 'express';
function changeLanguage(req: Request, res: Response) {
const { lang } = [Link]; Requisição de
[Link]('lang', lang); mudança da
[Link]({ lang }); linguagem
}
export default { changeLanguage };
Exercício: Implementar o mecanismo de troca de linguagem
utilizando cookies.
// Arquivo src/resources/languages/[Link] github
import { Router } from 'express';
ExpAPI
import languageController from './[Link]';
const router = Router();
[Link]('/change', [Link]);
export default router;
Cookies

Também podemos criar cookies com data de expiração
// Expira 360000 ms (6 minutos) após ser criado
[Link](name, 'value', { maxAge: 360000 } );


Se o cookie for criado sem data de expiração, ele será apagado
após o fechamento da janela do browser

Usamos a função clearCookie para apagar um cookie já criado
[Link]('lang');
Sessões

Através de sessões, podemos armazenar informações de
estado (variáveis) no lado servidor

Em vez do browser guardar um cookie por dado, ele guarda
apenas um cookie contendo um id de sessão ([Link])
Requisições do Cliente Respostas do Servidor

Sessão 6Bh
1 Get / HTTP1.1 Dados da sessão
no servidor
HTTP1.1 200 OK user: Bruna
2 Set-Cookie: [Link]=6Bh itens-carrinho: 3
Conteúdo da requisição start: 15:30

Get /produto HTTP1.1


3
Cookie: [Link]=6Bh

HTTP1.1 200 OK
4
Conteúdo da requisição
Sessões

Para usarmos as sessões, precisamos instalar um módulo para
geração de valores únicos para os IDs das sessões

Uma opção é o módulo uuid – Universally Unique Identifier –
que é uma implementação do UUID descrito na RFC 4122
$ npm install uuid
$ npm install -D @types/uuid


Os UUIDs são valores de 128 bits que podem ser usados como
ID únicos de qualquer coisa em sistemas computacionais

Ex: f0221c72-ac30-4796-83f5-fd7a8a4f6b15

Embora a probabilidade de um UUID ser duplicado não seja
nula, ela é próximo o suficiente de zero e pode ser ignorada
Sessões

Para usarmos as sessões, precisamos instalar um módulo para
geração de valores únicos para os IDs das sessões

Uma opção é o módulo uuid – Universally Unique Identifier –
que é uma implementação do UUID descrito na RFC 4122
$ npm install uuid
$ npm install -D @types/uuid


Os UUIDs dito
Conforme são anteriormente,
valores de 128embitsnosso
que sistema
podem usamos
ser usados como
UUIDs
ID únicos
como de primárias
chaves qualquer de
coisa em sistemas
tabelas, computacionais
ao invés de IDs auto
incrementados

Ex: f0221c72-ac30-4796-83f5-fd7a8a4f6b15

Embora a probabilidade de um UUID ser duplicado não seja
nula, ela é próximo o suficiente de zero e pode ser ignorada
Sessões

Para habilitar o uso de sessões em sua aplicação, é necessário
instalar o middleware express-session
$ npm install express-session
$ npm install -D @types/express-session

Para usar o middleware, precisamos importar o módulo e
adicioná-lo em nossa aplicação com o método use
// Arquivo [Link]
import session from 'express-session';
import { v4 as uuidv4 } from 'uuid';
...
[Link](session({
genid: (req) => uuidv4(),
secret: 'Hi9Cf#mK98',
resave: true,
saveUninitialized: true
}));
Sessões

Para habilitar o uso de sessões em sua aplicação, é necessário
instalar o middleware express-session
$ npm install express-session
$ npm install -D @types/express-session
Os UUIDs são

Para usar o middleware, precisamos importar o módulo e
usados para
gerar o
adicioná-lo em nossa aplicaçãoidcom o método use
de sessão
// Arquivo [Link]
import session from 'express-session';
import { v4 as uuidv4 } from 'uuid';
...
[Link](session({
genid: (req) => uuidv4(),
secret: 'Hi9Cf#mK98',
resave: true,
saveUninitialized: true
}));
Sessões

Para habilitar o uso de sessões em sua aplicação, é necessário
instalar o middleware express-session
$ npm install express-session
$ npm install -D @types/express-session
Usado para adicionar
Os UUIDs são

Para usar o middleware, precisamos
uma assinaturaimportar
usados para(similar aoo módulo e
adicioná-lo em nossa aplicação com
checksum)
gerar oidmétodo
de sessãouse
ao SESSID
os
enviado para o usuário.
// Arquivo [Link] Quando o usuário devolve o
[Link], a assinatura é
import session from 'express-session';
usada
import { v4 as uuidv4 } from para checar se o
'uuid';
... [Link] é válido. Usa uma
[Link](session({ técnica chamada HMAC.
genid: (req) => uuidv4(),
secret: 'Hi9Cf#mK98',
resave: true,
saveUninitialized: true
}));
Sessões

Para habilitar o uso de sessões em sua aplicação, é necessário
instalar o middleware express-session
$ npm install express-session
$ npm install -D @types/express-session
Usado para adicionar
Os UUIDs são

Para usar o middleware, precisamos
Quando
umatrue, importar
a sessão
assinatura
usados do aoo módulo e
(similar
para
usuário
adicioná-lo em nossa aplicaçãoégerar
salva
com
checksum) a SESSID
cada
o
osao método use
[Link]
requisição, mesmo
enviado paraque os
o usuário.
// Arquivo [Link]
dadosQuando
da sessão não tenham
o usuário devolve o
sido modificados
[Link], adurante
import session from 'express-session';
a é
assinatura
import { v4 as uuidv4requisição.
usada
} from Isso mantém
para
'uuid'; checarase o
... sessão ativa, visto
[Link] que ela
é válido. Usa uma
[Link](session({ podetécnica
ser deletada
chamadaapósHMAC.
algum tempo de desuso.
genid: (req) => uuidv4(),
secret: 'Hi9Cf#mK98',
resave: true,
saveUninitialized: true
}));
Sessões

Para habilitar o uso de sessões em sua aplicação, é necessário
instalar o middleware express-session
$ npm install express-session
$ npm install -D @types/express-session
Usado para adicionar
Os UUIDs são

Para usar o middleware, precisamos
Quando
umatrue, importar
a sessão
assinatura
usados do aoo módulo e
(similar
para
usuário
adicioná-lo em nossa aplicaçãoégerar
salva
com
checksum) a SESSID
cada
o
osao método use
[Link]
requisição, mesmo
enviado paraque os
o usuário.
Quando
dadosQuando
da sessãotrue, força
não tenham
o usuário que
devolve o
// Arquivo [Link] as sessões não inicializadas
sido modificados
[Link], adurante
import session from 'express-session';
a é
assinatura
sejam salvas
requisição.
usada Isso no store.
mantém
para Uma
checarase o
import { v4 as uuidv4 } from
sessão não'uuid';
inicializada ocorre
... sessão ativa,
[Link] visto
é que
válido. ela
Usa uma
quando
podetécnica a sessão
ser deletada
chamadaé nova e
apósHMAC.
[Link](session({ ainda não foi modificada.
algum tempo de desuso.
genid: (req) => uuidv4(),
secret: 'Hi9Cf#mK98',
resave: true,
saveUninitialized: true
}));
Sessões

Para habilitar o uso de sessões em sua aplicação, é necessário
instalar o middleware express-session
$ npm install express-session
$ npm install -D @types/express-session
Usado para adicionar
Os UUIDs são

Para usar o middleware, precisamos
Quando
umatrue, importar
a sessão
assinatura
usados do aoo módulo e
(similar
para
usuário
adicioná-lo em nossa aplicaçãoégerar
salva
com
checksum) a SESSID
cada
o
osao método use
[Link]
requisição, mesmo
enviado paraque os
o usuário.
Quando
dadosQuando
da sessãotrue, força
não tenham
o usuário que
devolve o
// Arquivo [Link] as sessões não inicializadas
sido modificados
[Link], adurante
import session from 'express-session';
a é
assinatura
sejam salvas
requisição.
usada Isso no store.
mantém
para Uma
checarase o
import { v4 as uuidv4 } from
sessão não'uuid';
inicializada ocorre
... sessão ativa,
[Link] visto
é que
válido. ela
Usa uma
quando
podetécnica a sessão
ser deletada
chamadaé nova e
apósHMAC.
[Link](session({ ainda não foi modificada.
algum tempo de desuso.
genid: (req) => uuidv4(),
secret: 'Hi9Cf#mK98',
resave: true,
saveUninitialized: true
}));
Cadastro de Usuários

O cadastro de usuário envolve dois modelos Prisma, que
precisam ser incluídos no arquivo prisma/[Link]
model User {
id String @id @default(uuid()) @[Link](36)
name String @[Link](100)
email String @unique @[Link](100)
password String @[Link](60)
userTypeId String @[Link](36)
userType UserType @relation(fields: [userTypeId], references: [id])
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}

model UserType {
id String @id @default(uuid()) @[Link](36)
label String @[Link](10)
User User[]
}
Cadastro de Usuários

O cadastro de usuário envolve dois modelos Prisma, que
precisam ser incluídos no arquivo prisma/[Link]
model User {
id String @id @default(uuid()) @[Link](36)
name String @[Link](100)
email String @unique @[Link](100)
password String @[Link](60)
userTypeId String @[Link](36)
userType UserType @relation(fields:Os[userTypeId], references: [id])
campos de relação
createdAt DateTime @default(now()) definem conexões entre
updatedAt DateTime @updatedAt modelos no nível Prisma
} e não existem no banco
de dados. Esses campos
model UserType { são usados para gerar o
id String @id @default(uuid()) @[Link](36)
Prisma Client.
label String @[Link](10)
User User[]
}
Cadastro de Usuários

O cadastro de usuário envolve dois modelos Prisma, que
precisam ser incluídos no arquivo prisma/[Link]
model User {
id String @id @default(uuid()) @[Link](36)
name String @[Link](100)
email String @unique @[Link](100)
password String @[Link](60)
userTypeId String @[Link](36)
userType UserType @relation(fields:Os[userTypeId], references: [id])
campos de relação
createdAt DateTime @default(now()) definem conexões entre
updatedAt DateTime @updatedAt modelos no nível Prisma
} e não existem no banco
de dados. Esses campos
model UserType { são usados para gerar o
id String @id @default(uuid()) @[Link](36)
Prisma Client.
label String @[Link](10)
User User[]
}
Tipos de Usuários

Em nossa app, teremos os tipos de usuários client e admin

Podemos criar um arquivo [Link] para guardar
os IDs que serão usados nesses dois tipos
// File src/resources/userType/[Link]
export enum UserTypes {
"ADMIN" = "d90171c9-a589-4883-a0bb-027a32e0be23",
"CLIENT" = "e3fd4fcd-3cd7-45ef-b49d-9237203e8924",
}
Tipos de Usuários

Em nossa app, teremos os tipos de usuários client e admin

Podemos criar um arquivo [Link] para guardar
os IDs que serão usados nesses dois tipos
// File src/resources/userType/[Link]
export enum UserTypes {
"ADMIN" = "7edd25c6-c89e-4c06-ae50-c3c32d71b8ad",
O Visual Studio
"CLIENT" possui extenções com geradores de UUIDs,
= "6a4cda94-fbb6-476b-be29-f4124cae9058",
} que podem facilitar a definição dos IDs dos tipos de usuários
Tipos de Usuários

Além do arquivo de constantes, podemos criar uma seed
prisma/[Link] para adicionar os tipos de usuários no banco
async function main() {
return await [Link]({
data: [
{ id: [Link], label: "admin" },
{ id: [Link], label: "client" },
],
skipDuplicates: true,
});
}
main()
.then(async () => {
await prisma.$disconnect();
})
.catch(async (e) => {
[Link](e);
await prisma.$disconnect();
});
Tipos de Usuários

Além do arquivo de constantes, podemos criar uma seed
prisma/[Link] para adicionar os tipos de usuários no banco
async function main() {
return await [Link]({
data: [
Para rodar{ oid:
arquivo de seeds, é necessário
[Link], incluir },
label: "admin" o seguinte
{ id: [Link], label:
trecho de código no arquivo [Link]: "client" },
],
"prisma": {
skipDuplicates: true,
"seed":
}); "ts-node prisma/[Link]"
} }
main()
.then(async () => {
await prisma.$disconnect();
})
.catch(async (e) => {
[Link](e);
await prisma.$disconnect();
});
Tipos de Usuários

Além do arquivo de constantes, podemos criar uma seed
prisma/[Link] para adicionar os tipos de usuários no banco
async function main() {
return await [Link]({
data: [
Para rodar{ oid:
arquivo de seeds, é necessário
[Link], incluir },
label: "admin" o seguinte
{ id: [Link], label:
trecho de código no arquivo [Link]: "client" },
],
"prisma": {
skipDuplicates: true,
"seed":
}); "ts-node prisma/[Link]"
} }
main()
.then(async () => {
await prisma.$disconnect();
})
.catch(async (e) => {
[Link](e);
await prisma.$disconnect();
});
Tipos de Usuários

Além do arquivo de constantes, podemos criar uma seed
prisma/[Link] para adicionar os tipos de usuários no banco
async function main() {
return await [Link]({
data: [
Para rodar{ oid:
arquivo de seeds, é necessário
[Link], incluir },
label: "admin" o seguinte
{ id: [Link], label:
trecho de código no arquivo [Link]: "client" },
],
"prisma": {
skipDuplicates: true,
"seed":
}); "ts-node prisma/[Link]"
} }
main()
.then(async () => {
await prisma.$disconnect();
})
.catch(async (e) => {
[Link](e);
await prisma.$disconnect();
});
Criptografando as senhas

Para criar um novo usuário, é necessário criptografar a sua
senha antes de salvá-la no banco de dados

Mas por quê? Por que não guardar a senha crua?

Todas as pessoas com acesso ao banco poderiam ver a senha

Os usuários frequentemente usam a mesma senha em vários sites

A senha iria aparecer nos backups do banco

Se o banco estiver na cloud, as senhas ficariam expostas na web

As senhas ficariam expostas a ataques de SQL-injection
Ataques de SQL-Injection

Caso os desenvolvedores não tomem os devidos cuidados, os
formulários podem ficar vulneráveis a ataques de SQL-injection
Tabela Estado Tabela Usuario
estado capital login senha
Rio de Janeiro Rio de Janeiro alberto flamengo
Amazonas Manaus maria teste123
Minas Gerais Belo Horizionte fernanda RmJ&AnhK@
Ceará Fortaleza matheus pokemon
Busca da capital pelo estado
Informe o estado
Ataques de SQL-Injection

Caso os desenvolvedores não tomem os devidos cuidados, os
formulários podem ficar vulneráveis a ataques de SQL-injection
Tabela Estado Tabela Usuario
estado capital login senha
Rio de Janeiro Rio de Janeiro alberto flamengo
Amazonas Manaus maria teste123
Minas Gerais Belo Horizionte fernanda RmJ&AnhK@
Ceará Fortaleza matheus pokemon
Busca da capital pelo estado
Amazonas

estado capital
SELECT estado, capital FROM estado
WHERE estado = 'Amazonas' Amazonas Manaus
Ataques de SQL-Injection

Caso os desenvolvedores não tomem os devidos cuidados, os
formulários podem ficar vulneráveis a ataques de SQL-injection
Tabela Estado Tabela Usuario
estado capital login senha
Rio de Janeiro Rio de Janeiro alberto flamengo
Amazonas Manaus maria teste123
Minas Gerais Belo Horizionte fernanda RmJ&AnhK@
Ceará Fortaleza matheus pokemon
Busca da capital pelo estado
' UNION SELECT login, senha FROM Usuario WHERE login != '

SELECT estado, capital FROM estado login senha


WHERE estado = '' UNION SELECT login, alberto flamengo
senha FROM Usuario WHERE login != '' maria teste123
fernanda RmJ&AnhK@
matheus pokemon
Ataques de SQL-Injection

Caso os desenvolvedores não tomem os devidos cuidados, os
formulários podem ficar vulneráveis a ataques de SQL-injection
Tabela Estado Tabela Usuario
estado capital login senha
Rio de Janeiro Rio de Janeiro alberto flamengo
Os ataques
Amazonas de SQL-Injection
Manaus só são possíveis se o invasor
maria teste123
conseguir incluir
Minas Gerais
caracteres como fernanda
Belo Horizionte
' (aspas simples),RmJ&AnhK@
'' (aspas
duplas) ou \b (caracter de backspace) nos inputs dos formulários.
Ceará Fortaleza matheus pokemon
Busca da capital pelo estado
' UNION SELECT login, senha FROM Usuario WHERE login != '

SELECT estado, capital FROM estado login senha


WHERE estado = '' UNION SELECT login, alberto flamengo
senha FROM Usuario WHERE login != '' maria teste123
fernanda RmJ&AnhK@
matheus pokemon
Ataques de SQL-Injection

Caso os desenvolvedores não tomem os devidos cuidados, os
formulários podem ficar vulneráveis a ataques de SQL-injection
Tabela Estado Tabela Usuario
estado capital login senha
Rio de Janeiro Rio de Janeiro alberto flamengo
Os ataquesresolve
O Prisma
Amazonas de SQL-Injection só são
este problema
Manaus possíveis seum
adicionando
maria o invasor
caracter
teste123
conseguir
de escapeincluir
Minas Gerais
a tais caracteres
[Link]
Belo Horizionte
' (aspas simples),
Por exemplo,
fernanda
'' (aspas
o caracter ' vira \',
RmJ&AnhK@
duplas),
'' vira \''\b (caracter
e \b deVide
vira \\b. backspace) nos inputs
SQL injection dos formulários.
no Prisma. Mesmo
Ceará Fortaleza matheus pokemon
assim, é sempre saudável fazer validação dos inputs.
Busca da capital pelo estado
' UNION SELECT login, senha FROM Usuario WHERE login != '

SELECT estado, capital FROM estado login senha


WHERE estado = '' UNION SELECT login, alberto flamengo
senha FROM Usuario WHERE login != '' maria teste123
fernanda RmJ&AnhK@
matheus pokemon
Funções HASH

Uma função Hash é um algoritmo que transforma um bloco de
dados qualquer em série de caracteres de comprimento fixo

Ex: md5, sha1, sha256, etc

São funções de uma mão-única, isto é, não é possível
recuperar o bloco de dados original a partir do hash gerado
Funções HASH

Uma função Hash é um algoritmo que transforma um bloco de
dados qualquer em série de caracteres de comprimento fixo

Ex: md5, sha1, sha256, etc

São funções de uma mão-única, isto é, não é possível
recuperar o bloco de dados original a partir do hash gerado
Funções HASH

Uma função Hash é um algoritmo que transforma um bloco de
dados qualquer em série de caracteres de comprimento fixo
― Os comandos md5sum e sha1sum são instalados por padrão em
Ex: md5, sha1, sha256, etc
sistemas UNIX , GNU/Linux e BSD. Eles são usados para verificar

São funções dedeuma
a integridade mão-única,
dados istoatravés
transmitidos é, nãodaé Web.
possível
Para
maiores informações,
recuperar vide Soma
o bloco de dados de Verificação
original a partir do(Checksum)
hash gerado
Funções HASH

Considere um sistema que usa a função hash MD5 para
criptografar as senhas de seus usuários

Ex: se a senha de um usuário é minhasenha, a string armazenada
no banco será 7c67e713a4b4139702de1a4fac672344

Quando o usuário tentar logar no sistema, o servidor irá comparar
o md5 da senha informada com a string armazenada no banco
Funções HASH

Considere um sistema que usa a função hash MD5 para
criptografar as senhas de seus usuários

Ex: se a senha de um usuário é minhasenha, a string armazenada
no banco será 7c67e713a4b4139702de1a4fac672344

Quando o usuário tentar logar no sistema, o servidor irá comparar
o md5 da senha informada com a string armazenada no banco
Note que ninguém poderá descobrir a senha do usuário caso
acesso a tabela Usuário do banco de dados
Funções HASH

Considere um sistema que usa a função hash MD5 para
criptografar as senhas de seus usuários

Ex: se a senha de um usuário é minhasenha, a string armazenada
no banco será 7c67e713a4b4139702de1a4fac672344

Quando o usuário tentar logar no sistema, o servidor irá comparar
o md5 da senha informada com a string armazenada no banco
Note que ninguém poderá descobrir a senha do usuário caso
Será mesmo?
acesso Então vamos
a tabela Usuário no Google
do banco e inserir a string hash
de dados
7c67e713a4b4139702de1a4fac672344 no campo de busca
Funções HASH

Considere um sistema que usa a função hash MD5 para
criptografar as senhas de seus usuários

Ex: se a senha de um usuário é minhasenha, a string armazenada
no banco será 7c67e713a4b4139702de1a4fac672344

Quando o usuário tentar logar no sistema, o servidor irá comparar
o md5 da senha informada com a string armazenada no banco
Note que ninguém poderá descobrir a senha do usuário caso
Será mesmo?
acesso Então vamos
a tabela Usuário no Google
do banco e inserir a string hash
de dados
7c67e713a4b4139702de1a4fac672344 no campo de busca
Funções HASH

Considere um sistema que usa a função hash MD5 para
criptografar as senhas de seus usuários

Ex: se a senha de um usuário é minhasenha, a string armazenada
no banco será 7c67e713a4b4139702de1a4fac672344

Quando o usuário tentar logar no sistema, o servidor irá comparar
o md5 da senha informada com a string armazenada no banco
Note que ninguém poderá descobrir a senha do usuário caso
Será mesmo?
acesso Então vamos
a tabela Usuário no Google
do banco e inserir a string hash
de dados
7c67e713a4b4139702de1a4fac672344 no campo de busca
Funções HASH

Considere um sistema que usa a função hash MD5 para
criptografar as senhas de seus usuários

Ex: se a senha de um usuário é minhasenha, a string armazenada
no banco será 7c67e713a4b4139702de1a4fac672344

Sistemaso como
Quando o Hash
usuário tentarToolkit usam
logar no tabelas
sistema, contendoirábilhares
o servidor comparar
de valores de hash pré-calculados. Essas tabelas normalmente
osão
md5 da senhade
chamadas informada
rainbow com a string armazenada no banco
tables.
Note que ninguém poderá descobrir a senha do usuário caso
Será mesmo?
acesso Então vamos
a tabela Usuário no Google
do banco e inserir a string hash
de dados
7c67e713a4b4139702de1a4fac672344 no campo de busca
Salt

O salt é uma estratégia utilizada para evitar que duas senhas
idênticas produzam hashes idênticos

A ideia é concaternar a senha original do usuário com uma
sequência adicional de caracteres aleatórios, chamada salt

Para exemplificar, podemos aplicar o salt Us8#upK12MjsM à
senha original do usuário, minhasenha
Salt

O salt é uma estratégia utilizada para evitar que duas senhas
idênticas produzam hashes idênticos

A ideia é concaternar a senha original do usuário com uma
sequência adicional de caracteres aleatórios, chamada salt

Para exemplificar, podemos aplicar o salt Us8#upK12MjsM à
senha original do usuário, minhasenha
Salt

O salt é uma estratégia utilizada para evitar que duas senhas
idênticas produzam hashes idênticos

A ideia é concaternar a senha original do usuário com uma
sequência
De uma formaadicional
geral, ode
saltcaracteres aleatórios, chamada
é gerado aleatoriamente salt
para cada
 usuário e armazenado
Para exemplificar, no bancoaplicar
podemos junto com o hash
o salt MD5 resultante:à
Us8#upK12MjsM
Us8#upK12MjsM
senha original do d3eeb71a83744a4bbaa3ce41be87a292
usuário, minhasenha
O módulo bcrypt

O módulo bcrypt é uma boa opção para geração de senhas, pois
incorpora uma função hash à uma estratégia de salt
$ npm install bcryptjs
$ npm install -D @types/bcryptjs


O bcrypt é um algoritmo de hash usado para geração de senhas
em sistemas como OpenBSD e algumas distribuições linux
$2a$10$orBtaSVUxYTL8DabeLMOg.J.tBEL2Y5UkXo1jX4z6rY14ps6dLrTK

$2a$ J.tBEL2Y5UkXo1jX4z6rY14ps6dLrTK
10
Bcrypt Hash – 31 chars
(Blowfish) Fator de orBtaSVUxYTL8DabeLMOg.
custo
Salt – 22 chars
O módulo bcrypt

Para gerar uma senha com salt podemos usar o código abaixo,
onde rounds é o número de rounds, e senha é a senha
informada pelo usuário
const salt = await [Link](rounds);
const hash = await [Link](senha, salt);


Para verificar se uma senha está correta (no resource auth),
podemos usar a função compare do bcrypt
const ok = await [Link](senha, hash);

Senha
criptografada
(valor de hash
do código
anterior)
CRUD de Usuários

Os DTOs usados no CRUD de usuários serão CreateUserDto,
UserDto e UpdateUserDto

Definidos no arquivo src/resources/user/[Link]

O código do roteador, mostrado abaixo, estabelece quais serão
as rotas e funções do controlador
[Link]('/', [Link]);
[Link]('/', [Link]);
[Link]('/:id', [Link]);
[Link]('/:id', [Link]);
[Link]('/:id', [Link]);


O serviço irá contar com as funções getAllUsers, createUser,
updateUser, findUserByEmail, findUserById e deleteUsuario
CRUD de Usuários

Os DTOs usados no CRUD de usuários serão CreateUserDto,
UserDto e UpdateUserDto

Definidos no arquivo src/resources/user/[Link]

O código do roteador, mostrado abaixo, estabelece quais serão
Tipo User sem
a propriedade
as rotas e funções do controlador
password
[Link]('/', [Link]);
[Link]('/', [Link]);
[Link]('/:id', [Link]);
[Link]('/:id', [Link]);
[Link]('/:id', [Link]);


O serviço irá contar com as funções getAllUsers, createUser,
updateUser, findUserByEmail, findUserById e deleteUsuario
O módulo bcrypt

Para gerar uma senha com salt podemos usar o código abaixo

Número de
rounds para
geração do
hash

const rounds = parseInt([Link].SALT_ROUNDS!);


const salt = await [Link](rounds);
const hash = await [Link]([Link], salt);
Exercício: Desenvolva o CRUD de usuário na
pasta src/resources/usuario
Senha
informada
github
pelo usuário
ExpAPI
Resource Auth

Além do resource Usuário, será criado um resource Auth
contendo as funções de signup, login e logout
// Arquivo src/resources/[Link]
import { Router } from 'express';
import authController from './[Link]';
const router = Router();
[Link]('/signup', [Link]);
[Link]('/login', [Link]);
[Link]('/logout', [Link]);
export default router;

// Arquivo src/router/[Link]
import authRouter from '../resources/auth/[Link]';
...
[Link]('/', authRouter);
...
Resource Auth
A função signup
será usada para

Além do resource Usuário,queserá
novoscriado
clientesum resource Auth
criem seulogin
contendo as funções de signup, próprio
e logout
cadastro na
loja virtual
// Arquivo src/resources/[Link]
import { Router } from 'express';
import authController from './[Link]';
const router = Router();
[Link]('/signup', [Link]);
[Link]('/login', [Link]);
[Link]('/logout', [Link]);
export default router;

// Arquivo src/router/[Link]
import authRouter from '../resources/auth/[Link]';
...
[Link]('/', authRouter);
...
Resource Auth
A função signup
será usada para

Além do resource Usuário,queserá
novoscriado umdaresource
clientes
Ela difere função Auth
criem seulogin
contendo as funções de signup, próprio
e do
create logout
resource
cadastro Usuário,
na que será
loja virtual
usada apenas por
// Arquivo src/resources/[Link]
import { Router } from 'express'; administradores
para criar novos
import authController from './[Link]';
usuários
const router = Router();
[Link]('/signup', [Link]);
[Link]('/login', [Link]);
[Link]('/logout', [Link]);
export default router;

// Arquivo src/router/[Link]
import authRouter from '../resources/auth/[Link]';
...
[Link]('/', authRouter);
...
Resource Auth

A função signup do controlador irá usar uma função
createUsuário, da camada de serviço do resource Usuario
// Arquivo src/resources/[Link]
const signup = async (req: Request, res: Response) => {
const usuario = [Link] as SignUpDto;
try {
if (await buscaUsuarioPorEmail([Link]))
return res
.status(400)
.json({ msg: 'Email informado já está sendo usado' });
const newUsuario = await createUsuario({
...usuario,
Usa o
tipoUsuarioId: [Link],
});
bcryptjs para
[Link](201).json(newUsuario); criptografar as
} catch (e: any) { Signup só senhas
[Link](500).json([Link]); permite a cria-
} ção de usuá-
}; rios cliente
Resource Auth

A função login do controlador Auth irá usar uma função
checkAuth da camada de serviço de Auth
// Arquivo src/resources/[Link]
export const checkAuth = async (
credenciais: LoginDto,
): Promise<Usuario | null> => {
const { email, senha } = credenciais;
const usuario = await [Link]({ where: { email } });
if (!usuario) return null;
const ok = await [Link](senha, [Link]);
}
Usa o
bcryptjs para
verificar a senha
digitada pelo
usuário no
login
Resource Auth

Ao efetuar o login, criamos as variável de sessão uid e
tipoUsuarioId serão criadas
// Arquivo src/resources/[Link]
const login = async (req: Request, res: Response) => {
const { email, senha } = [Link];
try {
const usuario = await checkAuth({ email, senha });
if (!usuario)
return [Link](401).json({
msg: 'Email e/ou senha incorretos'
});
[Link] = [Link];
[Link] = [Link];
[Link](200).json({ msg: 'Usuário autenticado' });
} catch (e) {
[Link](500).json(e); Variáveis
} de
} sessão
Resource Auth

Para que as variáveis de sessão funcionem, é preciso adicionar
tais atributos à interface SessionData de express-session

Para isso, adicionamos o seguinte código no começo (após a
importação dos pacotes) do arquivo src/[Link]
declare module "express-session" {
interface SessionData {
uid: string;
tipoUsuario: string
}
}
Resource Auth

Para que as variáveis de sessão funcionem, é preciso adicionar
tais atributos à interface SessionData de express-session

Para isso, adicionamos o seguinte código no começo (após a
importação dos pacotes) do arquivo src/[Link]
declare module "express-session" {
interface SessionData {
uid: string;
tipoUsuario: string
}
}
Resource Auth

Para que as variáveis de sessão funcionem, é preciso adicionar
tais atributos à interface SessionData de express-session

Para isso, adicionamos o seguinte código no começo (após a
importação dos pacotes) do arquivo src/[Link]
declare module "express-session" {
interface SessionData {
uid: string;
tipoUsuario: string
}
}
Mecanismo de Autorização

Um middleware isAdmin pode ser criado para restringir
usuários não autorizados de determinadas partes da aplicação
// Arquivo src/middlewares/[Link]

import { Request, Response, NextFunction } from 'express';


import { checkIsAdmin } from '../resources/auth/[Link]';

const isAdmin = async (


req: Request,
res: Response,
next: NextFunction
) => {
const uid = [Link];
if (uid && (await checkIsAdmin(uid))) next();
else [Link](403).json({ msg: 'Não autorizado' });
};

export default isAdmin;


Mecanismo de Autorização

Uma vez que o middleware isAdmin seja criado, podemos
impedir que usuários não logados ou clientes acessem rotas
que devem ser usadas apenas por usuários admin
// Arquivo src/resources/usuario/[Link]

import { Router } from 'express';


import produtoController from './[Link]';
import isAdmin from '../../middlewares/isAdmin';
const router = Router();

[Link]('/', [Link]);
[Link]('/', isAdmin, [Link]);
[Link]('/:id', [Link]);
[Link]('/:id', isAdmin, [Link]);
[Link]('/:id', isAdmin, [Link]);

export default router;


Mecanismo de Autorização

Uma vez que o middleware isAdmin seja criado, podemos
impedir que usuários não logados ou clientes acessem rotas
que devem ser usadas apenas por usuários admin
// Arquivo src/resources/usuario/[Link]

import { Router } from 'express';


import produtoController from './[Link]';
import isAdmin from '../../middlewares/isAdmin';
const router = Router();
Exercício: Desenvolva
[Link]('/', o middleware isAdmin, seguindo
[Link]);
os passos mostrados
[Link]('/', isAdmin,anteriormente. Além disso, desenvolva
[Link]);
um middleware [Link]);
[Link]('/:id', isAuth, similar ao isAdmin, mas
[Link]('/:id', isAdmin, [Link]);
que apenas verifica se o usuário está logado github
[Link]('/:id', isAdmin, [Link]);
ou não
export default router;
ExpAPI
Exercício: Implemente um resource compra que permita
o usuário i) adicionar produtos no carrinho de compra, e ii)
concluir a compra salvando os itens do carrinho no banco
de dados. Dica: o carrinho de compra pode ser representado
por uma variável de sessão contendo um array dos produtos
com suas respectivas quantidades.
github
ExpAPI
Swagger

O Swagger é uma ferramenta para documentação, consumo e
visualização dos endpoints de uma API Rest

Ele é comumente usado para que devs backend gerem a
documentação de APIs para devs frontend
Swagger

O Swagger é uma ferramenta para documentação, consumo e
visualização dos endpoints de uma API Rest

Ele é muito usado para que a equipe responsável pelo backend
documente a sua API para a equipe do frontend
Swagger

Para usar o Swagger, vamos precisar dos pacotes swagger-ui-
express e do swagger-autogen

$ npm i swagger-ui-express
$ npm i -D swagger-autogen @types/swagger-ui-express


A documentação oficial do Swagger é bastante completa e está
disponível no endereço [Link]
Swagger

Gerar a documentação do Swagger é uma tarefa difícil, e por
isso utilizamos ferramentas para automatizar esse processo

Em nossa aplicação, vamos utilizar um pacote do framework
Express chamad Swagger Autogen para gerar a documentação

Swagger Autogen
Swagger

Para usar o swagger-autogen, o primeiro passo é criar um
arquivo src/[Link], com o conteúdo abaixo
// Arquivo src/[Link]
import swaggerAutogen from "swagger-autogen";
import dotenv from "dotenv";
[Link](); O swagger
const doc = { autogen usa os
info: { arquivos de rotas
title: "API da Loja virtual", para documentar
description: "Documentação da API", a API
},
host: `${[Link]}:${[Link]}`,
};
const outputFile = "./[Link]";
const routes = ["./src/router/[Link]"];
swaggerAutogen()(outputFile, routes, doc);
Swagger

Após isso, criamos um novo script no arquivo [Link]
que será usado para gerar a documentação do Swagger
"scripts": {
"start": "nodemon -e js,json,ts,yaml src/[Link]",
"swagger": "ts-node src/[Link]"
},
Swagger
Com esse comando

Após isso, criamos um novo script no arquivo [Link]
a documentação
do swagger é
que será usado
gerada para gerar a documentação do Swagger
no arquivo
[Link]
"scripts": {
"start": "nodemon -e js,json,ts,yaml src/[Link]",
"swagger": "ts-node src/[Link]"
},
Swagger

Para disponibilizar a documentação, é necessário configurar o
pacote swagger-ui-express no src/[Link]
// Arquivo src/[Link]
...
import swaggerUi from "swagger-ui-express";
import swaggerFile from "./[Link]";
...
[Link]("/api", [Link], [Link](swaggerFile));
Swagger

Para disponibilizar a documentação,A documentação
é necessário configurar o
já pode ser
pacote swagger-ui-express no src/[Link]
acessada, mas
precisa de ajustes
// Arquivo src/[Link]
...
import swaggerUi from "swagger-ui-express";
import swaggerFile from "./[Link]";
...
[Link]("/api", [Link], [Link](swaggerFile));
Swagger

O primeiro passo para melhorar a documentação é definir as
tags de cada resource no arquivo router/[Link]

As tags são usadas para organizar e agrupar endpoints
relacionados de uma API

[Link](
"/auth",
// #[Link] = ['Auth']
authRouter
);

[Link]( Atribuindo a tag


"/product", Auth para os
// #[Link] = ['Product'] endpoints do
productRouter resource Auth
);

...
Swagger

O primeiro passo para melhorar aAgora
documentação
os é definir as
tags de cada resource no arquivo router/[Link]
endpoints estão
organizados
[Link]( por resources
"/auth",
// #[Link] = ['Auth']
authRouter
);

[Link](
"/produto",
// #[Link] = ['Produto']
produtoRouter
);

[Link](
"/usuario",
// #[Link] = ['Usuario']
usuarioRouter
);
Swagger

O primeiro passo para melhorar aAgora
documentação
os é definir as
tags de cada resource no arquivo router/[Link]
endpoints estão
organizados
[Link]( por resources
"/auth",
// #[Link] = ['Auth']
authRouter No entanto,
); dentro de cada
endpoint não
[Link]( existem muitas
"/produto", informações
// #[Link] = ['Produto']
produtoRouter
);

[Link](
"/usuario",
// #[Link] = ['Usuario']
usuarioRouter
);
Swagger

Para a documentação dos endpoints, precisamos informar
exemplos dos tipos de dados usados em src/[Link]
const doc = {
...
definitions: {
CreateProductDto: {
name: "Modern Soft Sausages",
price: 2699.0,
stockQuantity: 9,
},
Product: {
id: "8a2053de-5d92-4c43-97c0-c9b2b0d56703",
name: "Modern Soft Sausages",
price: 2699.0,
stockQuantity: 9,
createdAt: "2023-11-07T19:27:15.645Z",
updatedAt: "2023-11-07T19:27:15.645Z",
},
},
};
Swagger

A partir disso, podemos adicionar informações de cada
endpoint no controlador do resource

const create = async (req: Request, res: Response) => {


/*
#[Link] = 'Adiciona um novo produto na base.'
#[Link]['body'] = {
in: 'body',
schema: { $ref: '#/definitions/CreateProductDto' }
}
#[Link][200] = {
schema: { $ref: '#/definitions/Product' }
}
*/
const produto = [Link] as CreateProductDto;
...
};
Swagger

A partir disso, podemos adicionar informações de cada
endpoint no controlador do resource

constcreate
const read == async
async (req:
(req: Request,
Request, res:
res: Response)
Response) =>
=> {{
/*/*
#[Link]= ='Adiciona
#[Link] 'Recuperaum
dados
novo de um produto
produto específico.'
na base.'
#[Link]['id'] == {{ description: 'ID do produto' }
#[Link]['body']
#[Link][200]
in: 'body', = {
schema:{ {$ref:
schema: $ref:'#/definitions/CreateProduto'
'#/definitions/Product' } }
}}
*/
#[Link][200] = {
const { id
schema: } = [Link];
{ $ref: '#/definitions/Produto' }
} ...
};
*/
const produto = [Link] as CreateProdutoDto;
...
};
Swagger

A partir disso, podemos adicionar informações de cada
endpoint no controlador do resource

constcreate
const read == async
async (req:
(req: Request,
Request, res:
res: Response)
Response) =>
=> {{
/*/*
#[Link]= ='Adiciona
#[Link] 'Recuperaum
dados
novo de um produto
produto específico.'
na base.'
#[Link]['id'] == {{ description: 'ID do produto' }
#[Link]['body']
#[Link][200]
in: 'body', = {
schema:{ {$ref:
schema: $ref:'#/definitions/CreateProduto'
'#/definitions/Produto' } }
}}
*/
#[Link][200] = {
const { id
schema: } = [Link];
{ $ref: '#/definitions/Produto' }
} ...
};
*/
const produto = [Link] as CreateProdutoDto;
...
};
Swagger

A partir disso, podemos adicionar informações de cada
endpoint no controlador do resource

constcreate
const read == async
async (req:
(req: Request,
Request, res:
res: Response)
Response) =>
=> {{
/*/*
#[Link]= ='Adiciona
#[Link] 'Recuperaum
dados
novo de um produto
produto específico.'
na base.'
#[Link]['id'] == {{ description: 'ID do produto' }
#[Link]['body']
#[Link][200]
in: 'body', = {
schema:{ {$ref:
schema: $ref:'#/definitions/CreateProduto'
'#/definitions/Produto' } }
}}
*/
#[Link][200] = {
Exercício:
const Crie
{ id
schema: { } =a [Link];
$ref:página com a documentação
'#/definitions/Produto' } do swagger
usando
} ... as instruções dos slides
};
*/ github
const produto = [Link] as CreateProdutoDto;
... ExpAPI
};

Você também pode gostar