├── .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 | drawing1 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 | drawing1 9 |

10 | 11 |

12 | drawing2 13 |

14 | 15 |

16 | drawing3 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 | drawing1 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 | drawing2 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 |

39 | terminal-output-1 40 |

41 | 42 | Simples assim! :) 43 | 44 |

45 | Link para a versão final do código 46 |

47 | -------------------------------------------------------------------------------- /book/highlight.css: -------------------------------------------------------------------------------- 1 | /* Base16 Atelier Dune Light - Theme */ 2 | /* by Bram de Haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/dune) */ 3 | /* Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) */ 4 | 5 | /* Atelier-Dune Comment */ 6 | .hljs-comment, 7 | .hljs-quote { 8 | color: #AAA; 9 | } 10 | 11 | /* Atelier-Dune Red */ 12 | .hljs-variable, 13 | .hljs-template-variable, 14 | .hljs-attribute, 15 | .hljs-tag, 16 | .hljs-name, 17 | .hljs-regexp, 18 | .hljs-link, 19 | .hljs-name, 20 | .hljs-selector-id, 21 | .hljs-selector-class { 22 | color: #d73737; 23 | } 24 | 25 | /* Atelier-Dune Orange */ 26 | .hljs-number, 27 | .hljs-meta, 28 | .hljs-built_in, 29 | .hljs-builtin-name, 30 | .hljs-literal, 31 | .hljs-type, 32 | .hljs-params { 33 | color: #b65611; 34 | } 35 | 36 | /* Atelier-Dune Green */ 37 | .hljs-string, 38 | .hljs-symbol, 39 | .hljs-bullet { 40 | color: #60ac39; 41 | } 42 | 43 | /* Atelier-Dune Blue */ 44 | .hljs-title, 45 | .hljs-section { 46 | color: #6684e1; 47 | } 48 | 49 | /* Atelier-Dune Purple */ 50 | .hljs-keyword, 51 | .hljs-selector-tag { 52 | color: #b854d4; 53 | } 54 | 55 | .hljs { 56 | display: block; 57 | overflow-x: auto; 58 | background: #f1f1f1; 59 | color: #6e6b5e; 60 | padding: 0.5em; 61 | } 62 | 63 | .hljs-emphasis { 64 | font-style: italic; 65 | } 66 | 67 | .hljs-strong { 68 | font-weight: bold; 69 | } 70 | -------------------------------------------------------------------------------- /src/node_requisicoes_autenticacao.md: -------------------------------------------------------------------------------- 1 | # Autenticação 2 | 3 | Primeiro de tudo, para conseguirmos interagir com nossa API de produtos, precisamos de um token, ou seja, de autenticação/autorização. 4 | 5 | Segue abaixo o script de como usar o `axios` para fazer uma requisição `HTTP`. 6 | 7 | ```javascript 8 | const axios = require('axios') 9 | 10 | const BASE_URL = 'http://localhost:8000' 11 | 12 | async function run () { 13 | const authResponse = await axios({ 14 | baseURL: BASE_URL, 15 | url: '/auth', 16 | method: 'POST', 17 | data: { 18 | username: 'admin', 19 | password: '1234' 20 | } 21 | }) 22 | 23 | console.log('authResponse body:', authResponse.data) 24 | } 25 | 26 | run() 27 | ``` 28 | 29 | Lembrando que a variável `data` na requisição e na resposta (`authResponse`) representam o `body`/corpo do protocólo HTTP. 30 | 31 | Ao executar, com o nosso servidor do último capítulo rodando é claro, teremos o seguinte resultado no terminal: 32 | 33 |

34 | terminal-output 35 |

36 | 37 | Ou seja, nosso `token` foi gerado! 38 | 39 | Bom, agora poderemos prosseguir com as demais requisições a nossa API. 40 | 41 |

42 | Link para a versão final do código 43 |

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 |

44 | Link para a versão final do código 45 |

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 |

46 | Link para a versão final do código 47 |

48 | -------------------------------------------------------------------------------- /src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | - [Qual a ideia do livro?](./qual_a_ideia_do_livro.md) 4 | - [O que é um servidor Web HTTP?](./o_que_e_um_servidor_web_http.md) 5 | - [Como é a dinâmica da comunicação HTTP?](./como_e_a_dinamica_da_comunicacao_http.md) 6 | - [O que costuma girar entorno de servidores HTTP?](./o_que_costuma_girar_entorno_de_servidores_http.md) 7 | - [Quais são os dados trafegados no protocólo HTTP?](./quais_sao_os_dados_trafegados_no_protocolo_http.md) 8 | - [Vamos colocar a mão na massa e criar um servidor!](./vamos_colocar_a_mao_na_massa_e_criar_um_servidor.md) 9 | - [Criando um servidor com o módulo HTTP do Node.js](./criando_um_servidor_com_o_modulo_http_do_node.md) 10 | - [Usando o pacote express](./usando_o_express.md) 11 | - [Usando o corpo/body e JSON](./usando_corpo_body_e_json.md) 12 | - [Vamos criar uma simples API em memória!](./vamos_criar_uma_simples_api_em_memoria.md) 13 | - [Setup](./api_setup.md) 14 | - [Autenticação](./api_autenticacao.md) 15 | - [Criação de produto](./api_criacao_de_produto.md) 16 | - [Listagem de produtos](./api_listagem_de_produtos.md) 17 | - [Buscar somente um produto](./api_buscar_somente_um_produto.md) 18 | - [Edição de produto](./api_edicao_de_produto.md) 19 | - [Deleção de produto](./api_delecao_de_produto.md) 20 | - [Vamos fazer requisições via Node.js!](./vamos_fazer_requisicoes_via_node.md) 21 | - [Setup](./node_requisicoes_setup.md) 22 | - [Autenticação](./node_requisicoes_autenticacao.md) 23 | - [Criação de produto](./node_requisicoes_criacao_de_produto.md) 24 | - [Listagem de produtos](./node_requisicoes_listagem_de_produtos.md) 25 | - [Buscar somente um produto](./node_requisicoes_somente_um_produto.md) 26 | - [Edição de produto](./node_requisicoes_edicao_de_produto.md) 27 | - [Deleção de produto](./node_requisicoes_delecao_de_produto.md) 28 | - [O que é uma SDK?](./o_que_e_uma_sdk.md) 29 | -------------------------------------------------------------------------------- /book/tomorrow-night.css: -------------------------------------------------------------------------------- 1 | /* Tomorrow Night Theme */ 2 | /* http://jmblog.github.com/color-themes-for-google-code-highlightjs */ 3 | /* Original theme - https://github.com/chriskempson/tomorrow-theme */ 4 | /* http://jmblog.github.com/color-themes-for-google-code-highlightjs */ 5 | 6 | /* Tomorrow Comment */ 7 | .hljs-comment { 8 | color: #969896; 9 | } 10 | 11 | /* Tomorrow Red */ 12 | .hljs-variable, 13 | .hljs-attribute, 14 | .hljs-tag, 15 | .hljs-regexp, 16 | .ruby .hljs-constant, 17 | .xml .hljs-tag .hljs-title, 18 | .xml .hljs-pi, 19 | .xml .hljs-doctype, 20 | .html .hljs-doctype, 21 | .css .hljs-id, 22 | .css .hljs-class, 23 | .css .hljs-pseudo { 24 | color: #cc6666; 25 | } 26 | 27 | /* Tomorrow Orange */ 28 | .hljs-number, 29 | .hljs-preprocessor, 30 | .hljs-pragma, 31 | .hljs-built_in, 32 | .hljs-literal, 33 | .hljs-params, 34 | .hljs-constant { 35 | color: #de935f; 36 | } 37 | 38 | /* Tomorrow Yellow */ 39 | .ruby .hljs-class .hljs-title, 40 | .css .hljs-rule .hljs-attribute { 41 | color: #f0c674; 42 | } 43 | 44 | /* Tomorrow Green */ 45 | .hljs-string, 46 | .hljs-value, 47 | .hljs-inheritance, 48 | .hljs-header, 49 | .hljs-name, 50 | .ruby .hljs-symbol, 51 | .xml .hljs-cdata { 52 | color: #b5bd68; 53 | } 54 | 55 | /* Tomorrow Aqua */ 56 | .hljs-title, 57 | .css .hljs-hexcolor { 58 | color: #8abeb7; 59 | } 60 | 61 | /* Tomorrow Blue */ 62 | .hljs-function, 63 | .python .hljs-decorator, 64 | .python .hljs-title, 65 | .ruby .hljs-function .hljs-title, 66 | .ruby .hljs-title .hljs-keyword, 67 | .perl .hljs-sub, 68 | .javascript .hljs-title, 69 | .coffeescript .hljs-title { 70 | color: #81a2be; 71 | } 72 | 73 | /* Tomorrow Purple */ 74 | .hljs-keyword, 75 | .javascript .hljs-function { 76 | color: #b294bb; 77 | } 78 | 79 | .hljs { 80 | display: block; 81 | overflow-x: auto; 82 | background: #1d1f21; 83 | color: #c5c8c6; 84 | padding: 0.5em; 85 | -webkit-text-size-adjust: none; 86 | } 87 | 88 | .coffeescript .javascript, 89 | .javascript .xml, 90 | .tex .hljs-formula, 91 | .xml .javascript, 92 | .xml .vbscript, 93 | .xml .css, 94 | .xml .hljs-cdata { 95 | opacity: 0.5; 96 | } 97 | -------------------------------------------------------------------------------- /src/usando_corpo_body_e_json.md: -------------------------------------------------------------------------------- 1 | # Usando o corpo/body e JSON 2 | 3 | Agora iremos passar dados pelo corpo (`body`) da requisição, e retornar dados pelo corpo (`body`) da resposta. 4 | 5 | O `express` não faz o `parse` (análise e preparo) do corpo da requisição, portanto para fazer isso, iremos usar um `middleware` (uma função que é chamada em cada requisição, antes do nosso código) chamado `body-parser`. 6 | 7 | Para instalá-lo, execute: 8 | 9 | ```shell 10 | npm install --save body-parser 11 | ``` 12 | 13 | Vamos criar um novo arquivo `JavaScript` para isso! 14 | 15 | ```shell 16 | # cria o arquivo principal 17 | touch usando_body.js 18 | ``` 19 | 20 | Nosso arquivo vai começar com o seguinte: 21 | 22 | ```javascript 23 | const express = require('express') 24 | const PORT = 8000 25 | 26 | const app = express() 27 | 28 | app.listen(PORT) 29 | ``` 30 | 31 | Agora vamos adicionar o `body-parser` para poder ler o corpo da requisição no nosso servidor. 32 | 33 | ```javascript 34 | const express = require('express') 35 | const bodyParser = require('body-parser') 36 | const PORT = 8000 37 | 38 | const app = express() 39 | 40 | app.use(bodyParser.json()) 41 | 42 | app.listen(PORT) 43 | ``` 44 | 45 | Pronto! Agora iremos adicionar uma rota que irá receber um conteúdo no formato `JSON` e irá retornar o mesmo no corpo da resposta. 46 | 47 | ```javascript 48 | const express = require('express') 49 | const bodyParser = require('body-parser') 50 | const PORT = 8000 51 | 52 | const app = express() 53 | 54 | app.use(bodyParser.json()) 55 | 56 | app.post('/', function (req, res) { 57 | res.send(req.body) 58 | }) 59 | 60 | app.listen(PORT) 61 | ``` 62 | 63 | Temos nosso servidor, que tal testarmos para ver se funciona via `Postman`? 64 | 65 |

66 | postman-returning-same-body 67 |

68 | 69 | Uhulll!!! Funciona :) 70 | 71 |

72 | Link para a versão final do código 73 |

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 |

8 | sequence-diagram-information-flow 9 |

10 | 11 | ### Caso de sucesso 12 | > Requisição 13 | 14 | `URL`: `/products/cjuq20sms0001j35j9mbf246v` 15 | 16 | `método`: `GET` 17 | 18 | `headers`: 19 | - `Authorization`: `Bearer j19fn19fhq9f0jr0adsyf08aefhf0` 20 | 21 | > Resposta 22 | 23 | `status code`: `200` 24 | 25 | `body`: 26 | ```json 27 | { 28 | "id": "cjuq20sms0001j35j9mbf246v", 29 | "name": "boneco do pikachu", 30 | "price": 5.34 31 | } 32 | ``` 33 | 34 | ### Caso de erro, produto não encontrado 35 | > Requisição 36 | 37 | `URL`: `/products/id_nao_existente` 38 | 39 | `método`: `GET` 40 | 41 | `headers`: 42 | - `Authorization`: `Bearer j19fn19fhq9f0jr0adsyf08aefhf0` 43 | 44 | > Resposta 45 | 46 | `status code`: `404` 47 | 48 | `body`: 49 | ```json 50 | { 51 | "message": "No product exists with this id" 52 | } 53 | ``` 54 | 55 | ### Caso de erro de autenticação 56 | > Requisição 57 | 58 | `URL`: `/products/cjuq20sms0001j35j9mbf246v` 59 | 60 | `método`: `GET` 61 | 62 | `headers`: 63 | - `Authorization`: `Bearer TOKEN_INEXISTENTE` 64 | 65 | `body`: 66 | ```json 67 | { 68 | "name": "bolinha de ping pong" 69 | } 70 | ``` 71 | 72 | > Resposta 73 | 74 | `status code`: `401` 75 | 76 | `body`: 77 | ```json 78 | { 79 | "message": "Unauthorized" 80 | } 81 | ``` 82 | 83 | ## Implementação 84 | 85 | ```javascript 86 | // imports... 87 | 88 | // setup... 89 | 90 | const tokens = [] 91 | 92 | app.post('/auth', function (req, res) { 93 | // ... 94 | }) 95 | 96 | const products = [] 97 | 98 | function authorizationMiddleware (req, res, next) { 99 | // ... 100 | } 101 | 102 | app.post('/products', authorizationMiddleware, function (req, res) { 103 | // ... 104 | }) 105 | 106 | app.get('/products/:id', authorizationMiddleware, function (req, res) { 107 | const productFound = products.find(function (product) { 108 | return req.params.id == product.id 109 | }) 110 | 111 | if (!productFound) { 112 | return res.status(404).send({ 113 | message: 'No product exists with this id' 114 | }) 115 | } 116 | 117 | res.status(200).send(productFound) 118 | }) 119 | 120 | // app.listen... 121 | ``` 122 | 123 | Observe que só de colocar o `:id` no final da URL, o próprio `express` já coloca uma variável dentro do objeto `req.params` com o valor enviado. 124 | 125 |

126 | sample-request-show-one 127 |

128 | 129 |

130 | Link para a versão final do código 131 |

132 | -------------------------------------------------------------------------------- /src/quais_sao_os_dados_trafegados_no_protocolo_http.md: -------------------------------------------------------------------------------- 1 | # Quais são os dados trafegados no protocólo HTTP? 2 | 3 | ## URL 4 | 5 | Exemplo: `www.google.com/images?size=500&free_use=false`. 6 | - Nessa URL temos o domínio: `www.google.com`. 7 | - A rota: `/images`. 8 | - Dados `query string` (depois da interrogação): `size` valendo `500` e `free_use` valendo `false`. 9 | 10 | ## Status code 11 | 12 | É um número de três dígitos que representa o que aconteceu com a requisição HTTP, veja lista [aqui](https://developer.mozilla.org/pt-BR/docs/Web/HTTP/Status). Os mais comuns são: 13 | - `2xx`: deu tudo certo 14 | - `3xx`: redirecionamento 15 | - `4xx`: respostas de erro (algum campo faltando por exemplo) 16 | - `5xx`: servidor deu problema (pode estar fora do ar, ou "explodiu" no meio da requisição) 17 | 18 | ## Body 19 | 20 | O corpo da requisição. É basicamente o dado principal da requisição e resposta HTTP. Ele pode possuir diferentes formatos. Vamos supor que quero fazer login em uma página, o servidor precisa saber qual o meu `e-mail` e qual a minha `senha`. Geralmente esse tipo de dado é enviado pelo corpo, e na resposta nesse caso iremos receber um `status code` informando se os dados estão corretos (`200`, deu certo, e `400` deu errado). Exemplo utilizando o formato `JSON`: 21 | > Requisição 22 | 23 | `body`: 24 | ```json 25 | { 26 | "email": "email.bacana@provedor.com", 27 | "senha": "1234" 28 | } 29 | ``` 30 | 31 | > Resposta 32 | 33 | `status code`: `200` (ou seja, deu certo) 34 | 35 | `body`: 36 | ```json 37 | { 38 | "login_token": "j19fn19fhq9f0jr0adsyf08aefhf0" 39 | } 40 | ``` 41 | 42 | 43 | ## Headers 44 | 45 | São um conjunto de chaves e valores que podem ser enviados pelo cliente na requisição, e que também são respondidos pelo servidor. Neles costumam ser enviados dados como token de autenticação, qual o cliente fazendo a requisição (um browser/navegador, uma biblioteca, etc), tipo de conteúdo/formato (`JSON`, `form-data`, etc). Exemplo: 46 | > Requisição 47 | 48 | `headers`: 49 | 50 | | Chave | Valor | 51 | | :---: | :---: | 52 | | Content-Type | application/json | 53 | | User-Agent | Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36 | 54 | 55 | > Resposta 56 | 57 | `headers`: 58 | 59 | | Chave | Valor | 60 | | :---: | :---: | 61 | | X-Powered-By | Express | 62 | | Date | Sun, 14 Apr 2019 17:30:07 GMT | 63 | 64 | ## Método 65 | 66 | Indica o que será feito na requisição. Os principais são: 67 | 68 | - `GET`: cliente busca dados (listagem de dados, ou algo específico) 69 | - `POST`: cliente quer criar algo (criar uma sessão, um item, um produto, etc) 70 | - `PUT`: cliente quer alterar algo (um produto, item, etc) 71 | - `DELETE`: cliente quer deletar algo (um produto, item, etc) 72 | 73 | ### Observações 74 | 75 | Todos os dados trafegados no HTTP seguem convenções, porém nada impede de quem implementa o servidor, de entregar algo diferente do esperado em uma requisição. Por exemplo, um cliente pode criar um produto enviando os dados no `corpo` da requisição, o servidor retornar o `status code` 200, e mesmo assim nenhum produto ter de fato sido criado. 76 | -------------------------------------------------------------------------------- /api/listagem_produtos.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.listen(PORT) 123 | -------------------------------------------------------------------------------- /src/api_delecao_de_produto.md: -------------------------------------------------------------------------------- 1 | # Deleção de produto 2 | 3 | Esse capítulo toma como base o código do capítulo de edição de produto. 4 | 5 | ## Fluxo da informação 6 | 7 |

8 | sequence-diagram-information-flow 9 |

10 | 11 | ### Caso de sucesso 12 | > Requisição 13 | 14 | `URL`: `/products/cjuq20sms0001j35j9mbf246v` 15 | 16 | `método`: `DELETE` 17 | 18 | `headers`: 19 | - `Authorization`: `Bearer j19fn19fhq9f0jr0adsyf08aefhf0` 20 | 21 | > Resposta 22 | 23 | `status code`: `200` 24 | 25 | `body`: 26 | ```json 27 | { 28 | "id": "cjuq20sms0001j35j9mbf246v", 29 | "name": "bolinha de ping pong", 30 | "price": 5.34 31 | } 32 | ``` 33 | 34 | ### Caso de erro, produto não encontrado 35 | > Requisição 36 | 37 | `URL`: `/products/id_nao_existente` 38 | 39 | `método`: `DELETE` 40 | 41 | `headers`: 42 | - `Authorization`: `Bearer j19fn19fhq9f0jr0adsyf08aefhf0` 43 | 44 | > Resposta 45 | 46 | `status code`: `404` 47 | 48 | `body`: 49 | ```json 50 | { 51 | "message": "No product exists with this id" 52 | } 53 | ``` 54 | 55 | ### Caso de erro de autenticação 56 | > Requisição 57 | 58 | `URL`: `/products/cjuq20sms0001j35j9mbf246v` 59 | 60 | `método`: `DELETE` 61 | 62 | `headers`: 63 | - `Authorization`: `Bearer TOKEN_INEXISTENTE` 64 | 65 | > Resposta 66 | 67 | `status code`: `401` 68 | 69 | `body`: 70 | ```json 71 | { 72 | "message": "Unauthorized" 73 | } 74 | ``` 75 | 76 | ## Implementação 77 | 78 | ```javascript 79 | // imports... 80 | 81 | // setup... 82 | 83 | const tokens = [] 84 | 85 | app.post('/auth', function (req, res) { 86 | // ... 87 | }) 88 | 89 | const products = [] 90 | 91 | function authorizationMiddleware (req, res, next) { 92 | // ... 93 | } 94 | 95 | app.post('/products', authorizationMiddleware, function (req, res) { 96 | // ... 97 | }) 98 | 99 | app.get('/products', authorizationMiddleware, function (req, res) { 100 | // ... 101 | }) 102 | 103 | app.get('/products/:id', authorizationMiddleware, function (req, res) { 104 | // ... 105 | }) 106 | 107 | app.patch('/products/:id', authorizationMiddleware, function (req, res) { 108 | // ... 109 | }) 110 | 111 | app.delete('/products/:id', authorizationMiddleware, function (req, res) { 112 | const productFound = products.find(function (product) { 113 | return req.params.id == product.id 114 | }) 115 | 116 | if (!productFound) { 117 | return res.status(404).send({ 118 | message: 'No product exists with this id' 119 | }) 120 | } 121 | 122 | const itemToBeDeletedIndex = products.indexOf(productFound) 123 | 124 | products.splice(itemToBeDeletedIndex, 1) 125 | 126 | res.status(200).send(productFound) 127 | }) 128 | 129 | // app.listen... 130 | ``` 131 | 132 | Assim como nos últimos dois capítulos, o `:id` acaba por virar uma variável graças ao `express`. 133 | 134 |

135 | sample-request-delete 136 |

137 | 138 | Parabéns!!!! Você fez seu primeiro CRUD (Create, Read, Update, Delete) com Node.js!!!!!! 139 | 140 |

141 | Link para a versão final do código 142 |

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 |

8 | sequence-diagram-information-flow 9 |

10 | 11 | ### Caso de sucesso 12 | > Requisição 13 | 14 | `URL`: `/products/cjuq20sms0001j35j9mbf246v` 15 | 16 | `método`: `PATCH` 17 | 18 | `headers`: 19 | - `Authorization`: `Bearer j19fn19fhq9f0jr0adsyf08aefhf0` 20 | 21 | `body`: 22 | ```json 23 | { 24 | "name": "bolinha de ping pong" 25 | } 26 | ``` 27 | 28 | > Resposta 29 | 30 | `status code`: `200` 31 | 32 | `body`: 33 | ```json 34 | { 35 | "id": "cjuq20sms0001j35j9mbf246v", 36 | "name": "bolinha de ping pong", 37 | "price": 5.34 38 | } 39 | ``` 40 | 41 | ### Caso de erro, produto não encontrado 42 | > Requisição 43 | 44 | `URL`: `/products/id_nao_existente` 45 | 46 | `método`: `PATCH` 47 | 48 | `headers`: 49 | - `Authorization`: `Bearer j19fn19fhq9f0jr0adsyf08aefhf0` 50 | 51 | `body`: 52 | ```json 53 | { 54 | "name": "bolinha de ping pong" 55 | } 56 | ``` 57 | 58 | > Resposta 59 | 60 | `status code`: `404` 61 | 62 | `body`: 63 | ```json 64 | { 65 | "message": "No product exists with this id" 66 | } 67 | ``` 68 | 69 | ### Caso de erro de autenticação 70 | > Requisição 71 | 72 | `URL`: `/products/cjuq20sms0001j35j9mbf246v` 73 | 74 | `método`: `PATCH` 75 | 76 | `headers`: 77 | - `Authorization`: `Bearer TOKEN_INEXISTENTE` 78 | 79 | `body`: 80 | ```json 81 | { 82 | "name": "bolinha de ping pong" 83 | } 84 | ``` 85 | 86 | > Resposta 87 | 88 | `status code`: `401` 89 | 90 | `body`: 91 | ```json 92 | { 93 | "message": "Unauthorized" 94 | } 95 | ``` 96 | 97 | ## Implementação 98 | 99 | ```javascript 100 | // imports... 101 | 102 | // setup... 103 | 104 | const tokens = [] 105 | 106 | app.post('/auth', function (req, res) { 107 | // ... 108 | }) 109 | 110 | const products = [] 111 | 112 | function authorizationMiddleware (req, res, next) { 113 | // ... 114 | } 115 | 116 | app.post('/products', authorizationMiddleware, function (req, res) { 117 | // ... 118 | }) 119 | 120 | app.get('/products', authorizationMiddleware, function (req, res) { 121 | // ... 122 | }) 123 | 124 | app.get('/products/:id', authorizationMiddleware, function (req, res) { 125 | // ... 126 | }) 127 | 128 | app.patch('/products/:id', authorizationMiddleware, function (req, res) { 129 | const productFound = products.find(function (product) { 130 | return req.params.id == product.id 131 | }) 132 | 133 | if (!productFound) { 134 | return res.status(404).send({ 135 | message: 'No product exists with this id' 136 | }) 137 | } 138 | 139 | if (req.body.name != null) { 140 | productFound.name = req.body.name 141 | } 142 | 143 | if (req.body.price != null) { 144 | productFound.price = req.body.price 145 | } 146 | 147 | res.status(200).send(productFound) 148 | }) 149 | 150 | // app.listen... 151 | ``` 152 | 153 | Assim como no último capítulo, o `:id` acaba por virar uma variável graças ao `express`. 154 | 155 |

156 | sample-request-edit 157 |

158 | 159 |

160 | Link para a versão final do código 161 |

162 | -------------------------------------------------------------------------------- /book/css/general.css: -------------------------------------------------------------------------------- 1 | /* Base styles and content styles */ 2 | 3 | @import 'variables.css'; 4 | 5 | html { 6 | font-family: "Open Sans", sans-serif; 7 | color: var(--fg); 8 | background-color: var(--bg); 9 | text-size-adjust: none; 10 | } 11 | 12 | body { 13 | margin: 0; 14 | font-size: 1rem; 15 | overflow-x: hidden; 16 | } 17 | 18 | code { 19 | font-family: "Source Code Pro", Consolas, "Ubuntu Mono", Menlo, "DejaVu Sans Mono", monospace, monospace; 20 | font-size: 0.875em; /* please adjust the ace font size accordingly in editor.js */ 21 | } 22 | 23 | .left { float: left; } 24 | .right { float: right; } 25 | .hidden { display: none; } 26 | .play-button.hidden { display: none; } 27 | 28 | h2, h3 { margin-top: 2.5em; } 29 | h4, h5 { margin-top: 2em; } 30 | 31 | .header + .header h3, 32 | .header + .header h4, 33 | .header + .header h5 { 34 | margin-top: 1em; 35 | } 36 | 37 | a.header:target h1:before, 38 | a.header:target h2:before, 39 | a.header:target h3:before, 40 | a.header:target h4:before { 41 | display: inline-block; 42 | content: "»"; 43 | margin-left: -30px; 44 | width: 30px; 45 | } 46 | 47 | .page { 48 | outline: 0; 49 | padding: 0 var(--page-padding); 50 | } 51 | .page-wrapper { 52 | box-sizing: border-box; 53 | } 54 | .js .page-wrapper { 55 | transition: margin-left 0.3s ease, transform 0.3s ease; /* Animation: slide away */ 56 | } 57 | 58 | .content { 59 | overflow-y: auto; 60 | padding: 0 15px; 61 | padding-bottom: 50px; 62 | } 63 | .content main { 64 | margin-left: auto; 65 | margin-right: auto; 66 | max-width: var(--content-max-width); 67 | } 68 | .content a { text-decoration: none; } 69 | .content a:hover { text-decoration: underline; } 70 | .content img { max-width: 100%; } 71 | .content .header:link, 72 | .content .header:visited { 73 | color: var(--fg); 74 | } 75 | .content .header:link, 76 | .content .header:visited:hover { 77 | text-decoration: none; 78 | } 79 | 80 | table { 81 | margin: 0 auto; 82 | border-collapse: collapse; 83 | } 84 | table td { 85 | padding: 3px 20px; 86 | border: 1px var(--table-border-color) solid; 87 | } 88 | table thead { 89 | background: var(--table-header-bg); 90 | } 91 | table thead td { 92 | font-weight: 700; 93 | border: none; 94 | } 95 | table thead tr { 96 | border: 1px var(--table-header-bg) solid; 97 | } 98 | /* Alternate background colors for rows */ 99 | table tbody tr:nth-child(2n) { 100 | background: var(--table-alternate-bg); 101 | } 102 | 103 | 104 | blockquote { 105 | margin: 20px 0; 106 | padding: 0 20px; 107 | color: var(--fg); 108 | background-color: var(--quote-bg); 109 | border-top: .1em solid var(--quote-border); 110 | border-bottom: .1em solid var(--quote-border); 111 | } 112 | 113 | 114 | :not(.footnote-definition) + .footnote-definition, 115 | .footnote-definition + :not(.footnote-definition) { 116 | margin-top: 2em; 117 | } 118 | .footnote-definition { 119 | font-size: 0.9em; 120 | margin: 0.5em 0; 121 | } 122 | .footnote-definition p { 123 | display: inline; 124 | } 125 | 126 | .tooltiptext { 127 | position: absolute; 128 | visibility: hidden; 129 | color: #fff; 130 | background-color: #333; 131 | transform: translateX(-50%); /* Center by moving tooltip 50% of its width left */ 132 | left: -8px; /* Half of the width of the icon */ 133 | top: -35px; 134 | font-size: 0.8em; 135 | text-align: center; 136 | border-radius: 6px; 137 | padding: 5px 8px; 138 | margin: 5px; 139 | z-index: 1000; 140 | } 141 | .tooltipped .tooltiptext { 142 | visibility: visible; 143 | } 144 | -------------------------------------------------------------------------------- /api/buscar_um_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.listen(PORT) 137 | -------------------------------------------------------------------------------- /src/api_listagem_de_produtos.md: -------------------------------------------------------------------------------- 1 | # Listagem de produtos 2 | 3 | Novamente, esse capítulo toma como base o código do capítulo de criação de produto. 4 | 5 | ## Fluxo da informação 6 | 7 |

8 | sequence-diagram-information-flow 9 |

10 | 11 | ### Caso de sucesso 12 | > Requisição 13 | 14 | `URL`: `/products` 15 | 16 | `método`: `GET` 17 | 18 | `headers`: 19 | - `Authorization`: `Bearer j19fn19fhq9f0jr0adsyf08aefhf0` 20 | 21 | > Resposta 22 | 23 | `status code`: `200` 24 | 25 | `body`: 26 | ```json 27 | [ 28 | { 29 | "id": "cjuq20sms0001j35j9mbf246v", 30 | "name": "boneco do pikachu", 31 | "price": 5.34 32 | }, 33 | { 34 | "id": "cjuq21svd0002j35j8bdqh72w", 35 | "name": "garrafa dos simpsons", 36 | "price": 5.34 37 | } 38 | ] 39 | ``` 40 | 41 | ### Caso de sucesso com filtro 42 | > Requisição 43 | 44 | `URL`: `/products?name=boneco` 45 | 46 | `método`: `GET` 47 | 48 | `headers`: 49 | - `Authorization`: `Bearer j19fn19fhq9f0jr0adsyf08aefhf0` 50 | 51 | > Resposta 52 | 53 | `status code`: `200` 54 | 55 | `body`: 56 | ```json 57 | [ 58 | { 59 | "id": "cjuq20sms0001j35j9mbf246v", 60 | "name": "boneco do pikachu", 61 | "price": 5.34 62 | } 63 | ] 64 | ``` 65 | 66 | ### Caso de erro de autenticação 67 | > Requisição 68 | 69 | `URL`: `/products` 70 | 71 | `método`: `GET` 72 | 73 | `headers`: 74 | - `Authorization`: `Bearer TOKEN_INEXISTENTE` 75 | 76 | > Resposta 77 | 78 | `status code`: `401` 79 | 80 | `body`: 81 | ```json 82 | { 83 | "message": "Unauthorized" 84 | } 85 | ``` 86 | 87 | ## Implementação 88 | 89 | No caso a única coisa que precisamos fazer é enviar a lista de produtos que já temos na variável `products`. 90 | 91 | ```javascript 92 | // imports... 93 | 94 | // setup... 95 | 96 | const tokens = [] 97 | 98 | app.post('/auth', function (req, res) { 99 | // ... 100 | }) 101 | 102 | const products = [] 103 | 104 | function authorizationMiddleware (req, res, next) { 105 | // ... 106 | } 107 | 108 | app.post('/products', authorizationMiddleware, function (req, res) { 109 | // ... 110 | }) 111 | 112 | app.get('/products', authorizationMiddleware, function (req, res) { 113 | res.status(200).send(products) 114 | }) 115 | 116 | // app.listen... 117 | ``` 118 | 119 | É isso, simples assim! 120 | 121 | Lembrando que ao fazer a requisição, pode ser que não venha nenhum produto, por nada ter sido cadastrado. 122 | 123 |

124 | request-empty-products-list 125 |

126 | 127 | ### Adicionando filtros 128 | 129 | Agora complica um pouco, pois queremos receber via `query string` filtros, então ao invés de entregar todos os produtos sempre, veremos se o cliente enviou algum filtro, e dependendo iremos entregar listas filtradas. 130 | 131 | Código: 132 | 133 | ```javascript 134 | // ... 135 | 136 | app.get('/products', authorizationMiddleware, function (req, res) { 137 | const filters = [] 138 | 139 | if (req.query.name != null) { filters.push('name') } 140 | if (req.query.price != null) { filters.push('price') } 141 | 142 | let filteredProducts 143 | 144 | if (filters.length == 0) { 145 | filteredProducts = products 146 | } else { 147 | filteredProducts = products.filter(function (product) { 148 | const validations = [] 149 | 150 | if (filters.includes('name')) { 151 | validations.push(product.name.includes(req.query.name)) 152 | } 153 | 154 | if (filters.includes('price')) { 155 | validations.push(product.price == req.query.price) 156 | } 157 | 158 | return validations.some(function (validation) { 159 | return validation == true 160 | }) 161 | }) 162 | } 163 | 164 | res.status(200).send(filteredProducts) 165 | }) 166 | 167 | // ... 168 | ``` 169 | 170 | Pronto! Quem consumir a API pode enviar filtros :) 171 | 172 |

173 | Link para a versão final do código 174 |

175 | -------------------------------------------------------------------------------- /api/edicao_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.listen(PORT) 159 | -------------------------------------------------------------------------------- /src/api_autenticacao.md: -------------------------------------------------------------------------------- 1 | # Autenticação 2 | 3 | Antes de implementar a autenticação, observe abaixo o fluxo da informação: 4 | 5 | ## Fluxo da informação 6 | 7 |

8 | sequence-diagram-information-flow 9 |

10 | 11 | Olhando mais de uma perspectiva HTTP, ficaria algo assim: 12 | 13 | ### Caso de sucesso 14 | 15 | > Requisição 16 | 17 | `URL`: `/auth` 18 | 19 | `método`: `POST` 20 | 21 | `body`: 22 | ```json 23 | { 24 | "username": "admin", 25 | "password": "1234" 26 | } 27 | ``` 28 | 29 | > Resposta 30 | 31 | `status code`: `201` 32 | 33 | `body`: 34 | ```json 35 | { 36 | "token": "j19fn19fhq9f0jr0adsyf08aefhf0" 37 | } 38 | ``` 39 | 40 | ### Caso de erro 41 | 42 | > Requisição 43 | 44 | `URL`: `/auth` 45 | 46 | `método`: `POST` 47 | 48 | `body`: 49 | ```json 50 | { 51 | "username": "admin", 52 | "password": "SENHA ERRADA" 53 | } 54 | ``` 55 | 56 | > Resposta 57 | 58 | `status code`: `401` 59 | 60 | `body`: 61 | ```json 62 | { 63 | "message": "Unauthorized" 64 | } 65 | ``` 66 | 67 | ## Implementação 68 | 69 | Como é possível ver abaixo, temos dois caminhos, um que o cliente passou o usuário e senha corretos, e outro que não. 70 | 71 | ```javascript 72 | const express = require('express') 73 | const bodyParser = require('body-parser') 74 | const PORT = 8000 75 | 76 | const app = express() 77 | 78 | app.use(bodyParser.json()) 79 | 80 | app.post('/auth', function (req, res) { 81 | if (req.body.username == 'admin' && req.body.password == '1234') { 82 | res.status(201).send({ 83 | token: 'AQUI TEREMOS O TOKEN FUTURAMENTE' 84 | }) 85 | } else { 86 | res.status(401).send({ 87 | message: 'Unauthorized' 88 | }) 89 | } 90 | }) 91 | 92 | app.listen(PORT) 93 | ``` 94 | 95 | ### Testando via Postman: 96 | 97 | #### Caso de sucesso 98 | 99 |

100 | success-case 101 |

102 | 103 | #### Caso de erro 104 | 105 |

106 | error-case 107 |

108 | 109 | ### Ajustando o token! 110 | 111 | Para gerar o token, iremos utilizar de uma biblioteca que gera um texto aleatório grande que tem baixas chances de colisão: `cuid`. 112 | 113 | Portanto é necessário instalá-la utilizando `npm install --save cuid` em seu terminal. 114 | 115 | Vai ficar assim o código após a adição da biblioteca: 116 | 117 | ```javascript 118 | const express = require('express') 119 | const bodyParser = require('body-parser') 120 | const cuid = require('cuid') 121 | const PORT = 8000 122 | 123 | const app = express() 124 | 125 | app.use(bodyParser.json()) 126 | 127 | app.post('/auth', function (req, res) { 128 | if (req.body.username == 'admin' && req.body.password == '1234') { 129 | res.status(201).send({ 130 | token: cuid() 131 | }) 132 | } else { 133 | res.status(401).send({ 134 | message: 'Unauthorized' 135 | }) 136 | } 137 | }) 138 | 139 | app.listen(PORT) 140 | ``` 141 | 142 |

143 | cuid 144 |

145 | 146 | Além disso, precisamos também guardar esses tokens, para que quando alguém fizer uma requisição para criar ou listar um produto, por exemplo, possamos validar se o token enviado está correto. 147 | 148 | ```javascript 149 | // ... 150 | 151 | const tokens = [] 152 | 153 | app.post('/auth', function (req, res) { 154 | if (req.body.username == 'admin' && req.body.password == '1234') { 155 | const token = cuid() 156 | 157 | tokens.push(token) 158 | 159 | res.status(201).send({ 160 | token: token 161 | }) 162 | } else { 163 | res.status(401).send({ 164 | message: 'Unauthorized' 165 | }) 166 | } 167 | }) 168 | 169 | // ... 170 | ``` 171 | 172 | Pronto, terminamos! :) 173 | 174 |

175 | Link para a versão final do código 176 |

177 | -------------------------------------------------------------------------------- /src/node_requisicoes_criacao_de_produto.md: -------------------------------------------------------------------------------- 1 | # Criação de produto 2 | 3 | Agora que já temos o `token` podemos criar um produto. 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 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', 32 | price: 55.6 33 | } 34 | }) 35 | 36 | console.log('createProductResponse body:', createProductResponse.data) 37 | } 38 | 39 | run() 40 | ``` 41 | 42 |

43 | terminal-output-1 44 |

45 | 46 | Wow! Funciona :) 47 | 48 | Porém existe um problema com nosso script. Se tentarmos rodá-lo mais uma vez, sofreremos um erro. 49 | 50 |

