├── .gitignore
├── book
├── .nojekyll
├── favicon.png
├── FontAwesome
│ └── fonts
│ │ ├── FontAwesome.ttf
│ │ ├── fontawesome-webfont.eot
│ │ ├── fontawesome-webfont.ttf
│ │ ├── fontawesome-webfont.woff
│ │ └── fontawesome-webfont.woff2
├── css
│ ├── print.css
│ ├── general.css
│ ├── variables.css
│ └── chrome.css
├── ayu-highlight.css
├── highlight.css
├── tomorrow-night.css
├── clipboard.min.js
├── node_requisicoes_delecao_de_produto.html
├── vamos_fazer_requisicoes_via_node.html
├── node_requisicoes_edicao_de_produto.html
├── node_requisicoes_somente_um_produto.html
├── o_que_e_uma_sdk.html
└── o_que_e_um_servidor_web_http.html
├── book.toml
├── request
├── setup.js
├── package.json
├── auth.js
├── listagem_produtos.js
├── criacao_produto.js
└── package-lock.json
├── api
├── setup.js
├── package.json
├── auth.js
├── criacao_produto.js
├── listagem_produtos.js
├── buscar_um_produto.js
├── edicao_produto.js
└── delecao_produto.js
├── src
├── vamos_fazer_requisicoes_via_node.md
├── node_requisicoes_edicao_de_produto.md
├── node_requisicoes_delecao_de_produto.md
├── node_requisicoes_somente_um_produto.md
├── o_que_e_um_servidor_web_http.md
├── como_e_a_dinamica_da_comunicacao_http.md
├── o_que_costuma_girar_entorno_de_servidores_http.md
├── o_que_e_uma_sdk.md
├── vamos_colocar_a_mao_na_massa_e_criar_um_servidor.md
├── node_requisicoes_listagem_de_produtos.md
├── node_requisicoes_autenticacao.md
├── vamos_criar_uma_simples_api_em_memoria.md
├── api_setup.md
├── node_requisicoes_setup.md
├── SUMMARY.md
├── usando_corpo_body_e_json.md
├── qual_a_ideia_do_livro.md
├── api_buscar_somente_um_produto.md
├── quais_sao_os_dados_trafegados_no_protocolo_http.md
├── api_delecao_de_produto.md
├── api_edicao_de_produto.md
├── api_listagem_de_produtos.md
├── api_autenticacao.md
├── node_requisicoes_criacao_de_produto.md
├── usando_o_express.md
├── criando_um_servidor_com_o_modulo_http_do_node.md
└── api_criacao_de_produto.md
├── servidor
├── usando_body.js
├── package.json
├── pacote_express.js
└── modulo_http.js
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | */node_modules
2 |
--------------------------------------------------------------------------------
/book/.nojekyll:
--------------------------------------------------------------------------------
1 | This file makes sure that Github Pages doesn't process mdBook's output.
--------------------------------------------------------------------------------
/book/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evaporei/livro-desenvolvimento-web-basico/HEAD/book/favicon.png
--------------------------------------------------------------------------------
/book.toml:
--------------------------------------------------------------------------------
1 | [book]
2 | authors = ["Otávio Pace"]
3 | multilingual = false
4 | src = "src"
5 | title = "livro-desenvolvimento-web-basico"
6 |
--------------------------------------------------------------------------------
/book/FontAwesome/fonts/FontAwesome.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evaporei/livro-desenvolvimento-web-basico/HEAD/book/FontAwesome/fonts/FontAwesome.ttf
--------------------------------------------------------------------------------
/book/FontAwesome/fonts/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evaporei/livro-desenvolvimento-web-basico/HEAD/book/FontAwesome/fonts/fontawesome-webfont.eot
--------------------------------------------------------------------------------
/book/FontAwesome/fonts/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evaporei/livro-desenvolvimento-web-basico/HEAD/book/FontAwesome/fonts/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/book/FontAwesome/fonts/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evaporei/livro-desenvolvimento-web-basico/HEAD/book/FontAwesome/fonts/fontawesome-webfont.woff
--------------------------------------------------------------------------------
/book/FontAwesome/fonts/fontawesome-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evaporei/livro-desenvolvimento-web-basico/HEAD/book/FontAwesome/fonts/fontawesome-webfont.woff2
--------------------------------------------------------------------------------
/request/setup.js:
--------------------------------------------------------------------------------
1 | const axios = require('axios')
2 |
3 | const BASE_URL = 'http://localhost:8000'
4 |
5 | async function run () {
6 | // ...
7 | }
8 |
9 | run()
10 |
--------------------------------------------------------------------------------
/api/setup.js:
--------------------------------------------------------------------------------
1 | const express = require('express')
2 | const bodyParser = require('body-parser')
3 | const PORT = 8000
4 |
5 | const app = express()
6 |
7 | app.use(bodyParser.json())
8 |
9 | app.listen(PORT)
10 |
--------------------------------------------------------------------------------
/src/vamos_fazer_requisicoes_via_node.md:
--------------------------------------------------------------------------------
1 | # Vamos fazer requisições via Node.js!
2 |
3 | Iremos usar da API que construímos no capítulo anterior e iremos fazer várias requisições a ela, mas ao invés de usar o `Postman`, vamos usar `Node.js`.
4 |
5 | Logo, o conteúdo desse capítulo depende do anterior.
6 |
--------------------------------------------------------------------------------
/servidor/usando_body.js:
--------------------------------------------------------------------------------
1 | const express = require('express')
2 | const bodyParser = require('body-parser')
3 | const PORT = 8000
4 |
5 | const app = express()
6 |
7 | app.use(bodyParser.json())
8 |
9 | app.post('/', function (req, res) {
10 | res.send(req.body)
11 | })
12 |
13 | app.listen(PORT)
14 |
--------------------------------------------------------------------------------
/src/node_requisicoes_edicao_de_produto.md:
--------------------------------------------------------------------------------
1 | # Edição de produto
2 |
3 | Bom, como agora já sabe como fazer as requisições e ler os dados da resposta, tudo que precisa fazer para a edição é trocar o método para `PATCH` e passar o `id` na `url`.
4 |
5 | Já que é muito similar, não irei mostrar aqui como é feito.
6 |
--------------------------------------------------------------------------------
/src/node_requisicoes_delecao_de_produto.md:
--------------------------------------------------------------------------------
1 | # Deleção de produto
2 |
3 | Bom, como agora já sabe como fazer as requisições e ler os dados da resposta, tudo que precisa fazer para a edição é trocar o método para `DELETE` e remover o `body`/corpo (campo `data`).
4 |
5 | Já que é muito similar, não irei mostrar aqui como é feito.
6 |
--------------------------------------------------------------------------------
/request/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "request",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "dependencies": {
12 | "axios": "^0.19.0"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/node_requisicoes_somente_um_produto.md:
--------------------------------------------------------------------------------
1 | # Buscar somente um produto
2 |
3 | Bom, como agora já sabe como fazer as requisições e ler os dados da resposta, tudo que precisa fazer para a edição é trocar o método para `GET`, remover o `body`/corpo (campo `data`), e passar o `id` na `url`.
4 |
5 | Já que é muito similar, não irei mostrar aqui como é feito.
6 |
--------------------------------------------------------------------------------
/servidor/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "servidor",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "dependencies": {
12 | "body-parser": "^1.18.3",
13 | "express": "^4.16.4"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/api/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "api",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "setup.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "dependencies": {
12 | "body-parser": "^1.18.3",
13 | "cuid": "^2.1.6",
14 | "express": "^4.16.4"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/request/auth.js:
--------------------------------------------------------------------------------
1 | const axios = require('axios')
2 |
3 | const BASE_URL = 'http://localhost:8000'
4 |
5 | async function run () {
6 | const authResponse = await axios({
7 | baseURL: BASE_URL,
8 | url: '/auth',
9 | method: 'POST',
10 | data: {
11 | username: 'admin',
12 | password: '1234'
13 | }
14 | })
15 |
16 | console.log('authResponse body:', authResponse.data)
17 | }
18 |
19 | run()
20 |
--------------------------------------------------------------------------------
/servidor/pacote_express.js:
--------------------------------------------------------------------------------
1 | const express = require('express')
2 | const PORT = 8000
3 |
4 | const application = express()
5 |
6 | application.get('/', function (request, response) {
7 | console.log('URL:', request.url)
8 | console.log('Método:', request.method)
9 | console.log('Headers:', request.headers)
10 | response.status(200).send('Olá!')
11 | })
12 |
13 | application.disable('x-powered-by')
14 |
15 | application.listen(PORT)
16 |
--------------------------------------------------------------------------------
/servidor/modulo_http.js:
--------------------------------------------------------------------------------
1 | const http = require('http')
2 | const PORT = 8000
3 |
4 | const requestHandler = (request, response) => {
5 | console.log('URL:', request.url)
6 | console.log('Método:', request.method)
7 |
8 | response.write('Hello World Node.js HTTP server!')
9 | response.end()
10 | }
11 |
12 | const server = http.createServer(requestHandler)
13 |
14 | server.listen(PORT, function (err) {
15 | if (err) {
16 | return console.log('Algo de ruim aconteceu, erro:', err)
17 | }
18 |
19 | console.log(`Servidor escutando na porta: ${PORT}`)
20 | })
21 |
--------------------------------------------------------------------------------
/api/auth.js:
--------------------------------------------------------------------------------
1 | const express = require('express')
2 | const bodyParser = require('body-parser')
3 | const cuid = require('cuid')
4 | const PORT = 8000
5 |
6 | const app = express()
7 |
8 | app.use(bodyParser.json())
9 |
10 | const tokens = []
11 |
12 | app.post('/auth', function (req, res) {
13 | if (req.body.username == 'admin' && req.body.password == '1234') {
14 | const token = cuid()
15 |
16 | tokens.push(token)
17 |
18 | res.status(201).send({
19 | token: token
20 | })
21 | } else {
22 | res.status(401).send({
23 | message: 'Unauthorized'
24 | })
25 | }
26 | })
27 |
28 | app.listen(PORT)
29 |
--------------------------------------------------------------------------------
/request/listagem_produtos.js:
--------------------------------------------------------------------------------
1 | const axios = require('axios')
2 |
3 | const BASE_URL = 'http://localhost:8000'
4 |
5 | async function run () {
6 | const authResponse = await axios({
7 | baseURL: BASE_URL,
8 | url: '/auth',
9 | method: 'POST',
10 | data: {
11 | username: 'admin',
12 | password: '1234'
13 | }
14 | })
15 |
16 | const TOKEN = authResponse.data.token
17 |
18 | const listProductsResponse = await axios({
19 | baseURL: BASE_URL,
20 | url: '/products',
21 | method: 'GET',
22 | headers: {
23 | authorization: `Bearer ${TOKEN}`
24 | }
25 | })
26 |
27 | console.log('listProductsResponse body:', listProductsResponse.data)
28 | }
29 |
30 | run()
31 |
--------------------------------------------------------------------------------
/src/o_que_e_um_servidor_web_http.md:
--------------------------------------------------------------------------------
1 | # O que é um servidor Web HTTP?
2 |
3 | Imagine um programa, que aguarda conexões chegarem a ele, em uma porta específica do computador.
4 |
5 | Portas são apenas entradas do mundo externo para com o computador e os programas que rodam nele.
6 |
7 | Ou seja, é possível que sejam iniciados por exemplo 3 servidores, cada um escutando a uma porta distinta no computador, a qual irá poder falar com o mundo a fora. É claro que podem ser bem mais, ou menos, isso é só um exemplo.
8 |
9 | Exemplo de computador/servidor com portas e programas:
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/como_e_a_dinamica_da_comunicacao_http.md:
--------------------------------------------------------------------------------
1 | # Como é a dinâmica da comunicação HTTP?
2 |
3 | Existem dois players, o cliente e o servidor. O servidor aguarda por requisições de clientes. E os clientes iniciam a conexão criando uma requisição ao servidor.
4 |
5 | Imagens de exemplo:
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | Esse fluxo é **síncrono**, ou seja, a ideia é que quando o cliente inicia uma conexão, ele irá esperar até sua resposta chegar. Caso ela não chegue no tempo esperado, a requisição pode ser terminada.
20 |
--------------------------------------------------------------------------------
/book/css/print.css:
--------------------------------------------------------------------------------
1 |
2 | #sidebar,
3 | #menu-bar,
4 | .nav-chapters,
5 | .mobile-nav-chapters {
6 | display: none;
7 | }
8 |
9 | #page-wrapper.page-wrapper {
10 | transform: none;
11 | margin-left: 0px;
12 | overflow-y: initial;
13 | }
14 |
15 | #content {
16 | max-width: none;
17 | margin: 0;
18 | padding: 0;
19 | }
20 |
21 | .page {
22 | overflow-y: initial;
23 | }
24 |
25 | code {
26 | background-color: #666666;
27 | border-radius: 5px;
28 |
29 | /* Force background to be printed in Chrome */
30 | -webkit-print-color-adjust: exact;
31 | }
32 |
33 | pre > .buttons {
34 | z-index: 2;
35 | }
36 |
37 | a, a:visited, a:active, a:hover {
38 | color: #4183c4;
39 | text-decoration: none;
40 | }
41 |
42 | h1, h2, h3, h4, h5, h6 {
43 | page-break-inside: avoid;
44 | page-break-after: avoid;
45 | }
46 |
47 | pre, code {
48 | page-break-inside: avoid;
49 | white-space: pre-wrap;
50 | }
51 |
52 | .fa {
53 | display: none !important;
54 | }
55 |
--------------------------------------------------------------------------------
/src/o_que_costuma_girar_entorno_de_servidores_http.md:
--------------------------------------------------------------------------------
1 | # O que costuma girar entorno de servidores HTTP?
2 |
3 | O servidor fica escutando por conexões em uma porta no computador. Todo computador tem um IP (216.3.128.12), que é um identificador único na internet. Geralmente se configura um DNS (www.google.com), que nada mais é que um texto que direciona para um IP ou um conjunto de IPs.
4 |
5 |
6 |
7 |
8 |
9 | É comum também utilizar de `load balancers` para ficarem na frente dos servidores.
10 |
11 | Um `load balancer`, nada mais é que um servidor como qualquer outro, cuja função é receber requisições e direcioná-las a outros servidores, dividindo a carga de uma forma específica (igualmente por exemplo).
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/request/criacao_produto.js:
--------------------------------------------------------------------------------
1 | const axios = require('axios')
2 |
3 | const BASE_URL = 'http://localhost:8000'
4 |
5 | function randomNumber () {
6 | return Math.floor(Math.random() * 1000)
7 | }
8 |
9 | async function run () {
10 | try {
11 | const authResponse = await axios({
12 | baseURL: BASE_URL,
13 | url: '/auth',
14 | method: 'POST',
15 | data: {
16 | username: 'admin',
17 | password: '1234'
18 | }
19 | })
20 |
21 | const TOKEN = authResponse.data.token
22 |
23 | const createProductResponse = await axios({
24 | baseURL: BASE_URL,
25 | url: '/products',
26 | method: 'POST',
27 | headers: {
28 | authorization: `Bearer ${TOKEN}`
29 | },
30 | data: {
31 | name: `boneco do goku ${randomNumber()}`,
32 | price: 55.6
33 | }
34 | })
35 |
36 | console.log('createProductResponse body:', createProductResponse.data)
37 | } catch (error) {
38 | console.log('error body:', error.response.data)
39 | }
40 | }
41 |
42 | run()
43 |
--------------------------------------------------------------------------------
/src/o_que_e_uma_sdk.md:
--------------------------------------------------------------------------------
1 | # O que é uma SDK?
2 |
3 | Agora que já fizemos um servidor, entendemos como ele funciona e como fazer requisições a ele via código, iremos entender o que é uma SDK.
4 |
5 | SDK é um kit de desenvolvimento para algo.
6 |
7 | Existem SDKs que seguem APIs HTTP, e outras que são simplesmente sobre funções/classes/etc de alguma biblioteca.
8 |
9 | No caso de SDKs de APIs HTTP, basicamente elas são `"wrappers"` (uma camada entorno) sobre as requisições HTTP.
10 |
11 | Exemplo, vamos supor que tenho uma incrível API de mapas, porém é muito complexo enviar os dados a minha API para cada requisição. O que eu posso fazer para facilitar a vida de quem consome minha API, é escrever uma SDK que contém funções que já deixam tudo pronto quanto ao `HTTP` e só retornam os dados de forma simples.
12 |
13 | Uma SDK é só uma abstração.
14 |
15 | Portanto, ao invés de em `Node.js` termos de interagir com o `axios`, poderíamos importar uma biblioteca fictícia chamada `products-sdk` que teria funções como `createProduct`. Ou então, tería uma classe `Product` que ao instanciar iria fazer a requisição a nossa API.
16 |
--------------------------------------------------------------------------------
/book/ayu-highlight.css:
--------------------------------------------------------------------------------
1 | /*
2 | Based off of the Ayu theme
3 | Original by Dempfi (https://github.com/dempfi/ayu)
4 | */
5 |
6 | .hljs {
7 | display: block;
8 | overflow-x: auto;
9 | background: #191f26;
10 | color: #e6e1cf;
11 | padding: 0.5em;
12 | }
13 |
14 | .hljs-comment,
15 | .hljs-quote,
16 | .hljs-meta {
17 | color: #5c6773;
18 | font-style: italic;
19 | }
20 |
21 | .hljs-variable,
22 | .hljs-template-variable,
23 | .hljs-attribute,
24 | .hljs-attr,
25 | .hljs-regexp,
26 | .hljs-link,
27 | .hljs-selector-id,
28 | .hljs-selector-class {
29 | color: #ff7733;
30 | }
31 |
32 | .hljs-number,
33 | .hljs-builtin-name,
34 | .hljs-literal,
35 | .hljs-type,
36 | .hljs-params {
37 | color: #ffee99;
38 | }
39 |
40 | .hljs-string,
41 | .hljs-bullet {
42 | color: #b8cc52;
43 | }
44 |
45 | .hljs-title,
46 | .hljs-built_in,
47 | .hljs-section {
48 | color: #ffb454;
49 | }
50 |
51 | .hljs-keyword,
52 | .hljs-selector-tag,
53 | .hljs-symbol {
54 | color: #ff7733;
55 | }
56 |
57 | .hljs-name {
58 | color: #36a3d9;
59 | }
60 |
61 | .hljs-tag {
62 | color: #00568d;
63 | }
64 |
65 | .hljs-emphasis {
66 | font-style: italic;
67 | }
68 |
69 | .hljs-strong {
70 | font-weight: bold;
71 | }
72 |
--------------------------------------------------------------------------------
/src/vamos_colocar_a_mao_na_massa_e_criar_um_servidor.md:
--------------------------------------------------------------------------------
1 | # Vamos colocar a mão na massa e criar um servidor!
2 |
3 | ## O que é necessário instalar?
4 |
5 | - Node.js ([link p/ download](https://nodejs.org/en/))
6 | - Postman ([link p/ download](https://www.getpostman.com/downloads/))
7 |
8 | ## Observações
9 |
10 | Todos os blocos assim:
11 |
12 | ```
13 | coisas a serem executadas
14 | no terminal
15 | linha a linha
16 | :)
17 | ```
18 |
19 | Será o que é preciso fazer no terminal (no Windows, PowerShell ou Prompt de Comando), como é descrito acima.
20 |
21 | Já para fazer requisições imitando um cliente, aí será pelo `Postman`.
22 |
23 | ## Setup do servidor
24 |
25 | Crie uma pasta qualquer no seu computador, aqui iremos criar uma chamada `servidor`.
26 |
27 | ```shell
28 | mkdir servidor
29 | ```
30 |
31 | Depois entre na pasta e execute `npm init`.
32 |
33 | ```shell
34 | cd servidor
35 |
36 | npm init
37 | ```
38 |
39 | O `npm` (node package manager) irá perguntar por várias coisas, no momento pode apenas pressionar `Enter` várias vezes até acabar.
40 |
41 | Mas caso não queria responder nenhuma pergunta e somente criar o arquivo package.json, você pode substituir o comando `npm` por `npm init -y`.
42 |
43 | Em cada sub capítulo iremos criar um novo arquivo `JavaScript` na mesma pasta, o qual iremos codificar em cima.
44 |
--------------------------------------------------------------------------------
/src/node_requisicoes_listagem_de_produtos.md:
--------------------------------------------------------------------------------
1 | # Listagem de produtos
2 |
3 | Para listar a única coisa que precisamos fazer é modificar o `method` de `POST` para `GET`, e remover o `body`/corpo (campo `data`).
4 |
5 | ```javascript
6 | const axios = require('axios')
7 |
8 | const BASE_URL = 'http://localhost:8000'
9 |
10 | async function run () {
11 | const authResponse = await axios({
12 | baseURL: BASE_URL,
13 | url: '/auth',
14 | method: 'POST',
15 | data: {
16 | username: 'admin',
17 | password: '1234'
18 | }
19 | })
20 |
21 | const TOKEN = authResponse.data.token
22 |
23 | const listProductsResponse = await axios({
24 | baseURL: BASE_URL,
25 | url: '/products',
26 | method: 'GET',
27 | headers: {
28 | authorization: `Bearer ${TOKEN}`
29 | }
30 | })
31 |
32 | console.log('listProductsResponse body:', listProductsResponse.data)
33 | }
34 |
35 | run()
36 | ```
37 |
38 |
44 |
--------------------------------------------------------------------------------
/src/vamos_criar_uma_simples_api_em_memoria.md:
--------------------------------------------------------------------------------
1 | # Vamos criar uma simples API em memória!
2 |
3 | O que iremos construir?
4 |
5 | Um servidor HTTP de produtos. Teremos 6 rotas, são elas:
6 |
7 | - Autenticação (`POST /auth`)
8 | - Criação de produto (`POST /products`)
9 | - Listagem de produtos (`GET /products`)
10 | - Busca de um produto por id (`GET /products/:id`)
11 | - Edição de um produto (`PATCH /products/:id`)
12 | - Deleção de um produto (`DELETE /products/:id`)
13 |
14 | Como assim em memória?
15 |
16 | Quando o servidor for desligado, todos os dados serão perdidos.
17 |
18 | Mas por que fazer assim?
19 |
20 | Simplesmente por simplicidade. Lembre-se que tudo que salvarmos na memória, poderia simplesmente estar em um banco de dados.
21 |
22 | Bora!
23 |
24 | ## Observações importantes
25 |
26 | ### JSON
27 |
28 | Eu não explico no livro o que é `JSON`, recomendo pesquisar sobre, aqui está um artigo que talvez possa ajudar: [artigo Medium JSON](https://medium.com/clebertech/o-que-%C3%A9-json-daaa9311e929).
29 |
30 | Em poucas palavras é um formato de dados semelhante ao `XML`, que está ganhando mais popularidade.
31 |
32 | ### Persistência
33 |
34 | Uma coisa que não estou fazendo nesse tutorial é a comunicação com o banco de dados, como disse anteriormente, será tudo na memória.
35 |
36 | Isso **não** é a forma que as pessoas fazem para persistir dados em produção! Geralmente quando se quer armazenar dados, se utiliza de algo que não irá se perder ao terminar o programa.
37 |
--------------------------------------------------------------------------------
/request/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "request",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "axios": {
8 | "version": "0.19.0",
9 | "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.0.tgz",
10 | "integrity": "sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ==",
11 | "requires": {
12 | "follow-redirects": "1.5.10",
13 | "is-buffer": "^2.0.2"
14 | }
15 | },
16 | "debug": {
17 | "version": "3.1.0",
18 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
19 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
20 | "requires": {
21 | "ms": "2.0.0"
22 | }
23 | },
24 | "follow-redirects": {
25 | "version": "1.5.10",
26 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
27 | "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
28 | "requires": {
29 | "debug": "=3.1.0"
30 | }
31 | },
32 | "is-buffer": {
33 | "version": "2.0.3",
34 | "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz",
35 | "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw=="
36 | },
37 | "ms": {
38 | "version": "2.0.0",
39 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
40 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/api_setup.md:
--------------------------------------------------------------------------------
1 | # Setup
2 |
3 | Recomendo criar uma outra pasta somente para esse projeto.
4 |
5 | Execute `npm init` e pode pressionar `Enter` até as opções do `npm` acabarem.
6 |
7 | ## Dependências
8 |
9 | Iremos precisar somente do `express`, e do `body-parser`. Para instalá-las:
10 |
11 | ```shell
12 | npm install --save express body-parser
13 | ```
14 |
15 | Esse será o servidor base para o projeto:
16 |
17 | ```javascript
18 | const express = require('express')
19 | const bodyParser = require('body-parser')
20 | const PORT = 8000
21 |
22 | const app = express()
23 |
24 | app.use(bodyParser.json())
25 |
26 | app.listen(PORT)
27 | ```
28 |
29 | ## Facilitando o desenvolvimento
30 |
31 | Essa parte é opcional, mas irá facilitar o desenvolvimento.
32 |
33 | Existe uma ferramenta de linha de comando chamada `nodemon` a qual executa o script Node.js passado por argumento (`nodemon arquivo.js`), e toda vez que o arquivo for alterado, o script é iniciado novamente com o código novo.
34 |
35 | Isso facilita ter de ficar parando o servidor no terminal com `Ctrl + C` e o iniciando novamente, a cada alteração no código.
36 |
37 | Para instalar o `nodemon` utilize `npm install --global nodemon`, após executar isso, terá disponível o comando no terminal, assim como já possuia o `node`.
38 |
39 | ## Notas
40 |
41 | Todo o código do servidor estará não só aqui nos capítulos do livro, como na pasta [api do repositório no Github](https://github.com/otaviopace/livro-desenvolvimento-web-basico/tree/master/api).
42 |
43 |
46 |
--------------------------------------------------------------------------------
/src/node_requisicoes_setup.md:
--------------------------------------------------------------------------------
1 | # Setup
2 |
3 | Recomendo criar uma outra pasta somente para esse projeto.
4 |
5 | Execute `npm init` e pode pressionar `Enter` até as opções do `npm` acabarem.
6 |
7 | ## Dependências
8 |
9 | Iremos precisar somente do `axios`, ela é uma biblioteca/pacote que facilita a fazer requisições HTTP. Para instalá-lo:
10 |
11 | ```shell
12 | npm install --save axios
13 | ```
14 |
15 | Esse será o arquivo base para o projeto:
16 |
17 | ```javascript
18 | const axios = require('axios')
19 |
20 | const BASE_URL = 'http://localhost:8000'
21 |
22 | async function run () {
23 | // ...
24 | }
25 |
26 | run()
27 | ```
28 |
29 | Todo o nosso código ficará dentro da função assíncrona `run`.
30 |
31 | ## Facilitando o desenvolvimento
32 |
33 | Essa parte é opcional, mas irá facilitar o desenvolvimento.
34 |
35 | Existe uma ferramenta de linha de comando chamada `nodemon` a qual executa o script Node.js passado por argumento (`nodemon arquivo.js`), e toda vez que o arquivo for alterado, o script é iniciado novamente com o código novo.
36 |
37 | Isso facilita ter de ficar parando o servidor no terminal com `Ctrl + C` e o iniciando novamente, a cada alteração no código.
38 |
39 | Para instalar o `nodemon` utilize `npm install --global nodemon`, após executar isso, terá disponível o comando no terminal, assim como já possuia o `node`.
40 |
41 | ## Notas
42 |
43 | Todo o código do servidor estará não só aqui nos capítulos do livro, como na pasta [api do repositório no Github](https://github.com/otaviopace/livro-desenvolvimento-web-basico/tree/master/request).
44 |
45 |
74 |
--------------------------------------------------------------------------------
/api/criacao_produto.js:
--------------------------------------------------------------------------------
1 | const express = require('express')
2 | const bodyParser = require('body-parser')
3 | const cuid = require('cuid')
4 | const PORT = 8000
5 |
6 | const app = express()
7 |
8 | app.use(bodyParser.json())
9 |
10 | const tokens = []
11 |
12 | app.post('/auth', function (req, res) {
13 | if (req.body.username == 'admin' && req.body.password == '1234') {
14 | const token = cuid()
15 |
16 | tokens.push(token)
17 |
18 | res.status(201).send({
19 | token: token
20 | })
21 | } else {
22 | res.status(401).send({
23 | message: 'Unauthorized'
24 | })
25 | }
26 | })
27 |
28 | const products = []
29 |
30 | function authorizationMiddleware (req, res, next) {
31 | const authorizationHeader = req.get('authorization')
32 |
33 | if (!authorizationHeader) {
34 | return res.status(401).send({ message: 'Authorization header missing' })
35 | }
36 |
37 | const authorizationHeaderParts = authorizationHeader.split(' ')
38 |
39 | const bearer = authorizationHeaderParts[0]
40 | const token = authorizationHeaderParts[1]
41 |
42 | if (bearer != 'Bearer') {
43 | return res.status(401).send({ message: 'Authorization header needs the Bearer prefix' })
44 | }
45 |
46 | const isValidToken = tokens.find(function (validToken) {
47 | return validToken == token
48 | })
49 |
50 | if (!isValidToken) {
51 | return res.status(401).send({ message: 'Authorization header is not valid' })
52 | }
53 |
54 | next()
55 | }
56 |
57 | app.post('/products', authorizationMiddleware, function (req, res) {
58 | if (req.body.name == null) {
59 | return res.status(400).send({
60 | message: 'The product name is missing'
61 | })
62 | }
63 |
64 | if (req.body.price == null) {
65 | return res.status(400).send({
66 | message: 'The product price is missing'
67 | })
68 | }
69 |
70 | const productAlreadyExists = products.find(function (product) {
71 | return product.name == req.body.name
72 | })
73 |
74 | if (productAlreadyExists) {
75 | return res.status(400).send({
76 | message: 'This product name already exists'
77 | })
78 | }
79 |
80 | const newProduct = {
81 | id: cuid(),
82 | name: req.body.name,
83 | price: req.body.price
84 | }
85 |
86 | products.push(newProduct)
87 |
88 | res.status(201).send(newProduct)
89 | })
90 |
91 | app.listen(PORT)
92 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # livro-desenvolvimento-web-basico
2 |
3 | ## Qual a ideia do livro?
4 |
5 | Mostrar com termos pouco técnicos e simples:
6 |
7 | - O que é um servidor Web HTTP
8 | - Quais são os principais dados trafegados nesse tipo de conexão
9 | - Como é o modelo de comunicação servidor e cliente do HTTP
10 | - Como construir um servidor simples em Node.js que lê os dados recebidos pelo cliente
11 | - Como construir uma API em Node.js em memória
12 | - Como consumir essa API HTTP via Node.js
13 | - O que é uma SDK
14 |
15 | ## Como contribuir
16 |
17 | Esse livro foi escrito usando uma ferramenta chamada `mdbook` que transforma texto de `Markdown` para `HTML`. Link para baixá-la: https://github.com/rust-lang-nursery/mdBook/releases.
18 |
19 | ### Estrutura do projeto
20 |
21 | ```
22 | livro-desenvolvimento-web-basico
23 | │ README.md
24 | │ .gitignore
25 | │
26 | └───api
27 | │ └─── Código do projeto em que uma API é feita
28 | │
29 | └───book
30 | │ └─── Livro em HTML
31 | │
32 | └───request
33 | │ └─── Código do projeto em que requisições são feitas
34 | │
35 | └───servidor
36 | │ └─── Código do projeto em que um servidor HTTP é feito
37 | │
38 | └───src
39 | └─── Livro em Markdown
40 | ```
41 |
42 | Para contribuir no texto do livro, deve-se escrever `Markdown` dentro da pasta `src`, para então usar o `mdbook`, o qual irá converter isso em `HTML`, que é a versão final do livro.
43 |
44 | Caso não queira ou não possa por quaisquer motivos instalar o `mdbook` em seu computador, não tem problema, você pode criar um `Pull Request` que muda apenas a pasta `src`, para que então eu aplique as mudanças usando a ferramenta, a qual irá popular a pasta `book`.
45 |
46 | ### Gerando o HTML através do `mdbook`
47 |
48 | Depois que tiver o `mdbook` instalado na sua máquina e tiver modificado o `Markdown` na pasta `src`, pode ir no seu `Terminal` de preferência, entrar na pasta do projeto (`cd livro-desenvolvimento-web-basico`), e executar `mdbook build`.
49 |
50 | Voila! Agora os arquivos da pasta `book` já estarão com o `HTML` modificado :wink: Só falta fazer o commit com eles!
51 |
52 | ## Observações
53 |
54 | Esse é um livro que fiz para um amigo meu entender mais sobre o protocólo HTTP e Node.js. Esse projeto não tem fins lucrativos, apenas educacionais. Pode ser e é bem provável que eu tenha cometido vários erros de português durante o livro, se quiser corrigir, por favor se sinta a vontade para contribuir, seja criando uma `Issue` ou `Pull Request` no projeto do [Github](https://github.com/otaviopace/livro-desenvolvimento-web-basico).
55 |
--------------------------------------------------------------------------------
/src/qual_a_ideia_do_livro.md:
--------------------------------------------------------------------------------
1 | # livro-desenvolvimento-web-basico
2 |
3 | ## Qual a ideia do livro?
4 |
5 | Mostrar com termos pouco técnicos e simples:
6 |
7 | - O que é um servidor Web HTTP
8 | - Quais são os principais dados trafegados nesse tipo de conexão
9 | - Como é o modelo de comunicação servidor e cliente do HTTP
10 | - Como construir um servidor simples em Node.js que lê os dados recebidos pelo cliente
11 | - Como construir uma API em Node.js em memória
12 | - Como consumir essa API HTTP via Node.js
13 | - O que é uma SDK
14 |
15 | ## Como contribuir
16 |
17 | Esse livro foi escrito usando uma ferramenta chamada `mdbook` que transforma texto de `Markdown` para `HTML`. Link para baixá-la: https://github.com/rust-lang-nursery/mdBook/releases.
18 |
19 | ### Estrutura do projeto
20 |
21 | ```
22 | livro-desenvolvimento-web-basico
23 | │ README.md
24 | │ .gitignore
25 | │
26 | └───api
27 | │ └─── Código do projeto em que uma API é feita
28 | │
29 | └───book
30 | │ └─── Livro em HTML
31 | │
32 | └───request
33 | │ └─── Código do projeto em que requisições são feitas
34 | │
35 | └───servidor
36 | │ └─── Código do projeto em que um servidor HTTP é feito
37 | │
38 | └───src
39 | └─── Livro em Markdown
40 | ```
41 |
42 | Para contribuir no texto do livro, deve-se escrever `Markdown` dentro da pasta `src`, para então usar o `mdbook`, o qual irá converter isso em `HTML`, que é a versão final do livro.
43 |
44 | Caso não queira ou não possa por quaisquer motivos instalar o `mdbook` em seu computador, não tem problema, você pode criar um `Pull Request` que muda apenas a pasta `src`, para que então eu aplique as mudanças usando a ferramenta, a qual irá popular a pasta `book`.
45 |
46 | ### Gerando o HTML através do `mdbook`
47 |
48 | Depois que tiver o `mdbook` instalado na sua máquina e tiver modificado o `Markdown` na pasta `src`, pode ir no seu `Terminal` de preferência, entrar na pasta do projeto (`cd livro-desenvolvimento-web-basico`), e executar `mdbook build`.
49 |
50 | Voila! Agora os arquivos da pasta `book` já estarão com o `HTML` modificado :wink: Só falta fazer o commit com eles!
51 |
52 | ## Observações
53 |
54 | Esse é um livro que fiz para um amigo meu entender mais sobre o protocólo HTTP e Node.js. Esse projeto não tem fins lucrativos, apenas educacionais. Pode ser e é bem provável que eu tenha cometido vários erros de português durante o livro, se quiser corrigir, por favor se sinta a vontade para contribuir, seja criando uma `Issue` ou `Pull Request` no projeto do [Github](https://github.com/otaviopace/livro-desenvolvimento-web-basico).
55 |
--------------------------------------------------------------------------------
/src/api_buscar_somente_um_produto.md:
--------------------------------------------------------------------------------
1 | # Buscar somente um produto
2 |
3 | Esse capítulo toma como base o código do capítulo de listagem de produtos.
4 |
5 | ## Fluxo da informação
6 |
7 |
143 |
--------------------------------------------------------------------------------
/src/api_edicao_de_produto.md:
--------------------------------------------------------------------------------
1 | # Edição de produto
2 |
3 | Esse capítulo toma como base o código do capítulo de busca de um produto.
4 |
5 | ## Fluxo da informação
6 |
7 |
100 |
101 | Já existe um produto cadastrado com o mesmo nome. Isso acontece pois nunca mudamos o nome no script.
102 |
103 | Poderiamos trocar o nome a cada execução do script, mas isso é muito trabalhoso, por isso iremos adicionar uma função que coloca um número aleatório até 1000 final do texto.
104 |
105 | Dessa forma:
106 |
107 | ```javascript
108 | // ...
109 | function randomNumber () {
110 | return Math.floor(Math.random() * 1000)
111 | }
112 |
113 | async function run () {
114 | const authResponse = await axios({
115 | baseURL: BASE_URL,
116 | url: '/auth',
117 | method: 'POST',
118 | data: {
119 | username: 'admin',
120 | password: '1234'
121 | }
122 | })
123 |
124 | const TOKEN = authResponse.data.token
125 |
126 | const createProductResponse = await axios({
127 | baseURL: BASE_URL,
128 | url: '/products',
129 | method: 'POST',
130 | headers: {
131 | authorization: `Bearer ${TOKEN}`
132 | },
133 | data: {
134 | name: `boneco do goku ${randomNumber()}`,
135 | price: 55.6
136 | }
137 | })
138 |
139 | console.log('createProductResponse body:', createProductResponse.data)
140 | }
141 |
142 | // ...
143 | ```
144 |
145 |
146 |
147 |
148 |
149 | Voilá! Agora podemos executar indeterminadas (até ter colisão, ou chegarmos a 1000 produtos, um para cada número) vezes que irá funcionar! E sim, poderíamos usar algo como a biblioteca `cuid` aqui, a qual usamos na nossa API.
150 |
151 |
154 |
--------------------------------------------------------------------------------
/src/usando_o_express.md:
--------------------------------------------------------------------------------
1 | # Usando o pacote express
2 |
3 | Bom, dessa vez iremos usar uma biblioteca/pacote que facilita a criação de servidores HTTP, o nome dele é `express`.
4 |
5 | > Obs: uma biblioteca/pacote nada mais é do que um conjunto de código publicado
6 |
7 | Para instalá-lo rode `npm install --save express`.
8 |
9 | ```shell
10 | npm install --save express
11 | ```
12 |
13 | Crie um arquivo `JavaScript` que será o ponto de entrada da nossa aplicação/servidor Express.
14 |
15 | ```shell
16 | # cria o arquivo principal
17 | touch pacote_express.js
18 | ```
19 |
20 | Abaixo está uma aplicação que se comporta de forma extremamente similar a última que codificamos.
21 |
22 | - pacote_express.js
23 | ```javascript
24 | const express = require('express')
25 | const PORT = 8000
26 |
27 | const application = express()
28 |
29 | application.get('/', function (request, response) {
30 | console.log('URL:', request.url)
31 | console.log('Método:', request.method)
32 | response.status(200).send('Olá!')
33 | })
34 |
35 | application.listen(PORT)
36 | ```
37 |
38 | O `application.get('/', handler)`, funciona similar ao que fizemos da última vez. Porém nesse caso, toda requisição que o servidor receber na rota base (root), irá chamar a função passada como segundo parâmetro.
39 |
40 | Para iniciar o servidor, é da mesma forma que antes, apenas troque o nome do arquivo e execute:
41 |
42 | ```shell
43 | node pacote_express.js
44 | ```
45 |
46 | Algumas coisas que podemos brincar com esse servidor são, ler dados da requisição do cliente, e retornar dados.
47 |
48 | ## Lendo dados do servidor
49 |
50 | Exemplo de requisição ao servidor `express`:
51 |
52 |
53 |
54 |
55 | Podemos observar por exemplo que por padrão é enviado um `header` ao cliente chamado `X-Powered-By`, devido ao `express`.
56 |
57 |
58 |
59 |
60 |
61 | ## Alterando os headers
62 |
63 | Vamos supor que por motivos de segurança, você não quer que o mundo a fora saiba que seu servidor é feito em `express`.
64 |
65 | Para remover tal `header`, é necessário colocar a chamada para `application.disable('x-powered-by')` no código, ficando assim:
66 |
67 | ```javascript
68 | const express = require('express')
69 | const PORT = 8000
70 |
71 | const application = express()
72 |
73 | application.get('/', function (request, response) {
74 | console.log('URL:', request.url)
75 | console.log('Método:', request.method)
76 | response.status(200).send('Olá!')
77 | })
78 |
79 | application.disable('x-powered-by')
80 |
81 | application.listen(PORT)
82 | ```
83 |
84 | Dessa forma, ao fazer uma requisição pelo `Postman`, não veremos mais o `header`.
85 |
86 |
87 |
88 |
89 |
90 | Parabéns! Você acaba de modificar os `headers`, do seu servidor HTTP!
91 |
92 | ## Lendo os headers do cliente no servidor
93 |
94 | Exemplo de requisição de um cliente com um `dado-legal` como `header` adicional:
95 |
96 |
97 |
98 |
99 |
100 | Para ler o `header` no servidor:
101 |
102 | ```javascript
103 | const express = require('express')
104 | const PORT = 8000
105 |
106 | const application = express()
107 |
108 | application.get('/', function (request, response) {
109 | console.log('URL:', request.url)
110 | console.log('Método:', request.method)
111 | console.log('Headers:', request.headers)
112 | response.status(200).send('Olá!')
113 | })
114 |
115 | application.listen(PORT)
116 | ```
117 |
118 | Ao fazer a requisição, no terminal ficará da seguinte forma:
119 |
120 |
121 |
122 |
123 |
124 | Como pôde ver, o `header`, `dado-legal` foi lido no servidor!
125 |
126 | Da mesmo forma do anterior, para fechar o servidor simplesmente clique no teclado `Ctrl` + `C`, ou feche o `terminal`/`Prompt de comando`.
127 |
128 |
131 |
--------------------------------------------------------------------------------
/api/delecao_produto.js:
--------------------------------------------------------------------------------
1 | const express = require('express')
2 | const bodyParser = require('body-parser')
3 | const cuid = require('cuid')
4 | const PORT = 8000
5 |
6 | const app = express()
7 |
8 | app.use(bodyParser.json())
9 |
10 | const tokens = []
11 |
12 | app.post('/auth', function (req, res) {
13 | if (req.body.username == 'admin' && req.body.password == '1234') {
14 | const token = cuid()
15 |
16 | tokens.push(token)
17 |
18 | res.status(201).send({
19 | token: token
20 | })
21 | } else {
22 | res.status(401).send({
23 | message: 'Unauthorized'
24 | })
25 | }
26 | })
27 |
28 | const products = []
29 |
30 | function authorizationMiddleware (req, res, next) {
31 | const authorizationHeader = req.get('authorization')
32 |
33 | if (!authorizationHeader) {
34 | return res.status(401).send({ message: 'Authorization header missing' })
35 | }
36 |
37 | const authorizationHeaderParts = authorizationHeader.split(' ')
38 |
39 | const bearer = authorizationHeaderParts[0]
40 | const token = authorizationHeaderParts[1]
41 |
42 | if (bearer != 'Bearer') {
43 | return res.status(401).send({ message: 'Authorization header needs the Bearer prefix' })
44 | }
45 |
46 | const isValidToken = tokens.find(function (validToken) {
47 | return validToken == token
48 | })
49 |
50 | if (!isValidToken) {
51 | return res.status(401).send({ message: 'Authorization header is not valid' })
52 | }
53 |
54 | next()
55 | }
56 |
57 | app.post('/products', authorizationMiddleware, function (req, res) {
58 | if (req.body.name == null) {
59 | return res.status(400).send({
60 | message: 'The product name is missing'
61 | })
62 | }
63 |
64 | if (req.body.price == null) {
65 | return res.status(400).send({
66 | message: 'The product price is missing'
67 | })
68 | }
69 |
70 | const productAlreadyExists = products.find(function (product) {
71 | return product.name == req.body.name
72 | })
73 |
74 | if (productAlreadyExists) {
75 | return res.status(400).send({
76 | message: 'This product name already exists'
77 | })
78 | }
79 |
80 | const newProduct = {
81 | id: cuid(),
82 | name: req.body.name,
83 | price: req.body.price
84 | }
85 |
86 | products.push(newProduct)
87 |
88 | res.status(201).send(newProduct)
89 | })
90 |
91 | app.get('/products', authorizationMiddleware, function (req, res) {
92 | const filters = []
93 |
94 | if (req.query.name != null) { filters.push('name') }
95 | if (req.query.price != null) { filters.push('price') }
96 |
97 | let filteredProducts
98 |
99 | if (filters.length == 0) {
100 | filteredProducts = products
101 | } else {
102 | filteredProducts = products.filter(function (product) {
103 | const validations = []
104 |
105 | if (filters.includes('name')) {
106 | validations.push(product.name.includes(req.query.name))
107 | }
108 |
109 | if (filters.includes('price')) {
110 | validations.push(product.price == req.query.price)
111 | }
112 |
113 | return validations.some(function (validation) {
114 | return validation == true
115 | })
116 | })
117 | }
118 |
119 | res.status(200).send(filteredProducts)
120 | })
121 |
122 | app.get('/products/:id', authorizationMiddleware, function (req, res) {
123 | const productFound = products.find(function (product) {
124 | return req.params.id == product.id
125 | })
126 |
127 | if (!productFound) {
128 | return res.status(404).send({
129 | message: 'No product exists with this id'
130 | })
131 | }
132 |
133 | res.status(200).send(productFound)
134 | })
135 |
136 | app.patch('/products/:id', authorizationMiddleware, function (req, res) {
137 | const productFound = products.find(function (product) {
138 | return req.params.id == product.id
139 | })
140 |
141 | if (!productFound) {
142 | return res.status(404).send({
143 | message: 'No product exists with this id'
144 | })
145 | }
146 |
147 | if (req.body.name != null) {
148 | productFound.name = req.body.name
149 | }
150 |
151 | if (req.body.price != null) {
152 | productFound.price = req.body.price
153 | }
154 |
155 | res.status(200).send(productFound)
156 | })
157 |
158 | app.delete('/products/:id', authorizationMiddleware, function (req, res) {
159 | const productFound = products.find(function (product) {
160 | return req.params.id == product.id
161 | })
162 |
163 | if (!productFound) {
164 | return res.status(404).send({
165 | message: 'No product exists with this id'
166 | })
167 | }
168 |
169 | const itemToBeDeletedIndex = products.indexOf(productFound)
170 |
171 | products.splice(itemToBeDeletedIndex, 1)
172 |
173 | res.status(200).send(productFound)
174 | })
175 |
176 | app.listen(PORT)
177 |
--------------------------------------------------------------------------------
/book/css/variables.css:
--------------------------------------------------------------------------------
1 |
2 | /* Globals */
3 |
4 | :root {
5 | --sidebar-width: 300px;
6 | --page-padding: 15px;
7 | --content-max-width: 750px;
8 | }
9 |
10 | /* Themes */
11 |
12 | .ayu {
13 | --bg: hsl(210, 25%, 8%);
14 | --fg: #c5c5c5;
15 |
16 | --sidebar-bg: #14191f;
17 | --sidebar-fg: #c8c9db;
18 | --sidebar-non-existant: #5c6773;
19 | --sidebar-active: #ffb454;
20 | --sidebar-spacer: #2d334f;
21 |
22 | --scrollbar: var(--sidebar-fg);
23 |
24 | --icons: #737480;
25 | --icons-hover: #b7b9cc;
26 |
27 | --links: #0096cf;
28 |
29 | --inline-code-color: #ffb454;
30 |
31 | --theme-popup-bg: #14191f;
32 | --theme-popup-border: #5c6773;
33 | --theme-hover: #191f26;
34 |
35 | --quote-bg: hsl(226, 15%, 17%);
36 | --quote-border: hsl(226, 15%, 22%);
37 |
38 | --table-border-color: hsl(210, 25%, 13%);
39 | --table-header-bg: hsl(210, 25%, 28%);
40 | --table-alternate-bg: hsl(210, 25%, 11%);
41 |
42 | --searchbar-border-color: #848484;
43 | --searchbar-bg: #424242;
44 | --searchbar-fg: #fff;
45 | --searchbar-shadow-color: #d4c89f;
46 | --searchresults-header-fg: #666;
47 | --searchresults-border-color: #888;
48 | --searchresults-li-bg: #252932;
49 | --search-mark-bg: #e3b171;
50 | }
51 |
52 | .coal {
53 | --bg: hsl(200, 7%, 8%);
54 | --fg: #98a3ad;
55 |
56 | --sidebar-bg: #292c2f;
57 | --sidebar-fg: #a1adb8;
58 | --sidebar-non-existant: #505254;
59 | --sidebar-active: #3473ad;
60 | --sidebar-spacer: #393939;
61 |
62 | --scrollbar: var(--sidebar-fg);
63 |
64 | --icons: #43484d;
65 | --icons-hover: #b3c0cc;
66 |
67 | --links: #2b79a2;
68 |
69 | --inline-code-color: #c5c8c6;;
70 |
71 | --theme-popup-bg: #141617;
72 | --theme-popup-border: #43484d;
73 | --theme-hover: #1f2124;
74 |
75 | --quote-bg: hsl(234, 21%, 18%);
76 | --quote-border: hsl(234, 21%, 23%);
77 |
78 | --table-border-color: hsl(200, 7%, 13%);
79 | --table-header-bg: hsl(200, 7%, 28%);
80 | --table-alternate-bg: hsl(200, 7%, 11%);
81 |
82 | --searchbar-border-color: #aaa;
83 | --searchbar-bg: #b7b7b7;
84 | --searchbar-fg: #000;
85 | --searchbar-shadow-color: #aaa;
86 | --searchresults-header-fg: #666;
87 | --searchresults-border-color: #98a3ad;
88 | --searchresults-li-bg: #2b2b2f;
89 | --search-mark-bg: #355c7d;
90 | }
91 |
92 | .light {
93 | --bg: hsl(0, 0%, 100%);
94 | --fg: #333333;
95 |
96 | --sidebar-bg: #fafafa;
97 | --sidebar-fg: #364149;
98 | --sidebar-non-existant: #aaaaaa;
99 | --sidebar-active: #008cff;
100 | --sidebar-spacer: #f4f4f4;
101 |
102 | --scrollbar: #cccccc;
103 |
104 | --icons: #cccccc;
105 | --icons-hover: #333333;
106 |
107 | --links: #4183c4;
108 |
109 | --inline-code-color: #6e6b5e;
110 |
111 | --theme-popup-bg: #fafafa;
112 | --theme-popup-border: #cccccc;
113 | --theme-hover: #e6e6e6;
114 |
115 | --quote-bg: hsl(197, 37%, 96%);
116 | --quote-border: hsl(197, 37%, 91%);
117 |
118 | --table-border-color: hsl(0, 0%, 95%);
119 | --table-header-bg: hsl(0, 0%, 80%);
120 | --table-alternate-bg: hsl(0, 0%, 97%);
121 |
122 | --searchbar-border-color: #aaa;
123 | --searchbar-bg: #fafafa;
124 | --searchbar-fg: #000;
125 | --searchbar-shadow-color: #aaa;
126 | --searchresults-header-fg: #666;
127 | --searchresults-border-color: #888;
128 | --searchresults-li-bg: #e4f2fe;
129 | --search-mark-bg: #a2cff5;
130 | }
131 |
132 | .navy {
133 | --bg: hsl(226, 23%, 11%);
134 | --fg: #bcbdd0;
135 |
136 | --sidebar-bg: #282d3f;
137 | --sidebar-fg: #c8c9db;
138 | --sidebar-non-existant: #505274;
139 | --sidebar-active: #2b79a2;
140 | --sidebar-spacer: #2d334f;
141 |
142 | --scrollbar: var(--sidebar-fg);
143 |
144 | --icons: #737480;
145 | --icons-hover: #b7b9cc;
146 |
147 | --links: #2b79a2;
148 |
149 | --inline-code-color: #c5c8c6;;
150 |
151 | --theme-popup-bg: #161923;
152 | --theme-popup-border: #737480;
153 | --theme-hover: #282e40;
154 |
155 | --quote-bg: hsl(226, 15%, 17%);
156 | --quote-border: hsl(226, 15%, 22%);
157 |
158 | --table-border-color: hsl(226, 23%, 16%);
159 | --table-header-bg: hsl(226, 23%, 31%);
160 | --table-alternate-bg: hsl(226, 23%, 14%);
161 |
162 | --searchbar-border-color: #aaa;
163 | --searchbar-bg: #aeaec6;
164 | --searchbar-fg: #000;
165 | --searchbar-shadow-color: #aaa;
166 | --searchresults-header-fg: #5f5f71;
167 | --searchresults-border-color: #5c5c68;
168 | --searchresults-li-bg: #242430;
169 | --search-mark-bg: #a2cff5;
170 | }
171 |
172 | .rust {
173 | --bg: hsl(60, 9%, 87%);
174 | --fg: #262625;
175 |
176 | --sidebar-bg: #3b2e2a;
177 | --sidebar-fg: #c8c9db;
178 | --sidebar-non-existant: #505254;
179 | --sidebar-active: #e69f67;
180 | --sidebar-spacer: #45373a;
181 |
182 | --scrollbar: var(--sidebar-fg);
183 |
184 | --icons: #737480;
185 | --icons-hover: #262625;
186 |
187 | --links: #2b79a2;
188 |
189 | --inline-code-color: #6e6b5e;
190 |
191 | --theme-popup-bg: #e1e1db;
192 | --theme-popup-border: #b38f6b;
193 | --theme-hover: #99908a;
194 |
195 | --quote-bg: hsl(60, 5%, 75%);
196 | --quote-border: hsl(60, 5%, 70%);
197 |
198 | --table-border-color: hsl(60, 9%, 82%);
199 | --table-header-bg: #b3a497;
200 | --table-alternate-bg: hsl(60, 9%, 84%);
201 |
202 | --searchbar-border-color: #aaa;
203 | --searchbar-bg: #fafafa;
204 | --searchbar-fg: #000;
205 | --searchbar-shadow-color: #aaa;
206 | --searchresults-header-fg: #666;
207 | --searchresults-border-color: #888;
208 | --searchresults-li-bg: #dec2a2;
209 | --search-mark-bg: #e69f67;
210 | }
211 |
--------------------------------------------------------------------------------
/src/criando_um_servidor_com_o_modulo_http_do_node.md:
--------------------------------------------------------------------------------
1 | # Criando um servidor com o módulo HTTP do Node.js
2 |
3 | Crie um arquivo `JavaScript` que será o ponto de entrada da nossa aplicação/servidor HTTP.
4 |
5 | ```shell
6 | # cria o arquivo principal
7 | touch modulo_http.js
8 | ```
9 |
10 | > Obs: `#` significa um comentário no terminal, ou seja, que a linha não será executada :)
11 |
12 | Abra seu editor favorito e vamos codificar o tal servidor!
13 |
14 | Primeiro vamos importar o módulo `http` da biblioteca padrão do `Node.js`.
15 |
16 | Caso esteja curioso, não é necessário acessá-la, mas aqui está a [documentação do módulo](https://nodejs.org/api/http.html).
17 |
18 | ```javascript
19 | const http = require('http')
20 | ```
21 |
22 | Bom, agora que já importamos o módulo, iremos criar um servidor, usando a função `createServer`. Ela recebe uma função, que será chamada para cada vez que o servidor receber uma requisição.
23 |
24 | ```javascript
25 | const http = require('http')
26 |
27 | function requestHandler (request, response) {
28 | response.write('Hello World Node.js HTTP server!')
29 | response.end()
30 | }
31 |
32 | const server = http.createServer(requestHandler)
33 | ```
34 |
35 | Os dados das variáveis `request`, e `response`, correspondem a dados do que conseguimos ler da requisição e o que podemos colocar na resposta.
36 |
37 | A chamada a `response.write` e `response.end`, servem para retornar ao cliente um texto escrito `Hello World Node.js HTTP server!`.
38 |
39 | Como vimos antes, para o servidor ficar acessível ao mundo externo, precisamos escolher uma porta do computador que esteja disponível. No caso iremos usar a `8000`, por nenhum motivo específico.
40 |
41 | ```javascript
42 | const http = require('http')
43 | const PORT = 8000
44 |
45 | function requestHandler (request, response) {
46 | response.write('Hello World Node.js HTTP server!')
47 | response.end()
48 | }
49 |
50 | const server = http.createServer(requestHandler)
51 |
52 | server.listen(PORT, function (err) {
53 | if (err) {
54 | return console.log('Algo de ruim aconteceu, erro:', err)
55 | }
56 |
57 | console.log(`Servidor escutando na porta: ${PORT}`)
58 | })
59 | ```
60 |
61 | A `function (err) { ... }` que estamos passando como segundo parâmetro a função `server.listen`, é chamada assim que a tentativa de subir o servidor finalizou. Ela pode ter dado certo ou errado, para isso serve o `if` aqui.
62 |
63 | > Obs: `console.log` é a função que coloca texto no terminal, um `print`
64 |
65 | O `return` dentro do `if` serve para que terminemos a execução da função alí mesmo. Não queremos que apareça no terminal `Servidor escutando na porta:` quando na verdade o servidor não está realmente fazendo isso, pois ocorreu um erro.
66 |
67 | Aqui está! Temos o servidor, a porta e a função que cuida de cada requisição! Uhullll!!!!
68 |
69 | Bora colocar esse servidor para funcionar?!
70 |
71 | Execute em seu terminal:
72 |
73 | ```shell
74 | node modulo_http.js
75 | ```
76 |
77 | Caso nosso servidor esteja de pé, irá aparecer a seguinte frase no terminal: `Servidor escutando na porta: 8000`.
78 |
79 | Se der errado por algum motivo, aparecerá: `Algo de ruim aconteceu, erro: `, com o erro em sequência.
80 |
81 | Se cair no caso de erro, talvez seja porque já subiu o servidor anteriormente ou porque já existe algum outro programa que usa a porta `8000`. Sendo esse o caso, mude a porta do nosso servidor, ou feche os programas que estão usando tal porta.
82 |
83 | ### Ta, e agora?
84 |
85 | De que adianta um servidor HTTP se ele não for receber requisições não é mesmo? :O
86 |
87 | É agora que o Postman entra na jogada!
88 |
89 |
90 |
91 |
92 |
93 | Ao entrar será pedido para fazer login, porém é possível pular esse passo clicando no último texto que aparece na tela (`Take me straight to the app. I'll create an account another time.`):
94 |
95 |
96 |
97 |
98 |
99 | Na tela que o app começar, já poderá perceber que poderá criar uma requisição HTTP.
100 | - A esquerda está o `método`, no caso o padrão é `GET`.
101 | - Mais ao centro é possível colocar a `URL`, aqui coloque `localhost:8000`.
102 | - Abaixo da `URL`, existem vários campos que podem ser preenchidos, como `Headers`, `Body`, etc.
103 | - A direita tem o botão `Send` que serve para enviar a requisição.
104 | - Abaixo do botão `Send` é informado qual o `status code` da resposta e quanto tempo levou.
105 |
106 | Pressione o botão `Send`, e deverá ver a resposta do nosso servidor Node.js!
107 |
108 |
109 |
110 |
111 |
112 | Parabéns!!!! Você conseguiu criar seu primeiro servidor e enviar requisições a ele!!!!!! :)
113 |
114 | Só para ficar mais claro o que acabou de acontecer, aqui está um desenho:
115 |
116 |
117 |
118 |
119 |
120 | Uma última coisa que da para fazer de legal, é adicionar `logs`, na sua aplicação/servidor, para que mostre qual a `rota` que o cliente está pedindo, qual o `método`, etc.
121 |
122 | > Obs: `logs` nada mais são do que textos na tela do terminal para entender o fluxo da aplicação, ou seja, `console.log`s no caso do JavaScript
123 |
124 | Segue o código abaixo com alguns `logs` simples.
125 |
126 | ```javascript
127 | const http = require('http')
128 | const PORT = 8000
129 |
130 | function requestHandler (request, response) {
131 | console.log('URL:', request.url)
132 | console.log('Método:', request.method)
133 |
134 | response.write('Hello World Node.js HTTP server!')
135 | response.end()
136 | }
137 |
138 | const server = http.createServer(requestHandler)
139 |
140 | server.listen(PORT, function (err) {
141 | if (err) {
142 | return console.log('Algo de ruim aconteceu, erro:', err)
143 | }
144 |
145 | console.log(`Servidor escutando na porta: ${PORT}`)
146 | })
147 | ```
148 |
149 | Exemplo de requisição `POST`:
150 |
151 |
152 |
153 |
154 |
155 | Logs no terminal após uma requisição com o método `GET`, e outra com o `POST`:
156 |
157 |
158 |
159 |
160 |
161 | Topper! :D
162 |
163 | Para fechar o servidor simplesmente clique no teclado `Ctrl` + `C`, ou feche o `terminal`/`Prompt de comando`.
164 |
165 |
168 |
--------------------------------------------------------------------------------
/src/api_criacao_de_produto.md:
--------------------------------------------------------------------------------
1 | # Criação de produto
2 |
3 | Esse capítulo terá como base o código feito no capítulo de autenticação.
4 |
5 | ## Fluxo da informação
6 |
7 |
165 |
166 | Porém esse código não cobre o caso do nome e/ou preço estarem faltando. Segue abaixo como fazer a **validação** do nome, do preço, e se o produto já existe.
167 |
168 | ```javascript
169 | // ...
170 |
171 | const products = []
172 |
173 | app.post('/products', function (req, res) {
174 | if (req.body.name == null) {
175 | return res.status(400).send({
176 | message: 'The product name is missing'
177 | })
178 | }
179 |
180 | if (req.body.price == null) {
181 | return res.status(400).send({
182 | message: 'The product price is missing'
183 | })
184 | }
185 |
186 | const productAlreadyExists = products.find(function (product) {
187 | return product.name == req.body.name
188 | })
189 |
190 | if (productAlreadyExists) {
191 | return res.status(400).send({
192 | message: 'This product name already exists'
193 | })
194 | }
195 |
196 | const newProduct = {
197 | id: cuid(),
198 | name: req.body.name,
199 | price: req.body.price
200 | }
201 |
202 | products.push(newProduct)
203 |
204 | res.status(201).send(newProduct)
205 | })
206 |
207 | // ...
208 | ```
209 |
210 | > Obs: os `return`s feitos nos `if`s, servem para terminar a execução da rota sem que executem o resto do código
211 |
212 | Yesss!! Conseguimos fazer nossa primeira rota de criação de algum recurso!!!!
213 |
214 | Ainda tem a autenticação, lembre-se que o código acima **não valida** várias coisas, como o **tipo dos campos**, se eles estão **vazios**, se o preço é **negativo**, etc. Isso é somente um exemplo de como validar coisas mais básicas.
215 |
216 | ### Autenticação
217 |
218 | Algo está faltando, precisamos bloquear quem não pode fazer a requisição! Não queremos que pessoas sem acessos consigam realizar ações em nossa API.
219 |
220 | A biblioteca `express` permite que sejam passadas diversas funções para tratar uma rota, assim podemos validar se o cliente está autenticado, e caso esteja, a função que criamos que de fato cria o usuário, será chamada.
221 |
222 | Exemplo, considere uma rota `/rota_legal` que pode receber requisições `POST`:
223 |
224 | ```javascript
225 | function parte1 (req, res, next) {
226 | if (algo_faltando) {
227 | return res.status(401).send({ message: "Algo está faltando!!!" })
228 | }
229 |
230 | // chama parte2
231 | next()
232 | }
233 |
234 | function parte2 (req, res) {
235 | // lógica principal da rota
236 | }
237 |
238 | app.post('/rota_legal', parte1, parte2)
239 | ```
240 |
241 | Nesse caso, a função `parte2` só será chamada caso a função `parte1` chame a função `next`. Se não, a requisição irá parar por alí.
242 |
243 | Bora criar a autenticação no nosso app! Conforme o que foi colocado nos exemplos do começo do capítulo, iremos aceitar um `header` chamado `Authorization`. Ele terá duas partes, uma é o prefixo `Bearer`, isso é só uma palavra comum que costumam utilizar para esse tipo de coisa, e a segunda parte é o token em sí, gerado pela rota `/auth` do último capítulo.
244 |
245 | A principal parte é validar se o `token` recebido na requisição, está dentro da lista criada que armazena os `tokens` feitos na rota `/auth`.
246 |
247 | O código fica assim:
248 |
249 | ```javascript
250 | // ...
251 |
252 | function authorizationMiddleware (req, res, next) {
253 | const authorizationHeader = req.get('authorization')
254 |
255 | if (!authorizationHeader) {
256 | return res.status(401).send({ message: 'Authorization header missing' })
257 | }
258 |
259 | const authorizationHeaderParts = authorizationHeader.split(' ')
260 |
261 | const bearer = authorizationHeaderParts[0]
262 | const token = authorizationHeaderParts[1]
263 |
264 | if (bearer != 'Bearer') {
265 | return res.status(401).send({ message: 'Authorization header needs the Bearer prefix' })
266 | }
267 |
268 | const isValidToken = tokens.find(function (validToken) {
269 | return validToken == token
270 | })
271 |
272 | if (!isValidToken) {
273 | return res.status(401).send({ message: 'Authorization header is not valid' })
274 | }
275 |
276 | next()
277 | }
278 |
279 | app.post('/products', authorizationMiddleware, function (req, res) {
280 | // lógica principal já implementada...
281 | })
282 |
283 | // ...
284 | ```
285 |
286 | E a requisição de sucesso fica assim:
287 |
288 |
289 |
290 |
291 |
292 | No caso para dar certo, vai precisar gerar um `token` na rota `/auth` com as credenciais corretas.
293 |
294 | Uhulll ta tudo feito da criação de produtos!!!! :)
295 |
296 |
Bom, como agora já sabe como fazer as requisições e ler os dados da resposta, tudo que precisa fazer para a edição é trocar o método para DELETE e remover o body/corpo (campo data).
145 |
Já que é muito similar, não irei mostrar aqui como é feito.
Bom, como agora já sabe como fazer as requisições e ler os dados da resposta, tudo que precisa fazer para a edição é trocar o método para PATCH e passar o id na url.
145 |
Já que é muito similar, não irei mostrar aqui como é feito.
Bom, como agora já sabe como fazer as requisições e ler os dados da resposta, tudo que precisa fazer para a edição é trocar o método para GET, remover o body/corpo (campo data), e passar o id na url.
145 |
Já que é muito similar, não irei mostrar aqui como é feito.
Agora que já fizemos um servidor, entendemos como ele funciona e como fazer requisições a ele via código, iremos entender o que é uma SDK.
145 |
SDK é um kit de desenvolvimento para algo.
146 |
Existem SDKs que seguem APIs HTTP, e outras que são simplesmente sobre funções/classes/etc de alguma biblioteca.
147 |
No caso de SDKs de APIs HTTP, basicamente elas são "wrappers" (uma camada entorno) sobre as requisições HTTP.
148 |
Exemplo, vamos supor que tenho uma incrível API de mapas, porém é muito complexo enviar os dados a minha API para cada requisição. O que eu posso fazer para facilitar a vida de quem consome minha API, é escrever uma SDK que contém funções que já deixam tudo pronto quanto ao HTTP e só retornam os dados de forma simples.
149 |
Uma SDK é só uma abstração.
150 |
Portanto, ao invés de em Node.js termos de interagir com o axios, poderíamos importar uma biblioteca fictícia chamada products-sdk que teria funções como createProduct. Ou então, tería uma classe Product que ao instanciar iria fazer a requisição a nossa API.
Imagine um programa, que aguarda conexões chegarem a ele, em uma porta específica do computador.
145 |
Portas são apenas entradas do mundo externo para com o computador e os programas que rodam nele.
146 |
Ou seja, é possível que sejam iniciados por exemplo 3 servidores, cada um escutando a uma porta distinta no computador, a qual irá poder falar com o mundo a fora. É claro que podem ser bem mais, ou menos, isso é só um exemplo.
147 |
Exemplo de computador/servidor com portas e programas: