Node.js
Da introdução à APIs REST em produção
Henrique Rotava
Objetivos
- Apresentar conceitos sobre o ecossistema Node.js comumente utilizados por profissionais no dia-a-dia do desenvolvimento de aplicações
- Instigar a curiosidade para buscar mais conhecimento sobre os tópicos apresentados e sugeridos ao longo do curso
- Demonstrar como construir uma aplicações prontas para serem utilizadas por clientes.

Roadmap
- Introdução ao Node.js
- Programação assíncrona
- Reintrodução ao JavaScript
- Callbacks vs Promises
- Manipulação de listas
- Introdução ao NPM
- Trabalhando com bancos de dados não relacionais
- MongoDB e Mongoose
- Introdução a APIs RESTful
- Express
- Swagger e documentação
- Autenticação com JWT
- Configurações por ambiente

O que é Node.js
- Uma plataforma para desenvolvimento de aplicações no lado servidor
- Tem como objetivo fornecer uma maneira fácil de construir programas de rede escaláveis
- Execução single thread, possibilitando um melhor o uso de memória
- Manipulação de I/O não bloqueante orientado a eventos
- Permite criar qualquer tipo de aplicação no lado servidor
- Programação em JavaScript
- Multiplataforma

O que não é Node.js
- Não é um servidor de aplicação
- Não um framework
- Não é uma linguagem de programação

Onde usar Node.js
- Construção de APIs
- Aplicações de tempo real, chats, multi usuários
- Serviços streaming
- Aplicações com demanda de alta escalabilidade
- Construção de servidores de aplicação
- Manipulação de arquivos, vídeos, imagens
- Criação de CLIs
- Automação de atividades
- Comunicação com bases de dados
- Tratamentos de requisições e respostas HTTP

Como o Node.js funciona
- Executa em uma máquina virtual JavaScript, a engine V8
- Engine usada pelo Google para interpretar e executar JavaScript no navegador
- A engine é escrita em C++, é rápida e roda em qualquer arquitetura
- Orientado a eventos
- Usando JavaScript tudo se torna fácil e comum
- Processamento single thread com event loop

Como o Node.js funciona
- Eventos empilhados na event queue e processados usando o algoritmo FIFO
- Operações bloqueantes são delegadas para o sistema operacional, liberando a fila
- Quando a atividade terminar, o event loop devolve a resposta para quem chamou, via callback
- Atividades que demandam alto processamento podem bloquear toda a fila

Como o Node.js funciona


Como o Node.js funciona


Programação assíncrona
- Programação orientada a eventos
- Uma atividade é chamada e espera-se a resposta posterior
- Não existe uma ordem de execução do código
- Podendo gerar variáveis nulas ou indefinidas e consequentemente erros

Programação assíncrona
Demonstração: Exemplo de código sequencial vs assíncrono

// Código sequencial
console.log('Primeiro log');
function buscarPessoa() {
console.log('Segundo log')
return 'Asdrubal';
}
let pessoa = buscarPessoa();
console.log('Terceiro log', pessoa);
// Código assíncrono
console.log('Primeiro log');
function buscarPessoa() {
setTimeout(function() {
console.log('Segundo log')
return 'Asdrubal';
}, 1000);
}
let pessoa = buscarPessoa();
console.log('Terceiro log', pessoa);
Instalação Node.js e NPM
Windows: Acessar https://nodejs.org, baixar versão LTS e executar o instalador
Linux: executar o comando sudo apt-get install nodejs
macOS: Acessar https://nodejs.org, baixar versão LTS e executar o instalador
Após instalado, rodar os comandos a seguir para validar a instalação:
> node --version
> npm --version
Linux, Outras formas: https://nodejs.org/en/download/package-manager/#debian-and-ubuntu-based-linux-distributions-enterprise-linux-fedora-and-snap-packages

(Re)introdução ao JavaScript
- Linguagem de programação criada pela Netscape e originalmente usada para tornar páginas web dinâmicas
- ECMAScript uma especificação de uma linguagem de script, JavaScript é a implementação dessa especificação
- Uma engine é um programa que executa o código JavaScript
- Hoje está presente em servidores, bancos de dados, hardwares, IoT, etc.
- Altamente dinâmica
- Em evolução constante com novas especificações anuais
- Muito popular, comunidade enorme de desenvolvedores