51 | terminal-output-2 52 |

53 | 54 | Vamos investigar melhor o porque disso ocorrer. Para ficar mais fácil o `debug`, vamos colocar um `try { ... } catch (error) { ... }`. 55 | 56 | ```javascript 57 | // ... 58 | 59 | async function run () { 60 | try { 61 | const authResponse = await axios({ 62 | baseURL: BASE_URL, 63 | url: '/auth', 64 | method: 'POST', 65 | data: { 66 | username: 'admin', 67 | password: '1234' 68 | } 69 | }) 70 | 71 | const TOKEN = authResponse.data.token 72 | 73 | const createProductResponse = await axios({ 74 | baseURL: BASE_URL, 75 | url: '/products', 76 | method: 'POST', 77 | headers: { 78 | authorization: `Bearer ${TOKEN}` 79 | }, 80 | data: { 81 | name: 'boneco do goku', 82 | price: 55.6 83 | } 84 | }) 85 | 86 | console.log('createProductResponse body:', createProductResponse.data) 87 | } catch (error) { 88 | console.log('error body:', error.response.data) 89 | } 90 | } 91 | 92 | // ... 93 | ``` 94 | 95 | Agora o erro no terminal fica mais claro: 96 | 97 |

98 | terminal-output-3 99 |

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 | terminal-output-4 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 |

