Web Avançado
Chrysthian Simão
Backend e Banco
Web Avançado
Construir Soluções Web
- Frontend com React e TS
- Backend com Node
- Integração com Banco de dados NOSQL MongoDB
Javascript Moderno
O que queremos dizer com javascript moderno?
Sintaxe de código que funciona em todos os navegadores atuais.
95% dos navegadores utilizados hoje são: Chrome, Firefox, Edge, Safari e Opera
Javascript Moderno

Javascript tem versões e elas tem novas features a cada uma, então vamos nos basear em uma que tenha um suporte legal do que consideramos sintaxe moderna, tendo um suporte adequado
Montando Ambiente
Express
Já temos o node.js, já aprendemos a criar projetos frontend com ele, agora vamos precisar criar uma aplicação backend.
É um novo projeto, que vai ser levantando em uma porta separada usando uma biblioteca chamada express.js
Express
Express usa JS (ou TS no nosso caso) e isso facilita a barreira de linguagens, não é preciso aprender uma NOVA linguagem para mexer no back e no front, e só isso já ajuda grandão a diminuir a curva de aprendizado.
Express
Vamos criar um projeto usando o npm, MAS não temos o template de uma aplicação como o create-react-app ou create-next-app, vamos compor a solução manualmente.
Express
npm init
Pacotes e dependências
npm install express mongoose nodemon dotenv-
express: será a base para nossa aplicação backend
-
mongoose: será a biblioteca para conectarmos no BD
-
nodemon: irá recarregar a nossa aplicação quando os arquivos mudarem
- dotenv: será responsável por guardar e acessar as nossas variáveis de ambiente (informações necessárias para rodar nossa aplicação)
Pacotes e dependências
npm install -D typescript @types/express @types/node-
typescript: nosso companheiro de tipagem forte nos projetos
- @types: para que possamos usar as bibliotecas sem receber um xingão na IDE com TS, precisamos adicionar os tipos das bibliotecas que estamos utilizando
-D ou -dev significa instalar as dependências do projeto apenas para desenvolvimento, não são bibliotecas necessárias para a aplicação final, como o TS transpila para JS no build final, só precisamos em ambiente de dev
Configurando o TS
npx tsc --initTodo projeto TS usa um arquivo de configurações para definir várias coisas.
O arquivo tsconfig.json define esses parâmetros e oferece flexibilidade para modificar a customizar o transpiler para as suas necessidades.
Esse arquivo normalmente fica na raiz do projeto.
Configurando o TS
{
"compilerOptions": {
"outDir": "./build",
"target": "es2017",
"module": "commonjs",
"rootDir": "./",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
}
}
Configurando o TS
Se tentarmos executar um arquivo index.ts usando Node, nós vamos encontrar um erro.
Aplicações Node originalmente foram feitas pra usar JS, não TS!
Mas uma dependência adicional resolve isso pra gente
npm i -D ts-nodePackage.json
Para adicionar comandos de:
- build (transpile pra versão em JS)
- iniciar ambiente de desenvolvimento
- iniciar a aplicação final
Vamos adicionar uma configuração no nosso package.json
Configurando o Package.json
tsc é Typescript compiler, ele vai ler seu tsconfig.json e gerar uma versão final para deploy
"scripts": {
"build": "npx tsc",
"start": "node build/index.js",
"dev": "nodemon src/index.ts"
},Configurando o Nodemon
Vamos tomer um passo extra para um setup ligeiramente mais refinado, vamos usar um arquivo nodemon.json na raiz do projeto.
Este arquivo especifica diretórios e extensões a observar e definir comandos a serem executados, enquanto o nodemon faz sua mágica de recarregar a aplicação.
{
"watch": ["src"],
"ext": "ts",
"exec": "concurrently \"npx tsc --watch\" \"ts-node src/index.ts\""
}Configurando o Nodemon
Mas claro que isso vai pedir uma deppendência extra como você pode ter imaginado ao ver o comando
npm i -D concurrentlyconcurrently: vai permitir rodar comandos em paralelo
Vamos usar isso porque tanto o nodemon quanto o TSC vão monitorar alterações e oitencialmente competir para exibir seus respectivos logs.
Configurando o .env
PORT=4000Na raiz do projeto crie um arquivo chamado ".env", esse cara vai guardar as nossas variáveis de ambiente, configurações globais da nossa aplicação, e vamos armazenar por hora, apenas a porta que a aplicação vai subir (4000)
Aplicação básica
import express, { Express, Request, Response } from "express";
import dotenv from "dotenv";
dotenv.config();
const app: Express = express();
const port = process.env.PORT || 4000;
app.get("/", (req: Request, res: Response) => {
res.send("Express + TypeScript Server");
});
app.listen(port, () => {
console.log(`[server]: Server is running at http://localhost:${port}`);
});index.ts
Press Play!
Ok, configuramos tudão, mas vamos rodar isso então
npm run dev
npm run build
npm startExecutar em modo dev (com TS)
Gerar um build para produção
Executar a versão gerada no build
API
Get, Post, Put, Delete
Middlewares
Express é um framework que executa funções (middlewares) que tratam as informações que trafegam nas suas rotas.
Essas funções tem acesso aos objetos de Request e Response e podem fazer alterações neles
Middlewares
Para tratar o corpo das requisições vamos usar o express.json.
Para servir arquivos estáticos como imagens, arquivos CSS, JS, use o middleware express.static.
Os dois já acompanham a biblioteca.
Middlewares
const app: Express = express();
app.use(express.json());
app.use(express.static("public"));index.ts
Swagger