Declaração de variáveis
Disclaimer: por motivos de compatibilidade alguns códigos podem não funcionar em todos os ambientes, como em navegadores desatualizados

// var, forma antiga de declaração, evitar usar
var texto = 'exemplo de texto'
// A partir do ES2015
// const, forma preferível, não pode ser reatribuída,
// valor não é imutável, mais fácil de debugar
const fruta = 'abacaxi'
const funcao = function () {}
// let, para variáveis que mudam de valor
let valor = 12.89
valor = 5
let usuario = {
nome: 'Administrador'
}
Funções

function soma (valor1, valor2) {
return valor1 + valor2;
}
const resultado = soma(50, 33)
- São subprogramas
- Executadas apenas quando chamadas
- Sempre retornam um valor, por padrão retornam undefined
- Valores podem ser passados por parâmetro
Tipagem fraca

let dinamico;
console.log(dinamico, typeof dinamico);
dinamico = 'literal';
console.log(dinamico, typeof dinamico);
dinamico = 5;
console.log(dinamico, typeof dinamico);
dinamico = ['array', 'de', 'strings']
console.log(dinamico, typeof dinamico, Array.isArray(dinamico));
dinamico = {
objeto: true
}
console.log(dinamico, typeof dinamico);
- Variáveis não tem validação do tipo de dado
Classes

class User {
constructor (nome, dataNascimento) {
this.nome = nome
this.dataNascimento = dataNascimento
}
calcularIdade () {
// Calcular idade baseada na propriedade dataNascimento
}
}
- Introduzidas no EcmaScript 6 (2015)
- Basicamente são funções especiais
- Um novo modelo de herança e orientação a objeto
- Uma forma mais simples e clara de criar objetos e lidar com heranças
Opções interessantes

/*
* Verificação condicional
* Os valores null, "", undefined, 0 e false são considerados false em uma condição
*/
console.log(!!null, !!"", !!undefined, !!0)
// Template strings, usando crases
const pontos = 102
console.log(`Você possui ${pontos} pontos`)
// Aspas simples ou duplas
const simples = 'possível' // parece mais simples e limpo
const duplas = "possível também"
// O ponto e vírgula é opcional
console.log('Quem precisa de ponto e vírgula?');
Padrão callback

- Callback é um modelo de desenvolvimento onde uma função passada como parâmetro para outra função e será chamada quando um evento assíncrono acontecer
- Por padrão deve ser o último parâmetro da função chamada
- Por padrão deve esperar dois parâmetros, erro e resultado, respectivamente
Demonstração: Implementar funções assíncronas: obterUsuario, obterEndereco e obterTelefone onde uma depende da outra
Callback hell


Callback hell


Implementação usando Promises

- ECMAScript 6 (2015)
- Uma forma melhor de resolver chamadas assíncronas
- É um objeto que representa a eventual conclusão ou falha de uma operação assíncrona
- Permite executar operações assíncronas de forma encadeada
Estado pending -> estado inicial, nada aconteceu
Estado fulfilled -> quando já executou
Estado rejected -> quando a operação falhou
Demonstração: transformando callbacks em Promises
Implementação com async/await

- ECMAScript 7 e 8 (2016 e 2017)
- Mesmo com Promises o código pode continuar bagunçado
- Facilita a visualização do fluxo das operações assíncronas, fazendo parecer uma execução síncrona
- Não altera a performance se usado corretamente
- É preciso trabalhar com Promises para usar async/await
Demonstração: refatorando implementação com Promises para usar async e await
Implementação com async/await

Async
- Async na frente faz com que a função retorne uma Promise
- A promise é resolvida quando a função retorna um valor
- A promise é rejeitada quanto a função lança uma exceção
Await
- A expressão await só pode ser usada dentro de uma função com async
- Pausa a execução de uma função assíncrona e aguarda a resolução da Promise, retornando o valor resolvido
- É preciso ter cuidado para não travar a execução de códigos que não devem ou não precisam aguardar a resolução de Promises
Código de exemplo

