🚀🟢 1- Primeiro Projeto da Formação de NodeJs na Rocketseat. Inteiramente feito com NodeJS puro.
3 horas, 6 minutos e 43 segundos distribuídos em 25 aulas.
Nesse primeiro módulo iremos desenvolver uma API REST com Node.js focada nos fundamentos da tecnologia, sem frameworks ou bibliotecas externas. Aprenderemos sobre módulos internos do Node.js como HTTP, Crypto e File System e sobre fundamentos HTTP como requests, responses, headers, status code, route e query parameters, etc. Também daremos profundidade em Streams no Node.js e como aplicá-las para realizarmos operações assíncronas e parciais em nosso back-end.
O JavaScript tem dois padrões de importação, o primeiro deles é:]
- CommunJS:
const http = require('http')
- ESModule:
const http = require('http')
Por padrão, vem setado o CommunJS. Mas atualmente, o padrão mais utilizado é o ESModule
- Então, deve-se modificar o arquivo:
package.json
para settar o ESModule como padrão
'type': "ESModule"
Ao adicionar essa linha, o ESModule fica setado.
Para importar modulos padrões do node, como o http
comumente é usado node:
antes do modulo.
- Exemplo:
import http from 'node:http'
** só uma dica, o nome depois do import pode ser o que você quiser se for o padrão.
Criamos o nosso servidor node, mas ele não atualiza automaticamente quando há alguma mudança.
- Para melhorar isso, vamos fazer o node "assistir" o nosso server
Então, ao invez de rodar o servidor com node src/server.js
usamos o watch:
node --watch src/server.js
- Mas, para não ter que rodar esse mesmo comando sempre, vamos criar um comando no
package.json
:
"scripts": {
"dev": "node --watch src/server.js"
}
Agora, toda vez que quisermos usar o nosso script, rodamos npm run dev
no terminal
Rotas são os endpoints que a nossa aplicação backend pode ter, com diferentes funcionalidades para X assuntos.
Então, usando o exemplo da entidade Usuario, podemos ter rotas para:
- Criar Usuarios
- Listar de Usuarios
- Editar Usuarios
- Remover Usuarios
Requisições HTTP, dentre tantas coisas, tem duas caracteristicas
O Metodo de uma requisição HTTP é tudo que vem antes do servidor METODO http://localhost/url
Vamos falar melhor agora sobre cada metodo HTTP:
- GET => Buscar um recurso do backend
- POST => Criar um recurso no backend
- PUT => Atualizar um recurso completo no backend
- PATCH => Atualizar uma informação especifica de um recurso backend
- DELETE => Deletar um recurso do backend
A URL de uma requisição HTTP é tudo o que vem depois do servidor localhost:3333/url
Eu posso ter duas rotas no meu backend, com a mesma URL, mas com funções diferentes. O que as diferencia é o metodo que elas utilizam.
Exemplos:
GET /users
=> Buscando usuarios no BackendPOST /users
==> Criando usuario no BackendPUT /users
=> Atulizando usuario ao todo no BackendPATCH /users
=> Atualizando dado especifico de usuario no BackendDELETE /users
=> Deletando usuario do Backend
Agora na nossa aplicação, vamos começar a salvar dados de forma Statefull
A Statefull, depende das informações salvas em memoria para funcionar.
- Os dados são perdidos ao reiniciar
Já uma Stateless, não depende da memoria. Ela salva informações fora, em BDs, etc.
- Os dados não são perdios ao iniciar
Vamos criar um array para salvar os nossos usuarios
users = []
Agora, vou fazer para que quando uma Requisição POST seja feita, um usuario novo seja salvo.
ìf (method === 'POST' && url === '/users') {
users.push({
id: 1,
name: 'Pedro',
email: 'pedro@gmail.com'
})
return res.end('Criação de Usuario')
}
Agora, dese-jo mostrar essa lista em memoria com um usuario adicionado, como reposta de uma requisição GET.
if (method === 'GET' && url === '/users') {
return res.end(users)
}
string
ou buffer
Chamado de JavaScript Object Notation, ele é uma maneira de passar tipos de dados diferentes em uma string
if (method === 'POST' && url === '/users') {
return res.end(JSON.stringfy(users))
}
E, ao então fazer uma requisição GET
no servidor, temos:
[{"id": 1, "nome": "Pedro", email: "pedro@gmail.com"}]
Mas ainda da pra melhorar!
Como então podemos falar para o Frontend que o Backend esta devolvendo um JSON?
Para isso existem os headers
!
Cabeçalhos são metadados, para que tanto o Back quanto o Front saibam lidar com X requisição de determinada forma.
Ele basicamente diz, como o dado retornado deve ser interpretado, mas não o modifica.
Vamos definir a nossa resposta do servidor na requisição do metodo GET
e url /users
como JSON
Para isso, precisamos adicionar o header Content-Type
com o valor application/json
if(method === 'GET' && url === '/users'){
return res
.setHeader('Content-Type', 'application/json')
.end(JSON.stringfy(users))
}
[
{
"id": 1,
"name": "Pedro",
"email": "pedro@gmail.com"
}
]
✅ Podemos ter headers sendo enviados do backend ao -> frontend, como vimos em res.setHeader
✅ Ou também do frontend ao -> backend, como em header = req.headers
Os HTTP Status code são formas de dizer qual foi o resultado da requisição requerida.
Definindo o metodo certo para a criação de um novo recurso.
res.statusCode(201).end()
Caso a requisição não entre em nenhum de nossos if's, vamos dizer que ela foi uma má requisição
res.writeHead(404).end()
Streams nada mais são, do que maneiras de fazer uma ação grande, que demandaria muito tempo paraser executada, de pouquinho em poquinho.
- Eu leio um upload aos poucos. Processando os seus dados, enquanto ele ainda é enviado.
Cliente enviando aos poucos uma informação pro Servidor
- Exemplo de usuario enviando um arquivo CSV de 1gb, o servidor lê e ja processa ele em 10mb/s
Servidor aos poucos uma informação ao Cliente
- Exemplo de um vídeo da netflix. O servidor envia ele aos poucos ao cliente assistindo
Vamos começar a criar as nossas próprias Streams, as nossas proprias ferramentas que deixam que façamos aos poucos ao inves de fazer tudo de uma vez.
Vamos começar importanto o nosso primeiro tipo de Stream, o Readable
import { Readable } from 'node:stream'
Então, eu construi uma Stream que vai contando de 1 até 100, respeitando um tempo.
Primeira coisa que preciso entender, é que Streams são classes. Então, quando eu quiser criar uma Stream, eu preciso herdar da classe pai Readable ou Writable
que são os tipos de Stream.
class OneToHundredStream extends Readable {
index = 1
_read() {
const i = this.index++
setTimeout(() => {
if (i > 100) {
this.push(null)
} else {
const buf = Buffer.from(String(i))
this.push(buf)
}
}, 1000)
}
}
new OneToHundredStream()
.pipe(process.stdout)
O que acontece é. Readable quando criados ja utilizam do seu metodo _read()
- Então, setamos para que esse metodo seja esse contador de numeros
O ponto aqui é, eu consigo já mostrar na tela o numero 10, antes do 99 ter sido carregado.