Esse deve ser um velho conhecido de vocês para expor endpoints do servidor de aplicação para consultar no frontend
Swagger
O que precisa pra adicionar ele?
Sim! mais dependências no projeto
npm install tsoa swagger-ui-express
npm install -D @types/swagger-ui-express-
tsoa: Typescript Open API
- @types: lembra deles? sempre inclua os tipos pra não tomar xingão no TS!
Configurando o TS (again!)
{
"compilerOptions": {
...,
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
Adicionar suporte a decorators (swagger)
Configurando o tsoa
{
"entryFile": "src/index.ts",
"noImplicitAdditionalProperties": "throw-on-extras",
"spec": {
"outputDirectory": "public",
"specVersion": 3
}
}Percebe o padrão aqui? bibliotecas novas podem precisar de configurações novas
Crie um tsoa.json na raiz do projeto
Criando um controller
import { Get, Route } from "tsoa";
interface PingResponse {
message: string;
}
@Route("api/ping")
export default class PingController {
@Get("/")
public async getMessage(): Promise<PingResponse> {
return {
message: "pong!",
};
}
}Criando uma rota
import express, { Request, Response } from "express";
import PingController from "../controllers/PingController";
const router = express.Router()
router.get('/ping', async (req: Request, res: Response) => {
const controller = new PingController();
const response = await controller.getMessage();
return res.send(response);
})
export default router;Middlewares (again!)
import pingRoutes from './routes/pingRoutes'
app.use('/api/', pingRoutes)index.ts
Middlewares (again!)
app.use(
"/swagger", /* endereço do swagger */
swaggerUi.serve,
swaggerUi.setup(undefined, {
swaggerOptions: {
url: "/swagger.json",
},
})
);
index.ts
Configurando o Package.json (again!)
Swagger joins the party!
"scripts": {
"build": "npx tsc",
"start": "node build/index.js",
"dev": "nodemon src/index.ts",
"swagger": "tsoa spec"
},Configurando o Nodemon
(again!)
{
"watch": ["src"],
"ext": "ts",
"exec": "concurrently \"npx tsc --watch\" \"ts-node src/index.ts\" \"tsoa spec\""
}Promises
Promises
Um objeto de Promise representaum código que irá executar asíncronamente e retornar seu valor de sucesso ou falha
const myPromise = new Promise((resolve, reject) => {
// Asynchronous code here
});O construtor de uma Promise aceita uma função que deve receber dois parâmetros: uma função de sucesso (resolve) e uma função de falha (reject)
Promises
myPromise
.then((value) => {
console.log('Promise resolved with value: ' + value);
})
.catch((error) => {
console.error('Promise rejected with error: ' + error);
});Uma vez declarada, use os métodos .then() e .catch() para definir sucesso ou falha da operação assíncrona
Promises + async/await
async function myAsyncFunction() {
try {
const value = await myPromise;
console.log('Promise resolved with value: ' + value);
} catch (error) {
console.error('Promise rejected with error: ' + error);
}
}Banco de Dados
NoSQL
É uma abordagem de banco de dados que permite armazenamento e pesquisa (Queries) fora da estrutura tradicional encontrada em bancos relacionais.
Existem várias abordagens e tipos de bancos NoSQL
SQL vs NoSQL
Estrutura relacional
(tabelas, Ids, PKs, FKs, consistência, segurança de dados, backup seguro)
Estrutura não relacional
(posso definir as relações entre os dados on the fly, alta escala, alta flexibilidade)
Escala vertical
(mais poder de processamento para gerenciar relacionamentos)
Escala horizontal
(mais memória em uma escala mais eficiente que um banco relacional)
Baseado em tabelas
Baseado em documentos
( key/value, JSON etc...)
SQL vs NoSQL
SQL
Chaos I am your master!
(cada solução pode ter a sua forma de trafegar informação)
Controle de transações com múltiplas linhas
Não Estruturado
Qual é Melhor?
Depende da sua aplicação, são soluções para problemas diferentes, é comum usar alguma combinação dos dois para uma aplicação complexa.
Qual é Melhor?
Use um banco relacional se as palavras chave são:
- Consistência
- Segurança
- Facilidade de backup/restore
Use um NoSQL ee as palavras chave são:
- Flexibilidade
- Escalabilidade
- Eficiência custo/benefício
NoSQL em ação
JSON
{
"id": 1,
"name": "CH",
"role": "Frontend Engineer"
}Muitos de nós já usamos o formato para representar alguma estrutura, já trafegamos isso entre requisições e tudo mais, e se a gente resolvesse armazenar os dados nesse formato?
JSON
Isso traz um modelo mais livre de dados, sem muita estrutura pré-definida, com qualquer número de campos e tipos.
Mongo DB
MongoDB é feito pra esse tipo de dado.
Ele armazena as informações em memória, mantendo um acesso fácil para consulta.
Mongo DB
Mas com grandes poderes vem... Um mapeamento louco no seu banco de dados, é bom manter alguma estrutura ao trabalhar com essas informações.
Mantenha valores nulos em campos vazios (ao invés de omití-los) e defina algum tipo de consistência na sua estrutura.
Mongo DB
Uma das coisas mais diferentes que podem surpreender, é que muitas soluções NoSQL tem serviços de nuvem, por exemplo: Firebase (muito usado em soluções para aplicativos) e claro, mongoDB.
Mongo DB
Vamos então criar uma conta em:
Mongo DB
Escolha a opção grátis, configure as credenciais
GUARDE A SUA SENHA!
Crie seu cluster, como sempre o nome é a coisa mais difícil eu usei "labs" pro meu caso vocês queiram copiar
.env
PORT=4000
DATABASE_URL=[string de conexão aqui]Como agora temos a string de conexão, vamos adicioná-la ao seu projeto, lembre-se que ela não vem configurada com meu usuário, coloque o seu!
Criando uma conexão com o BD
import mongoose from "mongoose";
export const connect = (databaseUrl: string) =>{
mongoose.connect(databaseUrl)
const database = mongoose.connection
database.on('error', (error) => {
console.log(error)
})
database.once('connected', () => {
console.log('Database Connected');
})
}
Criando uma conexão com o BD
import { connect } from "./service/database"
const databaseUrl = process.env.DATABASE_URL || ""
connect(databaseUrl)Model
import mongoose from "mongoose"
const icecreamSchema = new mongoose.Schema({
name: {
required: true,
type: String,
},
})
export const IcecreamModel =
mongoose.model("Icecream", icecreamSchema)
Controller
@Post("/create")
public async create(@Body() body: { name: string })
: Promise<string> {
const data = new IcecreamModel({
name: body.name,
})
try {
await data.save()
return "OK"
} catch (error) {
return JSON.stringify(error)
}
}Route
router.post("/create",
async (req: Request, res: Response) => {
const response = await controller.create(req.body)
return res.status(
response === "OK" ? 200 : 400
).send(response)
})Hello-Express-Mongo

Exemplos estão nesse projeto no repositório
MAS, a string de conexão com o banco é a seu critério!
Queries mais complexas
await Character.create([
{ name: 'Jean-Luc Picard', age: 59, rank: 'Captain' },
{ name: 'William Riker', age: 29, rank: 'Commander' },
{ name: 'Deanna Troi', age: 28, rank: 'Lieutenant Commander' },
]);
const filter = { age: { $gte: 30 } };
docs = await Character.find(filter);
docs.length; // 1
docs[0].name; // 'Jean-Luc Picard'
docs[0].age // 59Queries mais complexas
- $eq: comparar valores iguais
- $lt: comparar valores menores
- $lte: comparar valores menores ou iguais
- $gt: comparar valores maiores
- $gte: comparar valores menores ou iguais
Relacionamentos
Embora a gente possa ter relacionamentos semelhantes ao SQL, o NoSQL é mais livre, mais "freestyle", você pode criar os relacionamentos mas cabe a você reforçar as regras entre as entidades.
One-to-One


One-to-Many


Many-to-Many

Queries mais complexas
@Get("/query")
public async query(): Promise<JsonObject> {
try {
const data = await IcecreamModel.find()
.populate("toppingId");
return data;
}
catch (error: any) {
return {
error: error.message
};
}
}Queries mais complexas
@Get("/fields")
public async fields(): Promise<JsonObject> {
try {
const data = await IcecreamModel.find()
.select("name toppingId -_id");
return data;
}
catch (error: any) {
return {
error: error.message
};
}
}campos
Omita o campo _Id
(sinal de menos)
Expondo endpoints
CORS
Achou que acabaram as bibliotecas para o nosso backend? achou errado!
npm install cors
npm install -D @types/cors-
cors: Cross-Origin Resource Sharing
- @types: lembra deles?
CORS
import cors from "cors"
// aceitar requisições desse endereço
const corsOptions = {
origin : ['http://localhost:3000'],
}
app.use(cors(corsOptions)) Lá no frontend
useEffect(() => {
fetch('http://localhost:4000/api/icecream/getAll',
{method: "GET"})
.then((res) => res.json())
.then((data) => {
console.log(data);
})
}, [])Dentro do seu componente (por exemplo)
useEffect(() => {
if (!isDark) {
const data = {
id: "6646818941907520577d1973",
name: "Morango",
}
fetch("http://localhost:4000/api/icecream/update",
{
method: "PATCH",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
})
.then((res) => res.json())
.then((data) => {
console.log(data)
})
}
}, [isDark])Avaliação A1!
- Frontend com React + TS chamando os endpoints
- Aplicação node ouvindo chamadas do front e persistindo dados num banco NoSQL
- GET / POST / PATCH / DELETE
- Queries customizadas, igual a quantidate de integrantes (minimo 3)
- Com controle de estado no front (Redux/Zustand)
- O tema que você quiser, defendendo o código durante a apresentação
Web Avançado 3
By Chrysthian Simão
Web Avançado 3
- 494