Manipulação de listas com for

Demonstração: comparação entre for, forin e forof
> npm init
> npm install axios
Lista de heróis: https://herois.getsandbox.com/herois
Manipulação de listas com map

Array.prototype.map
O método map() invoca a função callback passada por argumento para cada elemento do Array e devolve um novo Array como resultado.
Demonstração: trabalhando com map
Manipulação de listas com filter

Array.prototype.filter
O método filter() cria um novo array com todos os elementos que passaram no teste implementado pela função fornecida.
Demonstração: trabalhando com filter
Manipulação de listas com reduce

Array.prototype.reduce
O método reduce() executa uma função reducer (provida por você) para cada membro do array, resultando num único valor de retorno.
Demonstração: trabalhando com reduce
Exercício 1

Implementar a class UserDB emulando um banco de dados
Atributos:
- users deve conter a lista de usuários
O usuário deve possuir os atributos: name, login (id, único, obrigatório) e password (obrigatória)
Métodos (devem retornar Promises):
- create(user) deve adicionar usuário a lista
- read(query, fields) deve retornar todos os usuários que satisfazerem a query, se a query não for passada retornar todos os usuários, se o argumento fields for passado, retornar apenas os campos desejados
- update(user) deve encontrar e atualizar o usuário na lista
- delete(login) deve remover o usuário que possuir o login informado
Exercício 1 (Dicas)

Array.prototype.push(newItem) para adicionar items a uma lista
Object.prototype.keys(object) para obter uma lista de propriedades de um objeto
Object.prototype.assign(target, source) para copiar todos atributos de um objeto para outro
Array.prototype.splice(position, quantity) para remover itens de uma lista
NPM

- O Node é leve por oferecer apenas funções básicas e permitindo instalar pacotes conforme a necessidade do projeto
- NPM é um registro de pacotes Node e também um CLI
- Pacotes são aplicações JavaScript
- Os desenvolvedores podem gerenciar, instalar e publicar pacotes usando a CLI
- Existem pacotes que podem ser usados no browser, servidor ou linha de comando
- Permite versionar os pacotes
- Não é necessário incluir as bibliotecas no projeto, apenas mapear a dependência, e elas serão baixadas e incluídas no projeto
NPM

- Para pacotes com diferentes versões para SO o NPM irá instalar o pacote correto
- As dependências podem ter dependências
- Os pacotes instalados ficam na pasta node_modules e não devem ser versionados
- Um concorrente ao NPM é o Yarn https://yarnpkg.com/en/
- Yarn, mesmos pacotes, mas mais rápido?
Demonstração: Criando um projeto Node com NPM
> npm init
> npm init -y
> npm install request
> npm install -g yarn
Implementando uma CLI

Demonstração: Implementação de uma CLI para CRUD de usuários
> npm init
> npm install commander
Trabalhando com bancos de dados

Bancos de dados SQL
- Estrutura de dados fixa
- Modelagem estrutural no banco
- Menos flexibilidade
- Armazenamento em tabelas
- Fortes regras de validação (constraints)
- Maior confiabilidade e consistência
- Menor escalabilidade ou mais complexa, normalmente vertical
Bancos de dados NoSQL
- Estrutura dinâmica
- Aplicação modela os dados
- Maior flexibilidade
- Armazenamento em documentos (ou outras formas)
- Restrições fracas de validação
- Maior escalabilidade (horizontal) e performance
- Possibilidade de salvar dados embedded
Node.js + MongoDB

- Geralmente em aplicações Node.js usa-se MongoDB
- Não é uma regra, podendo-se usar qualquer banco
- Maior integração entre os dois por ambos usarem JavaScript
- Similaridade de paradigma
MongoDB

- https://www.mongodb.com/cloud
- Armazena as informações no formato de documentos Binary JSON (BSON)
- Os dados são agrupados na estrutura database > collections > documentos
- A manipulação dos dados e queries são feitas utilizando JavaScript

