Express API
Express API
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 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
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
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
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]
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"
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
Exemplos:
SELECT * 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:
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;
Exemplo:
INSERT INTO user (nome, idade) VALUES ('Carlos', 26);
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' }
});
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();
// 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'>;
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: 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
HTTP1.1 200 OK
2 Set-Cookie: lang=pt-BR
Dados requisitados
HTTP1.1 200 OK
4
Dados requisitados
Respostas do Cliente Respostas do Servidor
HTTP1.1 200 OK
2 Set-Cookie: lang=pt-BR
Dados requisitados
HTTP1.1 200 OK
4
Dados requisitados
HTTP1.1 200 OK
6 Set-Cookie: lang=en-US
4
Dados requisitados
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
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 != '
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
// 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]
[Link]('/', [Link]);
[Link]('/', isAdmin, [Link]);
[Link]('/:id', [Link]);
[Link]('/:id', isAdmin, [Link]);
[Link]('/:id', isAdmin, [Link]);
$ 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
);
...
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
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
};