152 | Link para a versão final do código 153 |

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 | postman-request-reading-data 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 | postman-request-reading-headers 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 | postman-request-missing-express-header 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 | postman-request-passing-headers 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 | terminal-headers-server 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 |

129 | Link para a versão final do código 130 |

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 | postman-logo 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 | postman-start-screen 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 | postman-example-request 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 | diagram-request-postman-response-node 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 | postman-example-post-request 153 |

154 | 155 | Logs no terminal após uma requisição com o método `GET`, e outra com o `POST`: 156 | 157 |

158 | postman-example-post-request 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 |

166 | Link para a versão final do código 167 |

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 |

8 | sequence-diagram-information-flow 9 |

10 | 11 | ### Caso de sucesso 12 | > Requisição 13 | 14 | `URL`: `/products` 15 | 16 | `método`: `POST` 17 | 18 | `headers`: 19 | - `Authorization`: `Bearer j19fn19fhq9f0jr0adsyf08aefhf0` 20 | 21 | `body`: 22 | ```json 23 | { 24 | "name": "colher de cozinha", 25 | "price": 5.34 26 | } 27 | ``` 28 | 29 | > Resposta 30 | 31 | `status code`: `201` 32 | 33 | `body`: 34 | ```json 35 | { 36 | "id": "cjuppslcp0000t05j1tpf2jxv", 37 | "name": "colher de cozinha", 38 | "price": 5.34 39 | } 40 | ``` 41 | 42 | ### Caso de erro de autenticação 43 | > Requisição 44 | 45 | `URL`: `/products` 46 | 47 | `método`: `POST` 48 | 49 | `headers`: 50 | - `Authorization`: `Bearer TOKEN_INEXISTENTE` 51 | 52 | `body`: 53 | ```json 54 | { 55 | "name": "colher de cozinha", 56 | "price": 5.34 57 | } 58 | ``` 59 | 60 | > Resposta 61 | 62 | `status code`: `401` 63 | 64 | `body`: 65 | ```json 66 | { 67 | "message": "Unauthorized" 68 | } 69 | ``` 70 | 71 | ### Caso de erro de produto já cadastrado 72 | > Requisição 73 | 74 | `URL`: `/products` 75 | 76 | `método`: `POST` 77 | 78 | `headers`: 79 | - `Authorization`: `Bearer j19fn19fhq9f0jr0adsyf08aefhf0` 80 | 81 | `body`: 82 | ```json 83 | { 84 | "name": "colher de cozinha", 85 | "price": 5.34 86 | } 87 | ``` 88 | 89 | > Resposta 90 | 91 | `status code`: `400` 92 | 93 | `body`: 94 | ```json 95 | { 96 | "message": "This product name already exists" 97 | } 98 | ``` 99 | 100 | ### Caso de erro de nome ou preço faltando 101 | > Requisição 102 | 103 | `URL`: `/products` 104 | 105 | `método`: `POST` 106 | 107 | `headers`: 108 | - `Authorization`: `Bearer j19fn19fhq9f0jr0adsyf08aefhf0` 109 | 110 | `body`: 111 | ```json 112 | { 113 | "price": 5.34 114 | } 115 | ``` 116 | 117 | > Resposta 118 | 119 | `status code`: `400` 120 | 121 | `body`: 122 | ```json 123 | { 124 | "message": "The product name is missing" 125 | } 126 | ``` 127 | 128 | ## Implementação 129 | 130 | ### Lógica principal 131 | 132 | Primeiro iremos implementar a lógica principal, depois iremos cuidar da autenticação. 133 | 134 | ```javascript 135 | // imports/setup... 136 | 137 | // app.post('/auth', function (req, res) { 138 | // ... 139 | // }) 140 | 141 | const products = [] 142 | 143 | app.post('/products', function (req, res) { 144 | const newProduct = { 145 | id: cuid(), 146 | name: req.body.name, 147 | price: req.body.price 148 | } 149 | 150 | products.push(newProduct) 151 | 152 | res.status(201).send(newProduct) 153 | }) 154 | 155 | // app.listen ... 156 | ``` 157 | 158 | No caso nosso produto tem apenas nome (`name`) e preço (`price`). O campo `id`, é único e é gerado no backend, o usuário não o informa. 159 | 160 | No retorno simplesmente retornamos o produto criado caso tenha dado certo. 161 | 162 |