MongoDB, exemplos

mongo “mongodb+srv://usuario:senha@url/banco” // Conectar
show dbs // Lista databases no servidor
use dev // Indica qual banco usar
show collections // Mostra collections do banco atual
db.herois.insert({name: 'Superman'}) // Insere documento
db.herois.find() // Lista todos os documentos
db.herois.find().pretty() // Lista formatada
db.herois.update({_id: ObjectId("")}, {nome:'Batman'}) // Atualização completa do documento
db.herois.find({_id: ObjectId("")}) // Busca por query
db.herois.update({_id: ObjectId("")}, {$set: {nome:'Batman'}}) // Atualização de atributos específicos
db.herois.update({}, {$set: {nome:'Batman'}}) // Atualiza um
db.herois.update({}, {$set: {nome:'Batman'}}, {multi: true}) // Atualiza todos
db.herois.count() // Contador de documentos na collection
db.herois.findOne({nome: 'Batman'}) // Busca um
db.herois.find().forEach(function(heroi){print(heroi.poderes)}) // Execução de JavaScript
db.herois.find().map(heroi => heroi.nome); // Execução de JavaScript
db.herois.remove({}) // Remove todos documentos
Mongoose

- Biblioteca para modelagem de dados
- Permite, gerenciar conexões com o servidor do banco
- Permite gerenciar relacionamentos, validação e tradução de dados
- Schema: definição de atributos de um documento
- Model: definição de uma collection e seu schema
> npm install mongoose
Conexão com o banco:
mongodb+srv://guardiao:grayskull80@herois-dev-cyj9w.mongodb.net/dev?retryWrites=true&w=majority
Mongoose

Demonstração: primeiras operações com Mongoose
Demonstração: parte 2, implementação estruturada e padronizada
Exercício 2

Implementar um repositório TeamsRepository para armazenamento dos dados das equipes de heróis estendendo o repositório Mongo.
- Atributo name do tipo string
- Atributo members do tipo array de string, para salvar a lista dos ids dos membros
- Atributo publisher para salvar a editora
- Método addHeroToTeam(team, hero) para adicionar o herói na equipe
- Método removeHeroFromTeam(team, hero) para remover o herói da equipe
- Método byHero(heroId) que retorna as equipes que o herói faz parte
Exercício 2 (Dicas)

Operadores Mongo para Arrays:
https://docs.mongodb.com/manual/reference/operator/update-array/#update-operators
APIs RESTful

- API (Application Programming Interface)
- Rotinas e padrões documentados por uma aplicação que permitem outros sistemas se comunicarem com ela sem saber detalhes da implementação
- REST (Representational State Transfer)
- Princípios e regras que se seguidos permite a implementação de projetos com uma interface definida, padronizada
- Permite a integração entre sistemas pela Web, baseado em uma comunicação via rede
- Utilização da arquitetura e padrões da própria web, como o protocolo HTTP
URI's e recursos

- Uma API REST gerencia recursos: cliente, produto, página, imagem, arquivo
- Os recursos são identificados através da URI
- Não usar uma ação como parte da URI
- Manter um padrão (singular, plural, formato letras, etc)
- URI's legíveis e amigáveis
- Transformar ações em atributos quando necessário
-- Fazer --
http://api.com.br/usuarios
http://api.com.br/usuarios/{id}
http://api.com.br/usuarios/{id}/times/{id}
https://api.com.br/produtos/{id}/likes
-- Não fazer --
http://api.com.br/usuarios/criar
Postman

Ótima ferramenta para testar, gerenciar, automatizar ou documentar APIs
Métodos HTTP

Utilizar os métodos HTTP para indicar a ação a ser realizada:
- GET: solicita a representação de um recurso
- POST: submeter uma entidade a um recurso, criando essa entidade
- DELETE: remove um recurso específico
- PUT: substitui a representação do recurso pelo dado enviado na carga da solicitação
- PATCH: aplicar modificações parciais de um recurso
Códigos HTTP

