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

Link da apresentação: https://slides.com/rotava/node/live

 

Link do 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

- https://www.npmjs.com

- 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)

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

Link: https://www.getpostman.com/

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

https://http.cat/

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

- https://jwt.io/

 

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!

Made with Slides.com