163 | postman-creation-request 164 |

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 | postman-auth-request 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 |

297 | Link para a versão final do código 298 |

299 | -------------------------------------------------------------------------------- /book/css/chrome.css: -------------------------------------------------------------------------------- 1 | /* CSS for UI elements (a.k.a. chrome) */ 2 | 3 | @import 'variables.css'; 4 | 5 | ::-webkit-scrollbar { 6 | background: var(--bg); 7 | } 8 | ::-webkit-scrollbar-thumb { 9 | background: var(--scrollbar); 10 | } 11 | 12 | #searchresults a, 13 | .content a:link, 14 | a:visited, 15 | a > .hljs { 16 | color: var(--links); 17 | } 18 | 19 | /* Menu Bar */ 20 | 21 | #menu-bar { 22 | position: -webkit-sticky; 23 | position: sticky; 24 | top: 0; 25 | z-index: 101; 26 | margin: auto calc(0px - var(--page-padding)); 27 | } 28 | #menu-bar > #menu-bar-sticky-container { 29 | display: flex; 30 | flex-wrap: wrap; 31 | background-color: var(--bg); 32 | border-bottom-color: var(--bg); 33 | border-bottom-width: 1px; 34 | border-bottom-style: solid; 35 | } 36 | .js #menu-bar > #menu-bar-sticky-container { 37 | transition: transform 0.3s; 38 | } 39 | #menu-bar.bordered > #menu-bar-sticky-container { 40 | border-bottom-color: var(--table-border-color); 41 | } 42 | #menu-bar i, #menu-bar .icon-button { 43 | position: relative; 44 | padding: 0 8px; 45 | z-index: 10; 46 | line-height: 50px; 47 | cursor: pointer; 48 | transition: color 0.5s; 49 | } 50 | @media only screen and (max-width: 420px) { 51 | #menu-bar i, #menu-bar .icon-button { 52 | padding: 0 5px; 53 | } 54 | } 55 | 56 | .icon-button { 57 | border: none; 58 | background: none; 59 | padding: 0; 60 | color: inherit; 61 | } 62 | .icon-button i { 63 | margin: 0; 64 | } 65 | 66 | .right-buttons { 67 | margin: 0 15px; 68 | } 69 | .right-buttons a { 70 | text-decoration: none; 71 | } 72 | 73 | html:not(.sidebar-visible) #menu-bar:not(:hover).folded > #menu-bar-sticky-container { 74 | transform: translateY(-60px); 75 | } 76 | 77 | .left-buttons { 78 | display: flex; 79 | margin: 0 5px; 80 | } 81 | .no-js .left-buttons { 82 | display: none; 83 | } 84 | 85 | .menu-title { 86 | display: inline-block; 87 | font-weight: 200; 88 | font-size: 20px; 89 | line-height: 50px; 90 | text-align: center; 91 | margin: 0; 92 | flex: 1; 93 | white-space: nowrap; 94 | overflow: hidden; 95 | text-overflow: ellipsis; 96 | } 97 | .js .menu-title { 98 | cursor: pointer; 99 | } 100 | 101 | .menu-bar, 102 | .menu-bar:visited, 103 | .nav-chapters, 104 | .nav-chapters:visited, 105 | .mobile-nav-chapters, 106 | .mobile-nav-chapters:visited, 107 | .menu-bar .icon-button, 108 | .menu-bar a i { 109 | color: var(--icons); 110 | } 111 | 112 | .menu-bar i:hover, 113 | .menu-bar .icon-button:hover, 114 | .nav-chapters:hover, 115 | .mobile-nav-chapters i:hover { 116 | color: var(--icons-hover); 117 | } 118 | 119 | /* Nav Icons */ 120 | 121 | .nav-chapters { 122 | font-size: 2.5em; 123 | text-align: center; 124 | text-decoration: none; 125 | 126 | position: fixed; 127 | top: 50px; /* Height of menu-bar */ 128 | bottom: 0; 129 | margin: 0; 130 | max-width: 150px; 131 | min-width: 90px; 132 | 133 | display: flex; 134 | justify-content: center; 135 | align-content: center; 136 | flex-direction: column; 137 | 138 | transition: color 0.5s; 139 | } 140 | 141 | .nav-chapters:hover { text-decoration: none; } 142 | 143 | .nav-wrapper { 144 | margin-top: 50px; 145 | display: none; 146 | } 147 | 148 | .mobile-nav-chapters { 149 | font-size: 2.5em; 150 | text-align: center; 151 | text-decoration: none; 152 | width: 90px; 153 | border-radius: 5px; 154 | background-color: var(--sidebar-bg); 155 | } 156 | 157 | .previous { 158 | float: left; 159 | } 160 | 161 | .next { 162 | float: right; 163 | right: var(--page-padding); 164 | } 165 | 166 | @media only screen and (max-width: 1080px) { 167 | .nav-wide-wrapper { display: none; } 168 | .nav-wrapper { display: block; } 169 | } 170 | 171 | @media only screen and (max-width: 1380px) { 172 | .sidebar-visible .nav-wide-wrapper { display: none; } 173 | .sidebar-visible .nav-wrapper { display: block; } 174 | } 175 | 176 | /* Inline code */ 177 | 178 | :not(pre) > .hljs { 179 | display: inline-block; 180 | vertical-align: middle; 181 | padding: 0.1em 0.3em; 182 | border-radius: 3px; 183 | color: var(--inline-code-color); 184 | } 185 | 186 | a:hover > .hljs { 187 | text-decoration: underline; 188 | } 189 | 190 | pre { 191 | position: relative; 192 | } 193 | pre > .buttons { 194 | position: absolute; 195 | z-index: 100; 196 | right: 5px; 197 | top: 5px; 198 | 199 | color: var(--sidebar-fg); 200 | cursor: pointer; 201 | } 202 | pre > .buttons :hover { 203 | color: var(--sidebar-active); 204 | } 205 | pre > .buttons i { 206 | margin-left: 8px; 207 | } 208 | pre > .buttons button { 209 | color: inherit; 210 | background: transparent; 211 | border: none; 212 | cursor: inherit; 213 | } 214 | pre > .result { 215 | margin-top: 10px; 216 | } 217 | 218 | /* Search */ 219 | 220 | #searchresults a { 221 | text-decoration: none; 222 | } 223 | 224 | mark { 225 | border-radius: 2px; 226 | padding: 0 3px 1px 3px; 227 | margin: 0 -3px -1px -3px; 228 | background-color: var(--search-mark-bg); 229 | transition: background-color 300ms linear; 230 | cursor: pointer; 231 | } 232 | 233 | mark.fade-out { 234 | background-color: rgba(0,0,0,0) !important; 235 | cursor: auto; 236 | } 237 | 238 | .searchbar-outer { 239 | margin-left: auto; 240 | margin-right: auto; 241 | max-width: var(--content-max-width); 242 | } 243 | 244 | #searchbar { 245 | width: 100%; 246 | margin: 5px auto 0px auto; 247 | padding: 10px 16px; 248 | transition: box-shadow 300ms ease-in-out; 249 | border: 1px solid var(--searchbar-border-color); 250 | border-radius: 3px; 251 | background-color: var(--searchbar-bg); 252 | color: var(--searchbar-fg); 253 | } 254 | #searchbar:focus, 255 | #searchbar.active { 256 | box-shadow: 0 0 3px var(--searchbar-shadow-color); 257 | } 258 | 259 | .searchresults-header { 260 | font-weight: bold; 261 | font-size: 1em; 262 | padding: 18px 0 0 5px; 263 | color: var(--searchresults-header-fg); 264 | } 265 | 266 | .searchresults-outer { 267 | margin-left: auto; 268 | margin-right: auto; 269 | max-width: var(--content-max-width); 270 | border-bottom: 1px dashed var(--searchresults-border-color); 271 | } 272 | 273 | ul#searchresults { 274 | list-style: none; 275 | padding-left: 20px; 276 | } 277 | ul#searchresults li { 278 | margin: 10px 0px; 279 | padding: 2px; 280 | border-radius: 2px; 281 | } 282 | ul#searchresults li.focus { 283 | background-color: var(--searchresults-li-bg); 284 | } 285 | ul#searchresults span.teaser { 286 | display: block; 287 | clear: both; 288 | margin: 5px 0 0 20px; 289 | font-size: 0.8em; 290 | } 291 | ul#searchresults span.teaser em { 292 | font-weight: bold; 293 | font-style: normal; 294 | } 295 | 296 | /* Sidebar */ 297 | 298 | .sidebar { 299 | position: fixed; 300 | left: 0; 301 | top: 0; 302 | bottom: 0; 303 | width: var(--sidebar-width); 304 | overflow-y: auto; 305 | padding: 10px 10px; 306 | font-size: 0.875em; 307 | box-sizing: border-box; 308 | -webkit-overflow-scrolling: touch; 309 | overscroll-behavior-y: contain; 310 | background-color: var(--sidebar-bg); 311 | color: var(--sidebar-fg); 312 | } 313 | .js .sidebar { 314 | transition: transform 0.3s; /* Animation: slide away */ 315 | } 316 | .sidebar code { 317 | line-height: 2em; 318 | } 319 | .sidebar-hidden .sidebar { 320 | transform: translateX(calc(0px - var(--sidebar-width))); 321 | } 322 | .sidebar::-webkit-scrollbar { 323 | background: var(--sidebar-bg); 324 | } 325 | .sidebar::-webkit-scrollbar-thumb { 326 | background: var(--scrollbar); 327 | } 328 | 329 | .sidebar-visible .page-wrapper { 330 | transform: translateX(var(--sidebar-width)); 331 | } 332 | @media only screen and (min-width: 620px) { 333 | .sidebar-visible .page-wrapper { 334 | transform: none; 335 | margin-left: var(--sidebar-width); 336 | } 337 | } 338 | 339 | .chapter { 340 | list-style: none outside none; 341 | padding-left: 0; 342 | line-height: 2.2em; 343 | } 344 | .chapter li { 345 | color: var(--sidebar-non-existant); 346 | } 347 | .chapter li a { 348 | color: var(--sidebar-fg); 349 | display: block; 350 | padding: 0; 351 | text-decoration: none; 352 | } 353 | .chapter li a:hover { text-decoration: none } 354 | .chapter li .active, 355 | a:hover { 356 | /* Animate color change */ 357 | color: var(--sidebar-active); 358 | } 359 | 360 | .spacer { 361 | width: 100%; 362 | height: 3px; 363 | margin: 5px 0px; 364 | } 365 | .chapter .spacer { 366 | background-color: var(--sidebar-spacer); 367 | } 368 | 369 | @media (-moz-touch-enabled: 1), (pointer: coarse) { 370 | .chapter li a { padding: 5px 0; } 371 | .spacer { margin: 10px 0; } 372 | } 373 | 374 | .section { 375 | list-style: none outside none; 376 | padding-left: 20px; 377 | line-height: 1.9em; 378 | } 379 | 380 | /* Theme Menu Popup */ 381 | 382 | .theme-popup { 383 | position: absolute; 384 | left: 10px; 385 | top: 50px; 386 | z-index: 1000; 387 | border-radius: 4px; 388 | font-size: 0.7em; 389 | color: var(--fg); 390 | background: var(--theme-popup-bg); 391 | border: 1px solid var(--theme-popup-border); 392 | margin: 0; 393 | padding: 0; 394 | list-style: none; 395 | display: none; 396 | } 397 | .theme-popup .default { 398 | color: var(--icons); 399 | } 400 | .theme-popup .theme { 401 | width: 100%; 402 | border: 0; 403 | margin: 0; 404 | padding: 2px 10px; 405 | line-height: 25px; 406 | white-space: nowrap; 407 | text-align: left; 408 | cursor: pointer; 409 | color: inherit; 410 | background: inherit; 411 | font-size: inherit; 412 | } 413 | .theme-popup .theme:hover { 414 | background-color: var(--theme-hover); 415 | } 416 | .theme-popup .theme:hover:first-child, 417 | .theme-popup .theme:hover:last-child { 418 | border-top-left-radius: inherit; 419 | border-top-right-radius: inherit; 420 | } 421 | -------------------------------------------------------------------------------- /book/clipboard.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * clipboard.js v1.6.1 3 | * https://zenorocha.github.io/clipboard.js 4 | * 5 | * Licensed MIT © Zeno Rocha 6 | */ 7 | !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var t;t="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,t.Clipboard=e()}}(function(){var e,t,n;return function e(t,n,o){function i(a,c){if(!n[a]){if(!t[a]){var l="function"==typeof require&&require;if(!c&&l)return l(a,!0);if(r)return r(a,!0);var u=new Error("Cannot find module '"+a+"'");throw u.code="MODULE_NOT_FOUND",u}var s=n[a]={exports:{}};t[a][0].call(s.exports,function(e){var n=t[a][1][e];return i(n?n:e)},s,s.exports,e,t,n,o)}return n[a].exports}for(var r="function"==typeof require&&require,a=0;a0&&void 0!==arguments[0]?arguments[0]:{};this.action=t.action,this.emitter=t.emitter,this.target=t.target,this.text=t.text,this.trigger=t.trigger,this.selectedText=""}},{key:"initSelection",value:function e(){this.text?this.selectFake():this.target&&this.selectTarget()}},{key:"selectFake",value:function e(){var t=this,n="rtl"==document.documentElement.getAttribute("dir");this.removeFake(),this.fakeHandlerCallback=function(){return t.removeFake()},this.fakeHandler=document.body.addEventListener("click",this.fakeHandlerCallback)||!0,this.fakeElem=document.createElement("textarea"),this.fakeElem.style.fontSize="12pt",this.fakeElem.style.border="0",this.fakeElem.style.padding="0",this.fakeElem.style.margin="0",this.fakeElem.style.position="absolute",this.fakeElem.style[n?"right":"left"]="-9999px";var o=window.pageYOffset||document.documentElement.scrollTop;this.fakeElem.style.top=o+"px",this.fakeElem.setAttribute("readonly",""),this.fakeElem.value=this.text,document.body.appendChild(this.fakeElem),this.selectedText=(0,i.default)(this.fakeElem),this.copyText()}},{key:"removeFake",value:function e(){this.fakeHandler&&(document.body.removeEventListener("click",this.fakeHandlerCallback),this.fakeHandler=null,this.fakeHandlerCallback=null),this.fakeElem&&(document.body.removeChild(this.fakeElem),this.fakeElem=null)}},{key:"selectTarget",value:function e(){this.selectedText=(0,i.default)(this.target),this.copyText()}},{key:"copyText",value:function e(){var t=void 0;try{t=document.execCommand(this.action)}catch(e){t=!1}this.handleResult(t)}},{key:"handleResult",value:function e(t){this.emitter.emit(t?"success":"error",{action:this.action,text:this.selectedText,trigger:this.trigger,clearSelection:this.clearSelection.bind(this)})}},{key:"clearSelection",value:function e(){this.target&&this.target.blur(),window.getSelection().removeAllRanges()}},{key:"destroy",value:function e(){this.removeFake()}},{key:"action",set:function e(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"copy";if(this._action=t,"copy"!==this._action&&"cut"!==this._action)throw new Error('Invalid "action" value, use either "copy" or "cut"')},get:function e(){return this._action}},{key:"target",set:function e(t){if(void 0!==t){if(!t||"object"!==("undefined"==typeof t?"undefined":r(t))||1!==t.nodeType)throw new Error('Invalid "target" value, use a valid Element');if("copy"===this.action&&t.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if("cut"===this.action&&(t.hasAttribute("readonly")||t.hasAttribute("disabled")))throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes');this._target=t}},get:function e(){return this._target}}]),e}();e.exports=c})},{select:5}],8:[function(t,n,o){!function(i,r){if("function"==typeof e&&e.amd)e(["module","./clipboard-action","tiny-emitter","good-listener"],r);else if("undefined"!=typeof o)r(n,t("./clipboard-action"),t("tiny-emitter"),t("good-listener"));else{var a={exports:{}};r(a,i.clipboardAction,i.tinyEmitter,i.goodListener),i.clipboard=a.exports}}(this,function(e,t,n,o){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function a(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function c(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}function l(e,t){var n="data-clipboard-"+e;if(t.hasAttribute(n))return t.getAttribute(n)}var u=i(t),s=i(n),f=i(o),d=function(){function e(e,t){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:{};this.action="function"==typeof t.action?t.action:this.defaultAction,this.target="function"==typeof t.target?t.target:this.defaultTarget,this.text="function"==typeof t.text?t.text:this.defaultText}},{key:"listenClick",value:function e(t){var n=this;this.listener=(0,f.default)(t,"click",function(e){return n.onClick(e)})}},{key:"onClick",value:function e(t){var n=t.delegateTarget||t.currentTarget;this.clipboardAction&&(this.clipboardAction=null),this.clipboardAction=new u.default({action:this.action(n),target:this.target(n),text:this.text(n),trigger:n,emitter:this})}},{key:"defaultAction",value:function e(t){return l("action",t)}},{key:"defaultTarget",value:function e(t){var n=l("target",t);if(n)return document.querySelector(n)}},{key:"defaultText",value:function e(t){return l("text",t)}},{key:"destroy",value:function e(){this.listener.destroy(),this.clipboardAction&&(this.clipboardAction.destroy(),this.clipboardAction=null)}}],[{key:"isSupported",value:function e(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:["copy","cut"],n="string"==typeof t?[t]:t,o=!!document.queryCommandSupported;return n.forEach(function(e){o=o&&!!document.queryCommandSupported(e)}),o}}]),t}(s.default);e.exports=h})},{"./clipboard-action":7,"good-listener":4,"tiny-emitter":6}]},{},[8])(8)}); -------------------------------------------------------------------------------- /book/node_requisicoes_delecao_de_produto.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Deleção de produto - livro-desenvolvimento-web-basico 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 39 | 40 | 41 | 55 | 56 | 57 | 64 | 65 | 66 | 76 | 77 | 80 | 81 |
82 | 83 |
84 | 85 | 118 | 119 | 120 | 130 | 131 | 132 | 133 | 140 | 141 |
142 |
143 |