Uma boa prática para informar o resultado da requisição
- 2xx Indica que a requisição foi processada com sucesso
- 3xx Indica ao cliente uma ação a ser tomada para que a requisição possa ser concluída
- 4xx Indica erro(s) na requisição causado(s) pelo cliente
- 5xx Indica que a requisição não foi concluída devido a erro(s) ocorrido(s) no servidor
Outros Padrões

- Stateless, é o cliente quem se identifica e mantém seus dados de identidade
- Não manter dados de autenticação em sessão
- Vários formatos de representação de recursos: JSON, XML, HTML, CSV, outros
- Definir qual conteúdo é aceito via cabeçalho accept
- Utilizar SSL/TLS para transporte da informação
Express

Um dos frameworks mais populares para criação de aplicações web e serviços em Node.js
- Funciona através de middlewares
- Simples e leve
É possível implementar uma aplicação sem usar um framework?
Demonstração: implementando uma rota usando o módulo http do Node.js
> npm install express
Estrutura de diretórios

.
├-- config
| ├- db.json
| ├- service.json
|
├-- routes
| ├- controllers
| ├- middlewares
| ├- routes.js
|
├-- services
| ├- serviceOne
| ├- serviceTwo
|
├-- db
| ├- models
| ├- migrations
|
├-- core
| ├- users.js
| ├- products.js
| ├- comments.js
|
├-- app.js
.
├-- config
| ├-- db.json
| ├-- service.json
|
├-- db
| ├-- connectionStuff
|
├-- users
| ├-- controllers # Lógica
| ├-- database
| ├-- model
| ├-- routes
| ├-- service
| ├-- index.js # Módulo
|
├-- app.js # Express
├-- main.js # Rede
Organização por função (responsabilidade) ou por funcionalidade (módulo)
Diretórios organizados por módulo

- Todos os arquivos de um assunto ficam no mesmo diretório
- Imports são mais curtos e simples
Nodemon

Automaticamente reinicia o app quando houverem modificações nos arquivos
> npm install --save-dev nodemon
"scripts": {
"dev": "nodemon main.js"
}
> npm run dev
API Heroes

Demonstração: Implementação da API Heroes, utilizando os padrões REST e boas práticas
Validação de dados

- Retornar respostas que deixem claro o que ocorreu durante o processamento da requisição
- Erros de autenticação
- Erros do servidor
- Erros de validação de dados
- Mensagem de sucesso
> npm install express-validator
Exercício 3

- Implementar e documentar rotas da API para o recurso teams (equipes dos heróis):
- GET /teams
- POST /teams
- PUT /teams/:id
- DELETE /teams/:id
- Implementar rota para obter equipes de um herói:
- GET /heroes/:id/teams/
- GET /heroes/:id/teams/:id
Autenticação com JWT

- JSON Web Token
- Padrão RFC 7519 para transmitir e armazenar objetos JSON de forma segura entre aplicações
- Header: identifica o tipo do token
- Payload: Informação a ser transmitida
- Signature: Concatenação do hash do header e payload com uma chave secreta
- Enviar token no Header Authorization com a flag Bearer
- Permite implementar APIs stateless apenas passando o token nas requisições
Autenticação com JWT

Demonstração: implementando autenticação com JWT
> npm install express-jwt
> npm install jsonwebtoken
Configurações por ambiente

- Configurações, não devem ficar fixas no código
- Geralmente são diferentes em cada ambiente
- Uma forma de tornar estas informações dinâmicas é através de variáveis de ambiente
> npm install dotenv
> npm install --save-dev cross-env
Swagger e documentação

- Documentar software é importante, APIs mais ainda
- Demonstrar quais recursos existem, quais ações são possíveis e quais são os parâmetros esperados
- Swagger, dentre outras utilidades, é uma ferramenta de interface para disponibilizar especificações de APIs
> npm install swagger-ui-express
Próximos passos

- Aprofundar o conhecimento sobre os tópicos apresentados durante o curso
- Testes unitários
- Node.js com bancos relacionais
- hapi.js
- Microserviços
- Typescript
Obrigado!
Node.js - Da introdução à APIs REST em produção
By Henrique Rotava
Node.js - Da introdução à APIs REST em produção
- 1,118