Deleção de produto

144 |

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.

146 | 147 |
148 | 149 | 165 |
166 |
167 | 168 | 181 | 182 |
183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | -------------------------------------------------------------------------------- /book/vamos_fazer_requisicoes_via_node.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Vamos fazer requisições via Node.js! - livro-desenvolvimento-web-basico 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 39 | 40 | 41 | 55 | 56 | 57 | 64 | 65 | 66 | 76 | 77 | 80 | 81 |
82 | 83 |
84 | 85 | 118 | 119 | 120 | 130 | 131 | 132 | 133 | 140 | 141 |
142 |
143 |

Vamos fazer requisições via Node.js!

144 |

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.

145 |

Logo, o conteúdo desse capítulo depende do anterior.

146 | 147 |
148 | 149 | 165 |
166 |
167 | 168 | 181 | 182 |
183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | -------------------------------------------------------------------------------- /book/node_requisicoes_edicao_de_produto.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Edição de produto - livro-desenvolvimento-web-basico 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 39 | 40 | 41 | 55 | 56 | 57 | 64 | 65 | 66 | 76 | 77 | 80 | 81 |
82 | 83 |
84 | 85 | 118 | 119 | 120 | 130 | 131 | 132 | 133 | 140 | 141 |
142 |
143 |

Edição de produto

144 |

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.

146 | 147 |
148 | 149 | 165 |
166 |
167 | 168 | 181 | 182 |
183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | -------------------------------------------------------------------------------- /book/node_requisicoes_somente_um_produto.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Buscar somente um produto - livro-desenvolvimento-web-basico 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 39 | 40 | 41 | 55 | 56 | 57 | 64 | 65 | 66 | 76 | 77 | 80 | 81 |
82 | 83 |
84 | 85 | 118 | 119 | 120 | 130 | 131 | 132 | 133 | 140 | 141 |
142 |
143 |

Buscar somente um produto

144 |

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.

146 | 147 |
148 | 149 | 165 |
166 |
167 | 168 | 181 | 182 |
183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | -------------------------------------------------------------------------------- /book/o_que_e_uma_sdk.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | O que é uma SDK? - livro-desenvolvimento-web-basico 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 39 | 40 | 41 | 55 | 56 | 57 | 64 | 65 | 66 | 76 | 77 | 80 | 81 |
82 | 83 |
84 | 85 | 118 | 119 | 120 | 130 | 131 | 132 | 133 | 140 | 141 |
142 |
143 |

O que é uma SDK?

144 |

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.

151 | 152 |
153 | 154 | 166 |
167 |
168 | 169 | 178 | 179 |
180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | -------------------------------------------------------------------------------- /book/o_que_e_um_servidor_web_http.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | O que é um servidor Web HTTP? - livro-desenvolvimento-web-basico 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 39 | 40 | 41 | 55 | 56 | 57 | 64 | 65 | 66 | 76 | 77 | 80 | 81 |
82 | 83 |
84 | 85 | 118 | 119 | 120 | 130 | 131 | 132 | 133 | 140 | 141 |
142 |
143 |

O que é um servidor Web HTTP?

144 |

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:

148 |

149 | drawing1 150 |

151 | 152 |
153 | 154 | 170 |
171 |
172 | 173 | 186 | 187 |
188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | --------------------------------------------------------------------------------