{"id":9408,"date":"2020-02-21T14:45:10","date_gmt":"2020-02-21T17:45:10","guid":{"rendered":"https:\/\/geekblogti.wpengine.com\/?p=9408"},"modified":"2024-10-11T11:41:40","modified_gmt":"2024-10-11T14:41:40","slug":"node-js-tutorial-twitter-clone-full-stack-app-parte-1","status":"publish","type":"post","link":"https:\/\/www.geekhunter.com/pt/blog\/node-js-tutorial-twitter-clone-full-stack-app-parte-1\/","title":{"rendered":"Twitter Clone full stack app &#8211; Parte 1: Node JS tutorial"},"content":{"rendered":"\r\n<p class=\"wp-block-paragraph\">\u00a0Node JS tutorial? Temos! Este artigo \u00e9 um bom lugar para entender os conceitos e se sentir mais confiante na hora de criar aplica\u00e7\u00f5es de ponta a ponta. Criaremos uma r\u00e9plica do Twitter com um passo a passo bem did\u00e1tico.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Dividiremos em 2 partes: na primeira parte aprenderemos a criar um servidor <a href=\"https:\/\/www.geekhunter.com\/pt\/blog\/como-comecar-em-web-back-end-do-zero\/\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\"back-end (abre numa nova aba)\">back-end<\/a> para nossa aplica\u00e7\u00e3o utilizando <a href=\"https:\/\/www.geekhunter.com\/pt\/blog\/por-que-usar-node-js-uma-justificativa-passo-a-passo\/\" target=\"_blank\" rel=\"noreferrer noopener\">Node JS<\/a> tutorial, Express e outras ferramentas que nos ajudar\u00e3o no processo de desenvolvimento. No fim, teremos uma aplica\u00e7\u00e3o completa, incluindo rotas, autentica\u00e7\u00e3o e persist\u00eancia de dados.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">J\u00e1 na segunda parte faremos a parte do <a href=\"https:\/\/www.geekhunter.com\/pt\/blog\/mercado-front-end-da-origem-ate-o-futuro\/\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\"front-end (abre numa nova aba)\">front-end<\/a> integrando com o nosso servidor e criando uma vers\u00e3o totalmente funcional.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">O tutorial visa atingir pessoas com pouco ou nenhum conhecimento sobre programa\u00e7\u00e3o, sendo que n\u00e3o \u00e9 necess\u00e1rio conhecimento avan\u00e7ado para conseguir acompanhar. Por\u00e9m, \u00e9 recomendado um conhecimento b\u00e1sico de <a href=\"https:\/\/www.geekhunter.com\/pt\/blog\/linguagem-javascript-um-guia-para-iniciantes\/\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\"JavaScript (abre numa nova aba)\">JavaScript<\/a>.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Tamb\u00e9m \u00e9 indicado que voc\u00ea tente fazer seguindo os passos do tutorial para ir aprendendo com seus pr\u00f3prios erros e para que os conceitos fixem melhor na sua mente. O c\u00f3digo da aplica\u00e7\u00e3o est\u00e1 em no meu <a href=\"https:\/\/github.com\/picolloo\/twitter-app\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\"GitHub  (abre numa nova aba)\">GitHub <\/a>caso prefira ir direto para a implementa\u00e7\u00e3o.<\/p>\r\n\r\n\r\n\r\n<h2 class=\"wp-block-heading\">Requisitos para fazer uma aplica\u00e7\u00e3o<\/h2>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">\u00c9 necess\u00e1rio que voc\u00ea:<\/p>\r\n\r\n\r\n\r\n<ul class=\"wp-block-list\">\r\n<li>Instale o <a href=\"https:\/\/nodejs.org\/en\/\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\"Node JS (abre numa nova aba)\">Node JS<\/a>;<\/li>\r\n<li><a href=\"https:\/\/yarnpkg.com\/\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\"YARN  (abre numa nova aba)\">YARN <\/a>ou\u00a0qualquer gerenciador de pacotes de sua prefer\u00eancia;<\/li>\r\n<li><a href=\"https:\/\/www.geekhunter.com\/pt\/blog\/melhores-extensoes-para-visual-studio-code-em-2020\/\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\"VSCode  (abre numa nova aba)\">VSCode <\/a>ou IDE de sua prefer\u00eancia.<\/li>\r\n<\/ul>\r\n\r\n\r\n\r\n<h2 class=\"wp-block-heading\">Iniciando Node JS tutorial<\/h2>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Para criarmos o ambiente de desenvolvimento temos que criar um diret\u00f3rio e inicializar o projeto:\u00a0<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code><strong>mkdir<\/strong> twitter-<strong>app<\/strong><br \/><strong>cd<\/strong> twitter-<strong>app<\/strong><br \/>yarn init -y <\/code><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Dentro do diret\u00f3rio, voc\u00ea deve encontrar um arquivo chamado <em>package.json<\/em>, que \u00e9 o arquivo de configura\u00e7\u00e3o inicial para aplica\u00e7\u00f5es que se baseiam em Node JS tutorial, por exemplo. Nele s\u00e3o declarados as depend\u00eancias do projeto, os scripts dispon\u00edveis e vers\u00e3o atual do software. No nosso caso este arquivo est\u00e1 com a configura\u00e7\u00e3o m\u00ednima de um projeto.<\/p>\r\n\r\n\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Nosso c\u00f3digo principal ficar\u00e1 dentro do arquivo <em><code>src\/app.js<\/code><\/em>, ent\u00e3o vamos cri\u00e1-lo: <em><code>mkdir\/src &amp;&amp; touch app.js<\/code><\/em>. No caso de voc\u00ea n\u00e3o ter notado o <em><code>package.json<\/code><\/em> apresenta uma refer\u00eancia para o c\u00f3digo principal chamado de <em><code>main<\/code><\/em>, como esse valor n\u00e3o bate com o nosso arquivo devemos alter\u00e1-lo para <em><code>src\/app.js<\/code><\/em>.<\/p>\r\n\r\n\r\n\r\n<h2 class=\"wp-block-heading\">Primeiros passos com Express<\/h2>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/expressjs.com\/\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\"Express  (abre numa nova aba)\">Express <\/a>\u00e9 uma biblioteca que facilita o desenvolvimento de servidores, ainda mais quando se trata de Node JS tutorial. Com ela podemos implementar rotas, middlewares e gerenciadores de erros de forma f\u00e1cil e pr\u00e1tica.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Atualmente, Express \u00e9 uma das libs mais usadas para cria\u00e7\u00e3o de servers em <a href=\"https:\/\/www.geekhunter.com\/pt\/blog\/como-aprender-javascript-como-javascripter\/\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\"JavaScript (abre numa nova aba)\">JavaScript<\/a>, com uma curva de aprendizado \u00e9 relativamente f\u00e1cil de construir aplica\u00e7\u00f5es totalmente \u00fateis.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">A cria\u00e7\u00e3o do Express ser\u00e1 feita no arquivo <em><code>src\/app.js<\/code><\/em>, precisamos de:<\/p>\r\n\r\n\r\n\r\n<ul class=\"wp-block-list\">\r\n<li>Uma inst\u00e2ncia do Express, chamaremos de <em>app<\/em>;<\/li>\r\n<li>A porta em que o servidor ficar\u00e1 ouvindo;<\/li>\r\n<li>A fun\u00e7\u00e3o <em>listen <\/em>do <em>app <\/em>que serve para fazer com que nosso server escute na porta espec\u00edfica.<\/li>\r\n<\/ul>\r\n\r\n\r\n\r\n<figure class=\"wp-block-table\">\r\n<table>\r\n<tbody>\r\n<tr>\r\n<td><code><strong>const<\/strong> express = require(\"express\");<br \/><strong>const<\/strong> app = express();<br \/><strong>const<\/strong> PORT = 3333;<br \/>app.listen(PORT, () =&gt; {<br \/>\u00a0 console.log(`Server running on port: ${PORT}`);<br \/>});<\/code><\/td>\r\n<\/tr>\r\n<\/tbody>\r\n<\/table>\r\n<\/figure>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Podemos iniciar nosso servidor utilizando<code> <em>node src\/app.js<\/em><\/code>, por\u00e9m at\u00e9 agora n\u00e3o implementamos nenhuma intera\u00e7\u00e3o com o usu\u00e1rio, nossa aplica\u00e7\u00e3o est\u00e1 isolada de qualquer requisi\u00e7\u00e3o.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Para que possamos receber requisi\u00e7\u00f5es, precisamos implementar as chamadas <em>rotas<\/em> de cada recurso que o usu\u00e1rio ter\u00e1 acesso. Em cada uma delas ser\u00e3o utilizados os m\u00e9todos HTTP que servem para identificar cada a\u00e7\u00e3o do cliente.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Existem diversos m\u00e9todos que podem ser usados, mas os mais utilizados s\u00e3o:<\/p>\r\n\r\n\r\n\r\n<ul class=\"wp-block-list\">\r\n<li>GET &#8211; Buscar um recurso no servidor;<\/li>\r\n<li>POST &#8211; Criar um recurso no servidor;<\/li>\r\n<li>PUT &#8211; Atualizar um recurso no servidor;<\/li>\r\n<li>DELETE &#8211; Apagar um recurso no servidor.<\/li>\r\n<\/ul>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">No caso do Express, o objeto <em>app<\/em> j\u00e1 possui os m\u00e9todos espec\u00edficos para cada m\u00e9todo HTTP, o que facilita muito o desenvolvimento.<\/p>\r\n\r\n\r\n\r\n<figure class=\"wp-block-table\">\r\n<table>\r\n<tbody>\r\n<tr>\r\n<td><code><strong>const<\/strong> express = require(\"express\");<br \/><br \/><strong>const<\/strong> app = express();<br \/><br \/>app.get(\"\/\", (req, res) =&gt; {<br \/>\u00a0 res.send(\"Hello GeekHunter! \ud83e\udd13\")<br \/>})<br \/><br \/><strong>const<\/strong> PORT = 3333;<br \/><br \/>app.listen(PORT, () =&gt; {<br \/>\u00a0 console.log(`Server running on port: ${PORT}`);<br \/>}<\/code><\/td>\r\n<\/tr>\r\n<\/tbody>\r\n<\/table>\r\n<\/figure>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Perceba que utilizamos o m\u00e9todo <em>get<\/em> do objeto <em>app,<\/em> ele recebe como primeiro par\u00e2metro a url em que a rota ser\u00e1 ativada\u00a0 (no exemplo a seguir o &#8216;\/&#8217; \u00e9 referente a ra\u00edz do projeto, no caso <em>localhost:3333\/), <\/em>e como segundo par\u00e2metro uma fun\u00e7\u00e3o que recebe 2 argumentos, <em>req, <\/em>que \u00e9 a requisi\u00e7\u00e3o vinda do nosso cliente, e <em>res<\/em> que \u00e9 a resposta do servidor para quem o chamou.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Agora podemos iniciar nosso servidor atrav\u00e9s da chamada <em>node src\/app.js<\/em>, que far\u00e1 com que nosso server seja criado e fique ouvindo a porta 3333. Para sabermos se tudo ocorreu corretamente poderemos acessar nosso server pelo browser utilizando a url: <em>localhost:3333<\/em><\/p>\r\n\r\n\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Para que n\u00e3o seja necess\u00e1rio que lembremos todos os scripts que nossa aplica\u00e7\u00e3o possui, podemos alterar nosso <em>package.json<\/em> para rodar scripts por n\u00f3s. No caso, podemos adicionar o script que inicia o servidor atrav\u00e9s da seguinte altera\u00e7\u00e3o:<\/p>\r\n\r\n\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">&#8211; Adicionamos a propriedade <em>scripts<\/em> que servir\u00e1 para guardar os <em>scripts <\/em>necess\u00e1rios para nossa aplica\u00e7\u00e3o.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">&#8211; Dentro de <em>scripts <\/em>foi adicionado o <em>start<\/em> que roda a aplica\u00e7\u00e3o por n\u00f3s sem que precisemos utilizar o comando inteiro toda vez, apenas utilizando o comando <em>yarn start<\/em>.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">&nbsp;<\/p>\r\n\r\n\r\n\r\n<h2 class=\"wp-block-heading\">Middlewares<\/h2>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">A arquitetura do Express consiste em fazer a requisi\u00e7\u00e3o passar por um caminho determinado de forma que seja tratada corretamente, esse caminho \u00e9 feito de fun\u00e7\u00f5es chamadas <em>middlewares<\/em>. Cada middleware \u00e9 um diferente tipo de par\u00e2metro, que serve para resolver diferentes tipos de fun\u00e7\u00f5es dentro do servidor como roteamento, tratamento de erros e autentica\u00e7\u00e3o, por exemplo.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Precisaremos implementar alguns middlewares para que nosso servidor se consiga tratar as requisi\u00e7\u00f5es da melhor maneira poss\u00edvel. Para isso, precisamos primeiro entender como s\u00e3o estruturados e como utiliz\u00e1-los corretamente. Se formos at\u00e9 a <a href=\"https:\/\/expressjs.com\/en\/guide\/using-middleware.html\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\"documenta\u00e7\u00e3o oficial do Express  (abre numa nova aba)\">documenta\u00e7\u00e3o oficial do Express <\/a>encontraremos uma se\u00e7\u00e3o espec\u00edfica para o uso de middlewares.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Pela defini\u00e7\u00e3o, middlewares s\u00e3o fun\u00e7\u00f5es que recebem como um dos par\u00e2metros a requisi\u00e7\u00e3o que chega ao servidor, nele podem ser acessadas propriedades, alteradas se preciso antes que cheguem na rota ou at\u00e9 adicionadas novas. Um exemplo de middleware que calcula o tempo a dura\u00e7\u00e3o do tratamento de requisi\u00e7\u00e3o seria como o seguinte:<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code><strong>var<\/strong> app = express()<\/code><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code>app.use(<strong>function<\/strong> (req, res) {<br \/>\u00a0 console.time(\u201cduration\u201d)<br \/>\u00a0 next()<br \/>}) <\/code><\/p>\r\n\r\n\r\n\r\n<ul class=\"wp-block-list\">\r\n<li>A vari\u00e1vel app representa a inst\u00e2ncia do nosso servidor<\/li>\r\n<li>O m\u00e9todo <em>use <\/em>serve para que nossa requisi\u00e7\u00e3o seja passada por esse middleware, que possui como par\u00e2metros\r\n<ul>\r\n<li>a pr\u00f3pria requisi\u00e7\u00e3o;<\/li>\r\n<li>a resposta;<\/li>\r\n<li>uma fun\u00e7\u00e3o <em>next<\/em> que serve para dar continuidade \u00e0 sequ\u00eancia de middewares (caso essa fun\u00e7\u00e3o n\u00e3o seja chamada, ou seja dada a resposta ao usu\u00e1rio, a cadeia \u00e9 interrompida).<\/li>\r\n<\/ul>\r\n<\/li>\r\n<li>A fun\u00e7\u00e3o <em>console.time <\/em>serve para iniciar uma cronometragem.<\/li>\r\n<\/ul>\r\n\r\n\r\n\r\n<figure class=\"wp-block-table\">\r\n<table>\r\n<tbody>\r\n<tr>\r\n<td><code>app.use('\/time, <strong>function<\/strong> (req, res) {<br \/>\u00a0 const duration = console.timeEnd(\u201cduration\u201d)\u00a0\u00a0<\/code><br \/><code>res.send(duration)<br \/>})<\/code><\/td>\r\n<\/tr>\r\n<\/tbody>\r\n<\/table>\r\n<\/figure>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Nessa rota encerramos a cronometragem e enviamos ao usu\u00e1rio o tempo total da requisi\u00e7\u00e3o.<\/p>\r\n\r\n\r\n\r\n<h2 class=\"wp-block-heading\">Implementando middlewares<\/h2>\r\n\r\n\r\n\r\n<h3 class=\"wp-block-heading\">Body Parsing middleware<\/h3>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Para que nossas futuras rotas tenham acesso ao corpo da requisi\u00e7\u00e3o, precisamos Informar ao Express que queremos acessar nossa request como json. Felizmente o Express j\u00e1 traz um middleware espec\u00edfico para isso.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code>app.use(express.json());<\/code><\/p>\r\n\r\n\r\n\r\n<h3 class=\"wp-block-heading\">Not Found middleware<\/h3>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Agora que sabemos o conceito por tr\u00e1s dos middlewares podemos implementar nossos pr\u00f3prios. Criaremos um middleware que gerenciar\u00e1 as requisi\u00e7\u00f5es que buscam recursos que n\u00e3o foram encontrados em nosso servidor, o popularmente conhecimento <em>Not Found Middleware.<\/em><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Nossa implementa\u00e7\u00e3o ficaria como a seguinte:<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code> app.use((req, res, next) =&gt; {<br \/><strong>const<\/strong> error = <strong>new<\/strong> Error(`Not found - ${req.originalUrl}`);<br \/>\u00a0 res.status(404);<br \/>\u00a0 next(error);<br \/>}); <\/code><\/p>\r\n\r\n\r\n\r\n<ul class=\"wp-block-list\">\r\n<li>Como sabemos criamos um middleware como uma fun\u00e7\u00e3o que recebe 3 par\u00e2metros:\r\n<ul>\r\n<li>A requisi\u00e7\u00e3o;<\/li>\r\n<li>A resposta;<\/li>\r\n<li>A fun\u00e7\u00e3o que d\u00e1 continuidade \u00e0 cadeia.<\/li>\r\n<\/ul>\r\n<\/li>\r\n<li>Nossa fun\u00e7\u00e3o \u00e9 encarregada de\r\n<ul>\r\n<li>Criar um erro referente a rota que foi requisitada;<\/li>\r\n<li>Atribuir o HTTP Status correto para requisi\u00e7\u00f5es que n\u00e3o encontraram o que procuravam;<\/li>\r\n<li>Passar o erro para o pr\u00f3ximo middleware da cadeia.<\/li>\r\n<\/ul>\r\n<\/li>\r\n<\/ul>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Sabemos que a request faz um caminho sequencial em nosso servidor, ent\u00e3o, para que a fun\u00e7\u00e3o de tratar um recurso n\u00e3o encontrado seja executada com sucesso, precisamos que a request passe por todos os middlewares anteriores para sabermos se em nenhum deles ela \u00e9 resolvida. Tendo isso em vista devemos declarar nosso middleware de <em>n\u00e3o encontrado <\/em>ap\u00f3s todas as rotas.<\/p>\r\n\r\n\r\n\r\n<h3 class=\"wp-block-heading\">Error handling middleware<\/h3>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Agora que implementamos nosso todos os middlewares que tratam a requisi\u00e7\u00e3o de forma correta, precisamos criar um que trate quando acontecer um erro e que retorne para nosso cliente uma mensagem que fa\u00e7a sentido e ajude a resolver o erro.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Como n\u00f3s mesmos implementaremos o lado do cliente tamb\u00e9m (na parte 2 deste tutorial), seria bom que envi\u00e1ssemos todas as informa\u00e7\u00f5es que pudessem nos ajudar a resolver o problema que aconteceu no servidor. Assim sendo, nossa implementa\u00e7\u00e3o ficaria como a seguinte:<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code> app.use((error, req, res, next) =&gt; {<br \/><strong>const<\/strong> statusCode = res.statusCode === 200 ? 500 : res.statusCode;<br \/>\u00a0 res.statusCode = statusCode;<br \/>\u00a0 res.json({<br \/>\u00a0 \u00a0 message: error.message,<br \/>\u00a0 \u00a0 stack: error.stack<br \/>\u00a0 });<br \/>}); <\/code><\/p>\r\n\r\n\r\n\r\n<ul class=\"wp-block-list\">\r\n<li>Diferente dos middlewares que j\u00e1 vimos, para tratarmos um erro chamamos uma fun\u00e7\u00e3o que recebe n\u00e3o 3, mas 4 par\u00e2metros, sendo o primeiro o erro encontrado (o Express se encarrega de diferenciar se a fun\u00e7\u00e3o tem 3 ou 4 par\u00e2metros e passar as vari\u00e1veis corretas a cada um deles);<\/li>\r\n<li>Caso nossa response tenha o status 200 (sucesso) significa que ela ainda n\u00e3o foi tratada e, se chegou at\u00e9 aqui, deve ser retornada como um erro ao cliente. Portanto, alteramos o status para 500 (error no servidor);<\/li>\r\n<li>Retornamos um JSON com a mensagem do erro o caminho percorrido pela request dentro de nosso servidor, conhecido como <em>stacktrace<\/em>.<\/li>\r\n<\/ul>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Ao acessarmos uma URL que nosso servidor n\u00e3o possui, devemos receber uma resposta como esta:\u00a0<\/p>\r\n\r\n\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Como podemos ver, a mensagem que \u00e9 retornada ao nosso cliente apresenta toda a stacktrace do nosso servidor, esteja ele sendo rodado localmente ou em uma cloud. <br \/>Devemos concordar que, por quest\u00f5es de seguran\u00e7a, isso n\u00e3o seria uma boa pr\u00e1tica j\u00e1 que a cada erro que aconte\u00e7a dizemos todos os arquivos que a requisi\u00e7\u00e3o passou, o que facilitaria muito a vida de algum mal-intencionado. <br \/>Para resolvermos esse problema sem perder os benef\u00edcios da stacktrace em tempo de desenvolvimento, podemos verificar qual \u00e9 o ambiente em que nossa aplica\u00e7\u00e3o est\u00e1 rodando e, caso esteja em produ\u00e7\u00e3o, envidar apenas a mensagem de erro.<\/p>\r\n\r\n\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code>app.use((error, req, res, next) =&gt; {<br \/>\u00a0 <strong>const<\/strong> statusCode = res.statusCode === 200 ? 500 : res.statusCode;<br \/>\u00a0 res.statusCode = statusCode;<br \/>\u00a0 res.json({<br \/>\u00a0 \u00a0 message: error.message,<br \/>\u00a0 \u00a0 stack: process.env.NODE_ENV === \"production\" ? \"\ud83e\udd13\" : error.stack\u00a0\u00a0});<br \/>}); <\/code><\/p>\r\n\r\n\r\n\r\n<h2 class=\"wp-block-heading\">Usando middlewares de terceiros<\/h2>\r\n\r\n\r\n\r\n<h3 class=\"wp-block-heading\">Morgan<\/h3>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Aprendemos a criar nossos pr\u00f3prios middlewares, mas nem sempre teremos que implementar um novo para cada funcionalidades que quisermos que nosso servidor forne\u00e7a.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Podemos querer, por exemplo, que nossa aplica\u00e7\u00e3o crie um registro a cada requisi\u00e7\u00e3o que chega at\u00e9 ela, para isso podemos utilizar a <a href=\"https:\/\/github.com\/expressjs\/morgan\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\"library morgan (abre numa nova aba)\">library <em>morgan<\/em><\/a>.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><em>yarn add morgan<\/em><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Como ela j\u00e1 possui integra\u00e7\u00e3o com o Express, podemos utilizar o middleware que a pr\u00f3pria library exporta. Utilizamos da seguinte forma:<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code><strong>const<\/strong> morgan = require(\"morgan\");<\/code><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code>app.use(morgan(\"common\")); <\/code><\/p>\r\n\r\n\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">A partir de agora, todas as requisi\u00e7\u00f5es, que chegarem ao nosso servidor, criar\u00e3o um registro (log) contendo informa\u00e7\u00f5es importantes como IP, hora, m\u00e9todo, URL e status para que possamos corrigir bugs, analisar e tomar decis\u00f5es em cima dos dados. <br \/>E essa capacidade ainda \u00e9 bastante conveniente quando nossa aplica\u00e7\u00e3o estiver em produ\u00e7\u00e3o, pois n\u00e3o exp\u00f5e nenhuma informa\u00e7\u00e3o sigilosa do servidor.<\/p>\r\n\r\n\r\n\r\n<h3 class=\"wp-block-heading\">CORS<\/h3>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Por padr\u00e3o nosso servidor aceita requisi\u00e7\u00f5es vindas de qualquer dom\u00ednio, seja de onde for, e, se estamos nos preocupando com seguran\u00e7a, temos que nos alertar para os poss\u00edveis ataques que isso nos imp\u00f5e.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Com intuito de proteger nossa aplica\u00e7\u00e3o de ataques, utilizaremos o CORS &#8211; sigla para Cross-origin resource sharing &#8211; que nos permite determinar uma origem de onde aceitaremos as requests e bloquear as que n\u00e3o s\u00e3o bem-vindas.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Para isso utilizaremos a <a href=\"https:\/\/github.com\/expressjs\/cors\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\"library cors (abre numa nova aba)\">library <em>cors<\/em><\/a> em sua vers\u00e3o para o Express(CORS \u00e9 agn\u00f3stico a linguagem e possui implementa\u00e7\u00e3o para a grande parte delas).\u00a0<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><em>yarn add cors<\/em><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Nossa implementa\u00e7\u00e3o ficaria como a seguinte:<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code><strong>const<\/strong> cors = require(\"cors\");<\/code><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code>app.use(<br \/>\u00a0 cors({<br \/>\u00a0 \u00a0 origin: \"http:\/\/localhost:3000\"<br \/>\u00a0 })<br \/>); <\/code><\/p>\r\n\r\n\r\n\r\n<ul class=\"wp-block-list\">\r\n<li>Importamos o CORS como um m\u00f3dulo.<\/li>\r\n<\/ul>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Declaramos um novo middleware e nele utilizamos o CORS para atribuir a origem permitida das request para a URL <em>http:\/\/localhost:3000<\/em><em>, <\/em>que \u00e9 onde ficar\u00e1 nossa aplica\u00e7\u00e3o front-end.<\/p>\r\n\r\n\r\n\r\n<h2 class=\"wp-block-heading\">Configura\u00e7\u00e3o do banco de dados<\/h2>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Em nossa aplica\u00e7\u00e3o utilizaremos MongoDB como o banco de dados para persist\u00eancia dos dados. Nele podemos ter dados em estrutura de json de forma din\u00e2mica devido ao Mongo de um banco NoSQL.\u00a0<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Para facilitar o desenvolvimento, utilizaremos uma vers\u00e3o do banco que roda na Cloud, assim n\u00e3o precisamos configurar nada em nossa m\u00e1quina. <a href=\"https:\/\/www.mongodb.com\/cloud\/atlas\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\"Mongo Atlas  (abre numa nova aba)\">Mongo Atlas <\/a>\u00e9 uma vers\u00e3o do Mongo que pode ser acessada pela aplica\u00e7\u00e3o atrav\u00e9s de uma URL, caso voc\u00ea ainda n\u00e3o tenha uma conta, ser\u00e1 necess\u00e1rio criar uma.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">De in\u00edcio precisaremos criar um cluster que armazenar\u00e1 nosso banco, seguindo os seguintes passos:<\/p>\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Enquanto nosso cluster \u00e9 criado, podemos ir at\u00e9 a aba <em>security<\/em> para configurarmos os usu\u00e1rios que ter\u00e3o acesso ao nosso banco. No menu a esquerda, na parte de Security &gt; Database Access n\u00e3o encontramos nenhum usu\u00e1rio com permiss\u00f5es de acessar o banco, devemos criar um da seguinte forma:<\/p>\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">As informa\u00e7\u00f5es utilizadas aqui devem permanecer confidenciais. No meu caso, criarei um usu\u00e1rio como tempor\u00e1rio para durar 1 semana com acesso \u00e0 escrita e leitura ao banco de dados.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Na aba Security &gt; Network Access devemos adicionar IP Addess e ativar a op\u00e7\u00e3o Allow Access From Anywhere, que se refere ao nosso banco poder ser acessado de qualquer IP do mundo.<\/p>\r\n\r\n\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">A esse momento nosso cluster j\u00e1 deve ter terminado de ser criado e podemos avan\u00e7ar para parte de conex\u00e3o com a aplica\u00e7\u00e3o.<\/p>\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Para que nossa aplica\u00e7\u00e3o se comunique com o banco de dados que criamos, precisamos copiar a URL alterando o campo &lt;password&gt; e outro campo referente ao nome do banco de dados que usaremos:<br \/><br \/><code>mongodb+srv:\/\/picollo:&lt;password&gt;@geeekhunter-0rjve.mongodb.net\/geek-twitter?retryWrites=true&amp;w=majority <\/code><\/p>\r\n\r\n\r\n\r\n<h2 class=\"wp-block-heading\">Intera\u00e7\u00f5es com o banco de dados<\/h2>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Esse Node JS tutorial precisa de um lugar onde possa persistir os dados que foram gravados desde o seu lan\u00e7amento, por\u00e9m at\u00e9 agora a aplica\u00e7\u00e3o n\u00e3o tem conhecimento algum sobre nenhum banco de dados que tenha sido criado e n\u00e3o h\u00e1 a comunica\u00e7\u00e3o entre essas partes.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Para facilitar essa integra\u00e7\u00e3o de servidor com o banco de dados usaremos a <a href=\"https:\/\/github.com\/Automattic\/mongoose\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\" lib mongoose (abre numa nova aba)\">lib <em>mongoose<\/em><\/a>. Com ela podemos lidar com nossas tabelas do banco de dados como se fossem objetos em nosso c\u00f3digo, sem precisar utilizar nenhum tipo de linguagem de consulta de banco, o que facilita bastante no desenvolvimento.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><em>yarn add mongoose<\/em><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Utilizaremos a URL do nosso cluster como ponto de comunica\u00e7\u00e3o, por\u00e9m, essa URL n\u00e3o deveria ficar vis\u00edvel a todos que mexem em nosso c\u00f3digo, porque l\u00e1 cont\u00e9m informa\u00e7\u00f5es sigilosas sobre nossa base de dados.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">A cria\u00e7\u00e3o de vari\u00e1veis feitas de forma secreta \u00e9 responsabilidade de um arquivo de vari\u00e1veis de ambiente, neste arquivo s\u00e3o guardadas todas as vari\u00e1veis que n\u00e3o devem ser expostas (se voc\u00ea estiver usando um controle de vers\u00e3o como git, voc\u00ea deve adicionar esse arquivo ao <em>.gitignore)<\/em>.\u00a0<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Por conven\u00e7\u00e3o chamamos este arquivo de <em>.env, <\/em>e todas as vari\u00e1veis criadas nele devem ser compostas de um nome seguido do s\u00edmbolo de atribui\u00e7\u00e3o (=), e o valor da vari\u00e1vel.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code> DB_URL=mongodb+srv:\/\/picollo:geekhunter@geeekhunter-0rjve.mongodb.net\/geek-twitter?retryWrites=<strong>true<\/strong>&amp;w=majority <\/code><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Para termos acesso a essas vari\u00e1veis dentro do c\u00f3digo precisaremos utilizar outra lib chamada <em><a href=\"https:\/\/github.com\/motdotla\/dotenv\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\"dotenv  (abre numa nova aba)\">dotenv <\/a>.\u00a0<\/em><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><em> yarn add dotenv<\/em><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Em nosso c\u00f3digo, principal devemos fazer o seguinte:<\/p>\r\n\r\n\r\n\r\n<figure class=\"wp-block-table\">\r\n<table>\r\n<tbody>\r\n<tr>\r\n<td><code><strong>const<\/strong> express = require(\"express\");<br \/>require(\"dotenv\").config();<br \/><br \/><strong>const<\/strong> app = express();<br \/><em>console<\/em>.log(process.env.DB_URL);<\/code><\/td>\r\n<\/tr>\r\n<\/tbody>\r\n<\/table>\r\n<\/figure>\r\n\r\n\r\n\r\n<ul class=\"wp-block-list\">\r\n<li>Importar o dotenv como um m\u00f3dulo;<\/li>\r\n<li>Chamar o m\u00e9todo config;<\/li>\r\n<li>Acessar a vari\u00e1vel atrav\u00e9s do objeto <em>process.env<\/em>.<\/li>\r\n<\/ul>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Agora ser\u00e1 poss\u00edvel fazer a conex\u00e3o com o banco de dados, precisaremos dos seguintes passos:<\/p>\r\n\r\n\r\n\r\n<figure class=\"wp-block-table\">\r\n<table>\r\n<tbody>\r\n<tr>\r\n<td><code><strong>const<\/strong> express = require(\"express\");<br \/><strong>const<\/strong> mongoose = require(\"mongoose\");require(\"dotenv\").config();<br \/><br \/><strong>const<\/strong> app = express();<br \/><br \/>mongoose.connect(process.env.DB_URL, {<\/code><br \/><code> useNewUrlParser: true,\u00a0\u00a0\u00a0\u00a0<\/code><br \/><code>useUnifiedTopology: true\u00a0\u00a0<\/code><br \/><code>},\u00a0\u00a0() =&gt; <em>console<\/em>.log(\"Connected to the database!\"))<\/code><\/td>\r\n<\/tr>\r\n<\/tbody>\r\n<\/table>\r\n<\/figure>\r\n\r\n\r\n\r\n<ul class=\"wp-block-list\">\r\n<li>Importar o mongoose como um m\u00f3dulo;<\/li>\r\n<li>Chamar o m\u00e9todo <em>connect<\/em> do mongoose passando como argumento:\r\n<ul>\r\n<li>a URL do nosso banco;<\/li>\r\n<li>um objeto de configura\u00e7\u00e3o do mongoose (n\u00e3o se preocupe em entender para que exatamente esse objeto serve);<\/li>\r\n<li>uma fun\u00e7\u00e3o que ser\u00e1 chamada assim que a conex\u00e3o for realizada.<\/li>\r\n<\/ul>\r\n<\/li>\r\n<\/ul>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Se tudo ocorreu corretamente voc\u00ea deve ver as duas mensagens no console.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Devemos criar as entidades que representar\u00e3o nossas tabelas atrav\u00e9s do c\u00f3digo, para o mongoose esses s\u00e3o os chamados <em>models<\/em>. Cada <em>model <\/em>possui um nome e um objeto json que representa sua estrutura, este objeto \u00e9 chamada de <em>Schema<\/em>.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Dentro de uma nova pasta chamada <em>model<\/em> devemos criar nossos models:<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code>const mongoose = require(\"mongoose\")<\/code><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code>const User = new mongoose.Schema({<br \/>     username: {<br \/>         type: String,<br \/>         required: true<br \/>     },<br \/>     password: {<br \/>         type: String,<br \/>         required: true,<br \/>         max: 1024<br \/>     },<br \/>      tweets: [{<br \/>         type: mongoose.Schema.Types.ObjectId,<br \/>         ref: 'Tweet'<br \/>     }]<br \/> }, {<br \/>     timestamps: true<br \/>   }<br \/> )<\/code><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code>module.exports = mongoose.model(\"User\", User)<\/code><\/p>\r\n\r\n\r\n\r\n<ul class=\"wp-block-list\">\r\n<li>Importamos o mongoose como m\u00f3dulo;<\/li>\r\n<li>Criamos um mongoose schema que representar\u00e1 nosso usu\u00e1rio\r\n<ul>\r\n<li>username do tipo string e obrigat\u00f3rio;<\/li>\r\n<li>password do tipo string, obrigat\u00f3rio e com no m\u00e1ximo 1024 caracteres;<\/li>\r\n<li>tweets que servir\u00e1 como relacionamento entre nossa tabela User com a Tweet.<\/li>\r\n<\/ul>\r\n<\/li>\r\n<li>O segundo par\u00e2metro \u00e9 um objeto de configura\u00e7\u00e3o do schema, nele declaramos que devem ser criados os campos <em>created_at<\/em> e <em>updated_at<\/em> automaticamente quando o registro for criado;<\/li>\r\n<li>Exportamos um mongoose model.<\/li>\r\n<\/ul>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code>const mongoose = require(\"mongoose\")<\/code><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code>const Tweet = new mongoose.Schema({<br \/>     owner: {<br \/>         type: mongoose.Schema.Types.ObjectId,<br \/>         ref: 'User'<br \/>     },<br \/>     content: {<br \/>         type: String,<br \/>         required: true,<br \/>         min: 1<br \/>     },<br \/>     likes: [{       <br \/>         type: mongoose.Schema.Types.ObjectId,<br \/>         ref: 'User'<br \/>     }]<br \/> }, {<br \/>     timestamps: true<br \/>   }<br \/> )<\/code><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code>module.exports = mongoose.model(\"Tweet\", Tweet)<\/code><\/p>\r\n\r\n\r\n\r\n<h2 class=\"wp-block-heading\">Configurando ambiente de desenvolvimento<\/h2>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Se voc\u00ea tem brincado um pouco com o servidor, alterado para ver como ele se comportava, deve ter percebido que a cada modifica\u00e7\u00e3o feita \u00e9 necess\u00e1rio que o servidor seja desligado e ligado novamente. Em tempo de desenvolvimento, isso acaba sendo uma grande dor para o dev devido \u00e0 perda de tempo e trabalho repetitivo.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Para acabarmos com essa dor, podemos utilizar a <a href=\"https:\/\/github.com\/remy\/nodemon\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\"library nodemon (abre numa nova aba)\">library <em>nodemon<\/em><\/a><em>. C<\/em>om ela nossos arquivos ser\u00e3o vigiados e a cada mudan\u00e7a nosso server ser\u00e1 reiniciado automaticamente.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Podemos instalar o Nodemon atrav\u00e9s do comando <em>yarn add nodemon -D <\/em>para que seja adicionado como uma depend\u00eancia de desenvolvimento, ou seja, que n\u00e3o vai interferir na experi\u00eancia do usu\u00e1rio, apenas na experi\u00eancia do dev.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Podemos tamb\u00e9m adicionar ao <em>package.json<\/em> o script para iniciar o ambiente de desenvolvimento para que seja poss\u00edvel rodar o comando <em>yarn dev<\/em> e todo nosso ambiente de desenvolvimento esteja rodando corretamente.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code>\"scripts\": {<br \/>   \"start\": \"node src\/app.js\",<br \/>   \"dev\": \"nodemon src\/app.js\"<br \/> }<\/code><\/p>\r\n\r\n\r\n\r\n<h2 class=\"wp-block-heading\">Criando nossa primeira rota<\/h2>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Por enquanto nosso app n\u00e3o tem muitas utilidades a n\u00e3o ser retornar &#8220;Hello GeekHunter!&#8221; para quem cham\u00e1-lo na rota <em>localhost:3333. <\/em>Para mudarmos isso precisamos come\u00e7ar a implementar a intera\u00e7\u00e3o com os clientes que utilizar\u00e3o nosso app, para isso implementaremos as seguintes rotas:<\/p>\r\n\r\n\r\n\r\n<ul class=\"wp-block-list\">\r\n<li>Login (POST);<\/li>\r\n<li>Registrar usu\u00e1rio (POST);<\/li>\r\n<li>Encontrar usu\u00e1rio\u00a0 (GET);<\/li>\r\n<li>Criar tweet (POST);<\/li>\r\n<li>Encontrar tweet (GET);<\/li>\r\n<li>Listar todos os tweets (GET);<\/li>\r\n<li>Listar tweets por usu\u00e1rio (GET);<\/li>\r\n<li>Listar \u00fanico tweet por usu\u00e1rio (GET);<\/li>\r\n<li>Deletar tweet (DELETE);<\/li>\r\n<li>Op\u00e7\u00e3o de <em>like\/unlike<\/em> no tweet (PUT).<\/li>\r\n<\/ul>\r\n\r\n\r\n\r\n<h2 class=\"wp-block-heading\">Cadastro de usu\u00e1rio<\/h2>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">O cadastro de um novo usu\u00e1rio necessita que nosso servidor seja acessado pela rota <em>\/register<\/em> utilizando o m\u00e9todo POST, passando o <em>username<\/em> e <em>password.<\/em> Nossa rota ficaria como a seguinte:<\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-code\"><code>\/\/ Criar usu\u00e1rio\r\n app.post(\"\/register\", async (req, res, next) =&gt; {\r\n   try {\r\n     const { username, password } = req.body;\r\n\/\/ Verificar se username \u00e9 valido\r\nconst userExists = await User.findOne({username});\r\n\r\nif (userExists) return res.status(400).send({error: \"Username already in use.\"});\r\n\r\n\/\/Criar novo usu\u00e1rio no banco\r\nconst user = await User.create({\r\n  username,\r\n  password\r\n})\r\n\r\nres.status(201).send({\r\n  id: user.id,\r\n  username: user.username\r\n});    \r\n} catch (err) {       \r\n     res.status(400)\r\n     next(err);\r\n   }\r\n });<\/code><\/pre>\r\n\r\n\r\n\r\n<ul class=\"wp-block-list\">\r\n<li>Criamos uma rota POST passando como argumento:\r\n<ul>\r\n<li>A url <em>\/register<\/em>;<\/li>\r\n<li>Uma fun\u00e7\u00e3o ass\u00edncrona que recebe a <em>request <\/em>e a <em>response <\/em>do usu\u00e1rio;\r\n<ul>\r\n<li>Acessamos as propriedades username e password do body da requisi\u00e7\u00e3o;<\/li>\r\n<li>Verificamos se o usu\u00e1rio j\u00e1 existe;<\/li>\r\n<li>Se existir retornamos um erro 400 ao usu\u00e1rio informando que o username j\u00e1 est\u00e1 em uso;<\/li>\r\n<li>Caso contr\u00e1rio criamos um novo usu\u00e1rio no banco e retornamos o usu\u00e1rio criado;<\/li>\r\n<li>Em caso de erro atribu\u00edmos o status 400 \u00e0 response e passamos o erro para o middleware de <em>tratamento de erro<\/em>.<\/li>\r\n<\/ul>\r\n<\/li>\r\n<\/ul>\r\n<\/li>\r\n<\/ul>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Para testarmos se ocorreu tudo corretamente, precisaremos utilizar um software chamado <a href=\"https:\/\/insomnia.rest\/\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\"Insomnia  (abre numa nova aba)\"><em>Insomnia<\/em><\/a>.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Nele podemos fazer requisi\u00e7\u00f5es de qualquer tipo ao nosso servidor, passando as informa\u00e7\u00f5es que quisermos sem precisar que tenhamos um <em>&lt;form&gt;<\/em> no front end (outra alternativa seria utilizar o Postman, que serve para o mesmo fim).<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Ao instalar o Insomnia veremos esta primeira p\u00e1gina:<\/p>\r\n\r\n\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Nossa rota <em>\/register <\/em>espera por uma requisi\u00e7\u00e3o do tipo POST, que tenha no body um username e um password, para fazermos isso no Insomnia basta que mudemos o tipo da requisi\u00e7\u00e3o ao lado da URL e que no body do tipo JSON esteja um objeto contendo essas propriedades.<\/p>\r\n\r\n\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Se tudo ocorreu como esperado devemos receber como resposta um c\u00f3digo 201 com o objeto criado no banco.<\/p>\r\n\r\n\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">No caso de n\u00e3o ter funcionado corretamente, podemos imprimir no console o erro que chega no catch de nossa rota, assim ser\u00e1 poss\u00edvel entender qual erro pode estar acontecendo.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Ao acessarmos o nosso banco, na aba de Collections ser\u00e1 poss\u00edvel ver o usu\u00e1rio criado.<\/p>\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Repare no objeto que est\u00e1 criado no banco, voc\u00ea repara algo de estranho com os dados mostrados? Pense um pouco e depois continue a ler.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Encontrou o erro? Voc\u00ea diria que o objeto criado est\u00e1 correto no nosso banco? Mesmo a senha sendo gravada como um texto leg\u00edvel por qualquer um? Obviamente que em aplica\u00e7\u00f5es reais nossas senhas n\u00e3o ficam expostas dessa maneira, precisamos que haja alguma tipo de criptografia que aumente ainda mais a seguran\u00e7a de nossos dados.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Utilizaremos a <a href=\"https:\/\/github.com\/dcodeIO\/bcrypt.js\/\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\"library bcryptjs (abre numa nova aba)\">library <em>bcryptjs<\/em><\/a> para criptografar a senha de nossos usu\u00e1rios.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><em>yarn add bcryptjs<\/em><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Alteraremos nossa rota <em>\/register <\/em>para gerar um hash a partir de um n\u00famero aleat\u00f3rio criado pelo bcrypt.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">\u00a0<code>\/\/ Criptografar a senha<br \/>\u00a0 \u00a0 <strong>const<\/strong> salt = <strong>await<\/strong> bcrypt.genSalt(10);<br \/>\u00a0 \u00a0 <strong>const<\/strong> hash = <strong>await<\/strong> bcrypt.hash(password, salt);<br \/><br \/>\u00a0 \u00a0 \/\/Criar novo usu\u00e1rio no banco<br \/>\u00a0 \u00a0 <strong>const<\/strong> user = <strong>await<\/strong> User.create({<br \/>\u00a0 \u00a0 \u00a0 username,<br \/>\u00a0 \u00a0 \u00a0 password: hash<br \/>\u00a0 \u00a0 })<br \/><\/code><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Agora para testarmos nossa rota novamente primeiro vamos excluir o registro antigo do banco, na op\u00e7\u00e3o delete do pr\u00f3prio Mongo Atlas. Ap\u00f3s isso mandamos nossa requisi\u00e7\u00e3o de novo para rota gerando um novo registro como o seguinte:<\/p>\r\n\r\n\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Se termos utilizar o mesmo username novamente devemos receber o seguinte output:<\/p>\r\n\r\n\r\n\r\n\r\n\r\n<h2 class=\"wp-block-heading\">Rota de login<\/h2>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Seguindo a mesma linha da rota <em>\/register<\/em>, nossa rota de login ficaria como a seguinte:<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code>app.post(\"\/login\", async (req, res, next) =&gt; {<br \/>   try {<br \/>     const { username, password } = req.body;<\/code><\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-preformatted\"><code>\/\/ Verificar se username \u00e9 valido\r\nconst user = await User.findOne({username})\r\n\r\nif (!user) return res.status(400).send({error: \"Username not found.\"});\r\n\r\n\/\/ Verifica se a senha \u00e9 v\u00e1lida\r\nconst validPassword = await bcrypt.compare(password, user.password);\r\n\r\nif (!validPassword) return res.status(400).send({ error: \"Invalid password.\"});\r\n\r\nres.send({message: \"User logged in.\"});<\/code><\/pre>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code>} catch (err) {            <br \/>     res.status(400);<br \/>     send(err);<br \/>   }<br \/> });<\/code><\/p>\r\n\r\n\r\n\r\n<ul class=\"wp-block-list\">\r\n<li>Criamos uma rota POST passando como argumento:\r\n<ul>\r\n<li>A url <em>\/login<\/em>\u00a0<\/li>\r\n<li>Uma fun\u00e7\u00e3o ass\u00edncrona que recebe a <em>request <\/em>e a <em>response <\/em>do usu\u00e1rio<\/li>\r\n<li>Acessamos as propriedades username e password do body da requisi\u00e7\u00e3o<\/li>\r\n<li>Verificamos se o usu\u00e1rio j\u00e1 existe<\/li>\r\n<li>Se n\u00e3o existir retornamos um erro 400 ao usu\u00e1rio informando que o user n\u00e3o foi encontrado<\/li>\r\n<li>Caso contr\u00e1rio verificamos se a senha est\u00e1 correta, utilizamos a fun\u00e7\u00e3o compare do bcrypt que faz a compara\u00e7\u00e3o pelo hash da senha salva no banco<\/li>\r\n<li>Caso n\u00e3o seja a senha correta retornamos um erro 400 informando que a senha est\u00e1 incorreta<\/li>\r\n<li>Caso esteja correta mandamos uma mensagem que o usu\u00e1rio est\u00e1 logado<\/li>\r\n<\/ul>\r\n<\/li>\r\n<\/ul>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Ao acessarmos pelo postman podemos verificar se tudo ocrreu como esperado:<\/p>\r\n\r\n\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Agora digamos que ap\u00f3s estar logado na aplica\u00e7\u00e3o, nosso usu\u00e1rio queira fazer mais a\u00e7\u00f5es como criar, curtir ou apagar um tweet, como poder\u00edamos fazer com que nosso servidor saiba que o usu\u00e1rio est\u00e1 logado e qual a identidade dele? Necessitamos um jeito de identificar o usu\u00e1rio e todas as requisi\u00e7\u00f5es feitas por ele, assim podemos controlar que apenas tweets criados pelo usu\u00e1rio logado possam ser exclu\u00eddos.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Para isto utilizaremos a <a href=\"https:\/\/github.com\/auth0\/node-jsonwebtoken\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\"lib jsonwebtoken (abre numa nova aba)\">lib <em>jsonwebtoken<\/em><\/a><em>. <\/em>Com ela ser\u00e1 poss\u00edvel criar um token espec\u00edfico para cada usu\u00e1rio na hora do login, ap\u00f3s isso todas as requisi\u00e7\u00f5es que estiverem com este token ser\u00e3o referentes a este usu\u00e1rio.\u00a0<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><em>yard add jsonwebtoken\u00a0<\/em><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Nossa rota ficaria como a seguinte:<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code>\/\/ Login<br \/> app.post(\"\/login\", async (req, res, next) =&gt; {<br \/>   try {<br \/>     const { username, password } = req.body;<\/code><\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-preformatted\"><code>\/\/ Verificar se username \u00e9 valido\r\nconst user = await User.findOne({username})\r\n\r\nif (!user) return res.status(400).send({error: \"Username not found.\"});\r\n\r\n\/\/ Verifica se a senha \u00e9 v\u00e1lida\r\nconst validPassword = await bcrypt.compare(password, user.password);\r\n\r\nif (!validPassword) return res.status(400).send(\"Invalid password.\");\r\n\r\n\/\/ Criar token de valida\u00e7\u00e3o de usu\u00e1rio\r\nconst token = jwt.sign({_id: user.id}, process.env.JWT_SECRET);    \r\nres.header('auth-token', token).send(token);<\/code><\/pre>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code>} catch (err) {            <br \/>     res.status(400);<br \/>     send(err);<br \/>   }<br \/> });<\/code><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Ap\u00f3s a valida\u00e7\u00e3o de senha adicionar a cria\u00e7\u00e3o de um token JWT utilizando o ID do usu\u00e1rio no banco como identificador e uma vari\u00e1vel de ambiente como segundo argumento (o JWT_SECRET pode ser qualquer texto que sirva para gerar o token, contudo essa vari\u00e1vel deve ser secreta para\u00a0 que n\u00e3o seja poss\u00edvel que nosso usu\u00e1rio crie um token como se fosse de outro usu\u00e1rio).<\/p>\r\n\r\n\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Enviamos como resposta o token no header e um objeto com o token gerado.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Nosso output ficaria como o seguinte:<\/p>\r\n\r\n\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Se copiarmos o token gerado e formos at\u00e9 o site oficial do <a href=\"https:\/\/jwt.io\/\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\"JWT  (abre numa nova aba)\">JWT <\/a>podemos descriptograf\u00e1-lo e ver o que temos dentro dele:<\/p>\r\n\r\n\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">A partir de agora, todas as requisi\u00e7\u00f5es que formos fazer, que dependem do usu\u00e1rio estar logado, devem conter este token para que o server identifique o usu\u00e1rio que est\u00e1 logado e fazendo as a\u00e7\u00f5es.<\/p>\r\n\r\n\r\n\r\n<h2 class=\"wp-block-heading\">Criando rotas privadas<\/h2>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Express funciona com uma arquitetura de chamados <em>middlewares<\/em> que funcionam como interceptadores da requisi\u00e7\u00e3o que chega ao servidor. Com eles podemos adicionar e modificar a requisi\u00e7\u00e3o antes mesmo que chega at\u00e9 a rota.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Criaremos um arquivo chamado <em>auth.js<\/em>, nele criaremos a seguinte fun\u00e7\u00e3o:<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code>const jwt = require(\"jsonwebtoken\")<\/code><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code>function validateToken (req, res, next) {<br \/>     const token = req.header('auth-token')<br \/>     if (!token) return res.status(400).send({error: 'Access denied.'})<\/code><\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-preformatted\"><code>try {\r\n    const verified = await jwt.verify(token, process.env.JWT_SECRET);\r\n    req.user = verified;\r\n\r\n    next();\r\n} catch (err) {\r\n    res.status(400).send({error: 'Invalid token.'})\r\n}<\/code><\/pre>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code>}<\/code><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code>module.exports = validateToken<\/code><\/p>\r\n\r\n\r\n\r\n<ul class=\"wp-block-list\">\r\n<li>Importamos jwt como um m\u00f3dulo<\/li>\r\n<li>Criamos uma fun\u00e7\u00e3o que recebe a request, a response e uma fun\u00e7\u00e3o next que presenta chamar o pr\u00f3ximo middleware da sequ\u00eancia, no nosso caso a pr\u00f3pria rota\r\n<ul>\r\n<li>Pegamos o token do header da requisi\u00e7\u00e3o<\/li>\r\n<li>Caso n\u00e3o haja um auth-token informamos ao usu\u00e1rio que ele n\u00e3o tem acesso a rota e enviamos um erro 400<\/li>\r\n<li>Caso haja um token verificamos se o token \u00e9 v\u00e1lido utilizando nossa vari\u00e1vel de ambiente JWT_SECRET<\/li>\r\n<li>No caso de o token ser v\u00e1lido adicionamos \u00e0 requisi\u00e7\u00e3o uma flag para identificar um usu\u00e1rio logado e verificado e chamamos a fun\u00e7\u00e3o next para a requisi\u00e7\u00e3o chegar at\u00e9 a rota<\/li>\r\n<\/ul>\r\n<\/li>\r\n<\/ul>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Devemos adicionar este middleware ao arquivo principal em todas as rotas que deveriam ser privadas. A implementa\u00e7\u00e3o ficaria como a seguinte:<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code>app.get(\"\/private\", authenticate, async (req, res, next) =&gt; {});<\/code><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Sendo authenticate o nome do m\u00f3dulo que importamos do arquivo auth.js<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">No caso da requisi\u00e7\u00e3o n\u00e3o ter passado na verifica\u00e7\u00e3o devemos receber um output como o seguinte:<\/p>\r\n\r\n\r\n\r\n\r\n\r\n<h2 class=\"wp-block-heading\">Criando um tweet<\/h2>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Agora que estamos corretamente logados na aplica\u00e7\u00e3o podemos come\u00e7ar a criar tweets normalmente. Nossa rota de cria\u00e7\u00e3o de tweet ficaria como a seguinte:<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code>\/\/ Criar tweets<br \/> app.post(\"\/tweets\", authenticate, async (req, res, next) =&gt; {<br \/>   const { content } = req.body;<\/code><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code>try {<br \/>     const tweet = await Tweet.create({owner: req.user, content});<\/code><\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-preformatted\"><code>if (!tweet) res.status(400).send({error: \"Unable to create tweet.\"})\r\n\r\nres.status(201).send(tweet)<\/code><\/pre>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code>} catch (err) {<br \/>     res.status(400);<br \/>     send(err);<br \/>   }<br \/> });<\/code><\/p>\r\n\r\n\r\n\r\n<ul class=\"wp-block-list\">\r\n<li>Buscamos o conte\u00fado do tweet no body da requisi\u00e7\u00e3o<\/li>\r\n<li>Criamos um novo tweet identificado pelo usu\u00e1rio que criou<\/li>\r\n<li>Retornamos o tweet criado com o status (201 &#8211; Recurso criado)<\/li>\r\n<\/ul>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Podemos testar a rota utilizando o Insomnia:<\/p>\r\n\r\n\r\n\r\n\r\n\r\n<h2 class=\"wp-block-heading\">Remover e Atualizar tweet<\/h2>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Para que o usu\u00e1rio tenha a op\u00e7\u00e3o de deletar um tweet feito, devemos implementar a rota de DELETE para um tweet de id espec\u00edfico. Nossa rota ficaria como a seguinte:<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code>\/\/ Deletar tweet espec\u00edfico<br \/> app.delete(\"\/tweets\/:id\", authenticate, async (req, res, next) =&gt; {<br \/>   const { id } = req.params;<\/code><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code>try {<br \/>     await Tweet.deleteOne(id);    <br \/>     res.status(200).send({message: \"Tweet deleted.\"})<br \/>   } catch (err) {    <br \/>     res.status(400);<br \/>     next(err);}<br \/> });<\/code><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Se quisermos que seja poss\u00edvel que tweets sejam curtidos devemos implementar a rota UPDATE de um tweet espec\u00edfico. Nossa rota ficaria como a seguinte:<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code>\/\/ Atualizar tweet espec\u00edfico (like\/unlike)<br \/> app.put(\"\/tweets\/:id\", authenticate, async (req, res, next) =&gt; {<br \/>   const { id } = req.params;<\/code><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code>try {<br \/>     const tweet = await Tweet.findById(id);  <\/code><\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-preformatted\">i<code>f (!tweet) return res.status(400).send({error: \"Tweet not found.\"});\r\n\r\nif (tweet.owner === req.user._id) return res.status(400).send({error: \"Unable to update tweet.\"})\r\n\r\nconst tweetAlreadyLiked = tweet.likes.some(like =&gt; like == req.user._id)\r\n\r\nif (tweetAlreadyLiked) {\r\n  tweet.likes = tweet.likes.filter(like =&gt; like != req.user._id);\r\n} else {\r\n  tweet.likes.push(req.user._id)\r\n}    \r\n\r\ntweet.save();\r\n\r\nres.status(200).send(tweet)<\/code><\/pre>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code>} catch (err) {           <br \/>     res.status(400);<br \/>     next(err);<\/code><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code> \u00a0\u00a0}<br \/>}); <\/code><\/p>\r\n\r\n\r\n\r\n<ul class=\"wp-block-list\">\r\n<li>Encontramos o tweet pelo par\u00e2metro ID da url<\/li>\r\n<li>Vemos se o tweet em quest\u00e3o pertence ao usu\u00e1rio que est\u00e1 logado<\/li>\r\n<li>Caso seja, verificamos se este tweet j\u00e1 o foi curtido pelo usu\u00e1rio logado<\/li>\r\n<li>Se n\u00e3o, adicionamos a curtida \u00e0 propriedade likes<\/li>\r\n<li>Se sim, removes a curtida da propriedade likes<\/li>\r\n<\/ul>\r\n\r\n\r\n\r\n<h2 class=\"wp-block-heading\">Rotas de GET<\/h2>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">As rotas a seguir s\u00e3o rotas para pegar um recurso no servidor, todas elas seguem um padr\u00e3o, ent\u00e3o ficar\u00e3o todas abaixo:<\/p>\r\n\r\n\r\n\r\n<h3 class=\"wp-block-heading\">Encontrar usu\u00e1rios<\/h3>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code>\/\/ Encontrar usu\u00e1rios<br \/> app.get(\"\/users\", authenticate, async (req, res, next) =&gt; {<br \/>   try {<br \/>     const users = await User.find({})<\/code><\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-preformatted\"><code>if (!users.length) return res.status(400).send({error: \"Unable to get users.\"});\r\n\r\nres.status(200).send(users.map(user =&gt; (\r\n  {\r\n    _id: user.id,\r\n    username: user.username\r\n  }\r\n)))<\/code><\/pre>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">&nbsp;<\/p>\r\n\r\n\r\n\r\n<h3 class=\"wp-block-heading\">Encontrar todos os tweets<\/h3>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code>\/\/ Encontrar todos os tweets<br \/> app.get(\"\/tweets\", authenticate, async (req, res, next) =&gt; {<br \/>   try {<br \/>     const tweets = await Tweet.find({})<\/code><\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-preformatted\"><code>res.status(200).send(tweets);<\/code><\/pre>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code>} catch (err) {           <br \/>     res.status(400);<br \/>     next(err);<br \/>   }<br \/> });<\/code><\/p>\r\n\r\n\r\n\r\n<h3 class=\"wp-block-heading\">Encontrar tweet espec\u00edfico<\/h3>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code>\/\/ Encontrar tweet espec\u00edfico<br \/>app.get(\"\/tweets\/:id\", authenticate, <strong>async<\/strong> (req, res, next) =&gt; {<br \/>\u00a0 <strong>const<\/strong> { id } = req.params;<br \/><br \/>\u00a0 <strong>try<\/strong> {<br \/>\u00a0 \u00a0 <strong>const<\/strong> tweet = <strong>await<\/strong> Tweet.findById(id)<br \/><br \/>\u00a0 \u00a0 <strong>if<\/strong> (!tweet) <strong>return<\/strong> res.status(400).send({error: \"Tweet not found.\"});<br \/><br \/>\u00a0 \u00a0 res.status(200).send(tweet)<br \/><br \/>\u00a0 } <strong>catch<\/strong> (err) { \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <br \/>\u00a0 \u00a0 res.status(400);\u00a0\u00a0\u00a0\u00a0next(err);<br \/>\u00a0 }<\/code><br \/><code> }); <\/code><\/p>\r\n\r\n\r\n\r\n<h2 class=\"wp-block-heading\">Organiza\u00e7\u00e3o do projeto<\/h2>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Nossa aplica\u00e7\u00e3o est\u00e1 terminada, por\u00e9m nossas fun\u00e7\u00f5es est\u00e3o todas sendo aplicadas no arquivo principal. Isso atrapalha a manuten\u00e7\u00e3o e demonstra que n\u00e3o nos preocupamos em separar os m\u00f3dulos que comp\u00f5em o app. Vamo organizar para que nossa experi\u00eancia mantendo esse servidor vivo seja a melhor poss\u00edvel.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Para organizar as rotas podemos criar um arquivo <em>src\/routes.js<\/em> e la declarar todas as rotas como o seguinte:<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code>const { Router } = require(\"express\");<br \/> const bcrypt = require(\"bcryptjs\");<br \/> const jwt = require(\"jsonwebtoken\");<\/code><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code>const authenticate = require(\".\/auth\");<br \/> const User = require(\"..\/model\/User\");<br \/> const Tweet = require(\"..\/model\/Tweet\");<\/code><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code>const router = new Router();<\/code><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code>\/\/ Criar usu\u00e1rio<br \/> router.post(\"\/register\", authenticate, async (req, res, next) =&gt; {});<\/code><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code>\/\/ Login<br \/> router.post(\"\/login\", authenticate, async (req, res, next) =&gt; {});<\/code><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code>\/\/ \u2026 Demais rotas<\/code><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code>module.exports = router;<\/code><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Aqui utilizamos o Router do Express que \u00e9 um objeto espec\u00edfico para lidar com as rotas, mas que funciona basicamente como o pr\u00f3pria app.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Para organizarmos os middlewares podemos tamb\u00e9m criar um arquivo separado e cham\u00e1-lo de <em>src\/middleware.js, <\/em>a implementa\u00e7\u00e3o ficaria com a seguinte como o seguinte:<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code>\/\/ Middleware recurso n\u00e3o encontrado<br \/> const notFound = (req, res, next) =&gt; {<br \/>   const error = new Error(Not found - ${req.originalUrl});<br \/>   res.status(404);<br \/>   next(error);<br \/> };<\/code><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code>\/\/ Middleware de tratamento de erro<br \/> const errorHandling = (error, req, res, next) =&gt; {<br \/>   const statusCode = res.statusCode === 200 ? 500 : res.statusCode;<br \/>   res.statusCode = statusCode;<br \/>   res.json({<br \/>     message: error.message,<br \/>     trace: process.env.NODE_ENV === \"production\" ? \"\ud83e\udd13\" : error.trace<br \/>   });<br \/> };<\/code><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><code>module.exports = {<br \/>   notFound,<br \/>   errorHandling<br \/> };<\/code><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Ao fim dos ajustes seu arquivo <em>src\/app.js<\/em> deve estar parecido com o seguinte:<\/p>\r\n\r\n\r\n\r\n<figure class=\"wp-block-table\">\r\n<table>\r\n<tbody>\r\n<tr>\r\n<td><code><strong>const<\/strong> express = require(\"express\");<br \/><strong>const<\/strong> mongoose = require(\"mongoose\");<br \/><strong>const<\/strong> morgan = require(\"morgan\");<br \/><strong>const<\/strong> cors = require(\"cors\");<br \/>require(\"dotenv\").config();<br \/><br \/><strong>const<\/strong> router = require(\".\/routes\");<br \/><strong>const<\/strong> middlewares = require(\".\/middlewares\");<br \/><br \/><strong>const<\/strong> app = express();<br \/><br \/>app.use(morgan(\"common\"));<br \/>app.use(express.json());<br \/>app.use(cors({<br \/>\u00a0 \u00a0 origin: process.env.CORS_ORIGIN<br \/>\u00a0 })<br \/>);<br \/><br \/>mongoose.connect(<br \/>\u00a0 process.env.DB_URL,<br \/>\u00a0 {<br \/>\u00a0 \u00a0 useNewUrlParser: <strong>true<\/strong>,<br \/>\u00a0 \u00a0 useUnifiedTopology: <strong>true<\/strong><br \/>\u00a0 },<br \/>\u00a0 () =&gt; console.log(\"Connected to the database!\")<br \/>);<br \/><br \/>app.use(router);<br \/><br \/>\/\/ Middleware recurso n\u00e3o encontrado<br \/>app.use(middlewares.notFound);<br \/>\/\/ Middleware de tratamento de erro<br \/>app.use(middlewares.errorHandling);<br \/><br \/><strong>const<\/strong> PORT = 3333;<br \/>app.listen(PORT, () =&gt; {<br \/>\u00a0 console.log(`Server running on port: ${PORT}`);<br \/>});<\/code><\/td>\r\n<\/tr>\r\n<\/tbody>\r\n<\/table>\r\n<\/figure>\r\n\r\n\r\n\r\n<h2 class=\"wp-block-heading\">Com esse Node JS tutorial fica f\u00e1cil, n\u00e9?<\/h2>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Com isso terminamos a implementa\u00e7\u00e3o do nosso back-end, implementamos as seguintes funcionalidades:<\/p>\r\n\r\n\r\n\r\n<ul class=\"wp-block-list\">\r\n<li>Rotas espec\u00edficas para cada recurso;<\/li>\r\n<li>Autentica\u00e7\u00e3o de usu\u00e1rio;<\/li>\r\n<li>Integra\u00e7\u00e3o com banco de dados NoSQL (MongoDB);<\/li>\r\n<li>Registro de logs;<\/li>\r\n<li>Teste de rotas com o Insomnia.<\/li>\r\n<\/ul>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Com esse tutorial, minha inten\u00e7\u00e3o \u00e9 mostrar como \u00e9 simples implementar uma aplica\u00e7\u00e3o back-end utilizando as melhores tecnologias presentes no mercado. Todos os conceitos aqui mostrados s\u00e3o utilizados diariamente no dia a dia de um desenvolvedor web, n\u00e3o interessando a linguagem ou o framework.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Gostaria de deixar claro que estou aberto a responder qualquer d\u00favida atrav\u00e9s do email <a href=\"mailto:lucas.picollo@geekhunter.com.br\">lucas.picollo@geekhunter.com.br<\/a> ou pelo slack do React Brasil como Lucas Picollo. O c\u00f3digo da aplica\u00e7\u00e3o est\u00e1 no <a href=\"https:\/\/github.com\/picolloo\/twitter-app\">meu GitHub. <\/a><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Agrade\u00e7o a todos que participaram deste tutorial e volto em breve com a segunda parte e, quem sabe, mais algumas\u2026 <br \/><br \/>Muito obrigado!<\/p>\r\n","protected":false},"excerpt":{"rendered":"<p>\u00a0Node JS tutorial? Temos! Este artigo \u00e9 um bom lugar para entender os conceitos e se sentir mais confiante na hora de criar aplica\u00e7\u00f5es de ponta a ponta. Criaremos uma r\u00e9plica do Twitter com um passo a passo bem did\u00e1tico. Dividiremos em 2 partes: na primeira parte aprenderemos a criar um servidor back-end para nossa [&hellip;]<\/p>\n","protected":false},"author":167,"featured_media":9417,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[18],"tags":[],"class_list":["post-9408","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-desenvolvimiento-de-software"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v28.5 (Yoast SEO v28.5) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Node JS tutorial: Fa\u00e7a sua pr\u00f3pria vers\u00e3o do Twitter!<\/title>\n<meta name=\"description\" content=\"Com esse Node JS tutorial voc\u00ea vai entender os conceitos e se sentir mais confiante na hora de criar aplica\u00e7\u00f5es de ponta a ponta!\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.geekhunter.com\/pt\/blog\/node-js-tutorial-twitter-clone-full-stack-app-parte-1\/\" \/>\n<meta property=\"og:locale\" content=\"pt_BR\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Twitter Clone full stack app - Parte 1: Node JS tutorial\" \/>\n<meta property=\"og:description\" content=\"\u00a0Node JS tutorial? Temos! Este artigo \u00e9 um bom lugar para entender os conceitos e se sentir mais confiante na hora de criar aplica\u00e7\u00f5es de ponta a ponta.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.geekhunter.com\/pt\/blog\/node-js-tutorial-twitter-clone-full-stack-app-parte-1\/\" \/>\n<meta property=\"og:site_name\" content=\"Blog Geekhunter\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/geekhunterbr\/\" \/>\n<meta property=\"article:published_time\" content=\"2020-02-21T17:45:10+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2024-10-11T14:41:40+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.geekhunter.com/pt/blog\/wp-content\/uploads\/2020\/02\/linguagens-de-programa\u00e7\u00e3o.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"750\" \/>\n\t<meta property=\"og:image:height\" content=\"410\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Geekhunter\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@geekhunterbr\" \/>\n<meta name=\"twitter:site\" content=\"@geekhunterbr\" \/>\n<meta name=\"twitter:label1\" content=\"Escrito por\" \/>\n\t<meta name=\"twitter:data1\" content=\"Geekhunter\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. tempo de leitura\" \/>\n\t<meta name=\"twitter:data2\" content=\"24 minutos\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.geekhunter.com\\\/pt\\\/blog\\\/node-js-tutorial-twitter-clone-full-stack-app-parte-1\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.geekhunter.com\\\/pt\\\/blog\\\/node-js-tutorial-twitter-clone-full-stack-app-parte-1\\\/\"},\"author\":{\"name\":\"Geekhunter\",\"@id\":\"https:\\\/\\\/www.geekhunter.com\\\/pt\\\/blog\\\/#\\\/schema\\\/person\\\/9f62b173345139f7bbdaf55312c3d400\"},\"headline\":\"Twitter Clone full stack app &#8211; Parte 1: Node JS tutorial\",\"datePublished\":\"2020-02-21T17:45:10+00:00\",\"dateModified\":\"2024-10-11T14:41:40+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.geekhunter.com\\\/pt\\\/blog\\\/node-js-tutorial-twitter-clone-full-stack-app-parte-1\\\/\"},\"wordCount\":4643,\"publisher\":{\"@id\":\"https:\\\/\\\/www.geekhunter.com\\\/pt\\\/blog\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/www.geekhunter.com\\\/pt\\\/blog\\\/node-js-tutorial-twitter-clone-full-stack-app-parte-1\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.geekhunter.com/pt/blog\\\/wp-content\\\/uploads\\\/2020\\\/02\\\/linguagens-de-programa\u00e7\u00e3o.jpg\",\"articleSection\":[\"Desenvolvimiento de software\"],\"inLanguage\":\"pt-BR\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.geekhunter.com\\\/pt\\\/blog\\\/node-js-tutorial-twitter-clone-full-stack-app-parte-1\\\/\",\"url\":\"https:\\\/\\\/www.geekhunter.com\\\/pt\\\/blog\\\/node-js-tutorial-twitter-clone-full-stack-app-parte-1\\\/\",\"name\":\"Node JS tutorial: Fa\u00e7a sua pr\u00f3pria vers\u00e3o do Twitter!\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.geekhunter.com\\\/pt\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.geekhunter.com\\\/pt\\\/blog\\\/node-js-tutorial-twitter-clone-full-stack-app-parte-1\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.geekhunter.com\\\/pt\\\/blog\\\/node-js-tutorial-twitter-clone-full-stack-app-parte-1\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.geekhunter.com/pt/blog\\\/wp-content\\\/uploads\\\/2020\\\/02\\\/linguagens-de-programa\u00e7\u00e3o.jpg\",\"datePublished\":\"2020-02-21T17:45:10+00:00\",\"dateModified\":\"2024-10-11T14:41:40+00:00\",\"description\":\"Com esse Node JS tutorial voc\u00ea vai entender os conceitos e se sentir mais confiante na hora de criar aplica\u00e7\u00f5es de ponta a ponta!\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.geekhunter.com\\\/pt\\\/blog\\\/node-js-tutorial-twitter-clone-full-stack-app-parte-1\\\/#breadcrumb\"},\"inLanguage\":\"pt-BR\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.geekhunter.com/pt/blog\\\/node-js-tutorial-twitter-clone-full-stack-app-parte-1\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"pt-BR\",\"@id\":\"https:\\\/\\\/www.geekhunter.com\\\/pt\\\/blog\\\/node-js-tutorial-twitter-clone-full-stack-app-parte-1\\\/#primaryimage\",\"url\":\"https:\\\/\\\/www.geekhunter.com\\\/pt\\\/blog\\\/wp-content\\\/uploads\\\/2020\\\/02\\\/linguagens-de-programa\u00e7\u00e3o.jpg\",\"contentUrl\":\"https:\\\/\\\/www.geekhunter.com/pt/blog\\\/wp-content\\\/uploads\\\/2020\\\/02\\\/linguagens-de-programa\u00e7\u00e3o.jpg\",\"width\":750,\"height\":410,\"caption\":\"Node JS tutorial\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.geekhunter.com\\\/pt\\\/blog\\\/node-js-tutorial-twitter-clone-full-stack-app-parte-1\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"In\u00edcio\",\"item\":\"https:\\\/\\\/www.geekhunter.com/pt/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Mundo TI\",\"item\":\"https:\\\/\\\/www.geekhunter.com/pt/blog\\\/category\\\/mundo-ti\\\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Desenvolvimiento de software\",\"item\":\"https:\\\/\\\/www.geekhunter.com/pt/blog\\\/category\\\/mundo-ti\\\/desenvolvimiento-de-software\\\/\"},{\"@type\":\"ListItem\",\"position\":4,\"name\":\"Twitter Clone full stack app &#8211; Parte 1: Node JS tutorial\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.geekhunter.com\\\/pt\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/www.geekhunter.com\\\/pt\\\/blog\\\/\",\"name\":\"Blog de TI\",\"description\":\"Dicas para o RH e profissionais da \u00e1rea TI\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.geekhunter.com\\\/pt\\\/blog\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.geekhunter.com/pt/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"pt-BR\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/www.geekhunter.com\\\/pt\\\/blog\\\/#organization\",\"name\":\"GeekHunter\",\"url\":\"https:\\\/\\\/www.geekhunter.com\\\/pt\\\/blog\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"pt-BR\",\"@id\":\"https:\\\/\\\/www.geekhunter.com\\\/pt\\\/blog\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/www.geekhunter.com\\\/pt\\\/blog\\\/wp-content\\\/uploads\\\/2024\\\/10\\\/icon-1.png\",\"contentUrl\":\"https:\\\/\\\/www.geekhunter.com/pt/blog\\\/wp-content\\\/uploads\\\/2024\\\/10\\\/icon-1.png\",\"width\":300,\"height\":300,\"caption\":\"GeekHunter\"},\"image\":{\"@id\":\"https:\\\/\\\/www.geekhunter.com\\\/pt\\\/blog\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/www.facebook.com\\\/geekhunterbr\\\/\",\"https:\\\/\\\/x.com\\\/geekhunterbr\",\"https:\\\/\\\/www.instagram.com\\\/geekhunterbr\\\/\",\"https:\\\/\\\/www.linkedin.com\\\/company\\\/geekhunter-brasil\\\/\",\"https:\\\/\\\/www.youtube.com\\\/@GeekHunterOfficial\"]},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/www.geekhunter.com\\\/pt\\\/blog\\\/#\\\/schema\\\/person\\\/9f62b173345139f7bbdaf55312c3d400\",\"name\":\"Geekhunter\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"pt-BR\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/09a0f76de8cf1fa3621f8b73417ce43344b42e1324a07a9af24f57c5eef49840?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/09a0f76de8cf1fa3621f8b73417ce43344b42e1324a07a9af24f57c5eef49840?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/09a0f76de8cf1fa3621f8b73417ce43344b42e1324a07a9af24f57c5eef49840?s=96&d=mm&r=g\",\"caption\":\"Geekhunter\"},\"url\":\"https:\\\/\\\/www.geekhunter.com\\\/pt\\\/blog\\\/author\\\/matias\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Node JS tutorial: Fa\u00e7a sua pr\u00f3pria vers\u00e3o do Twitter!","description":"Com esse Node JS tutorial voc\u00ea vai entender os conceitos e se sentir mais confiante na hora de criar aplica\u00e7\u00f5es de ponta a ponta!","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.geekhunter.com\/pt\/blog\/node-js-tutorial-twitter-clone-full-stack-app-parte-1\/","og_locale":"pt_BR","og_type":"article","og_title":"Twitter Clone full stack app - Parte 1: Node JS tutorial","og_description":"\u00a0Node JS tutorial? Temos! Este artigo \u00e9 um bom lugar para entender os conceitos e se sentir mais confiante na hora de criar aplica\u00e7\u00f5es de ponta a ponta.","og_url":"https:\/\/www.geekhunter.com\/pt\/blog\/node-js-tutorial-twitter-clone-full-stack-app-parte-1\/","og_site_name":"Blog Geekhunter","article_publisher":"https:\/\/www.facebook.com\/geekhunterbr\/","article_published_time":"2020-02-21T17:45:10+00:00","article_modified_time":"2024-10-11T14:41:40+00:00","og_image":[{"width":750,"height":410,"url":"https:\/\/www.geekhunter.com/pt/blog\/wp-content\/uploads\/2020\/02\/linguagens-de-programa\u00e7\u00e3o.jpg","type":"image\/jpeg"}],"author":"Geekhunter","twitter_card":"summary_large_image","twitter_creator":"@geekhunterbr","twitter_site":"@geekhunterbr","twitter_misc":{"Escrito por":"Geekhunter","Est. tempo de leitura":"24 minutos"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.geekhunter.com\/pt\/blog\/node-js-tutorial-twitter-clone-full-stack-app-parte-1\/#article","isPartOf":{"@id":"https:\/\/www.geekhunter.com\/pt\/blog\/node-js-tutorial-twitter-clone-full-stack-app-parte-1\/"},"author":{"name":"Geekhunter","@id":"https:\/\/www.geekhunter.com\/pt\/blog\/#\/schema\/person\/9f62b173345139f7bbdaf55312c3d400"},"headline":"Twitter Clone full stack app &#8211; Parte 1: Node JS tutorial","datePublished":"2020-02-21T17:45:10+00:00","dateModified":"2024-10-11T14:41:40+00:00","mainEntityOfPage":{"@id":"https:\/\/www.geekhunter.com\/pt\/blog\/node-js-tutorial-twitter-clone-full-stack-app-parte-1\/"},"wordCount":4643,"publisher":{"@id":"https:\/\/www.geekhunter.com\/pt\/blog\/#organization"},"image":{"@id":"https:\/\/www.geekhunter.com\/pt\/blog\/node-js-tutorial-twitter-clone-full-stack-app-parte-1\/#primaryimage"},"thumbnailUrl":"https:\/\/www.geekhunter.com/pt/blog\/wp-content\/uploads\/2020\/02\/linguagens-de-programa\u00e7\u00e3o.jpg","articleSection":["Desenvolvimiento de software"],"inLanguage":"pt-BR"},{"@type":"WebPage","@id":"https:\/\/www.geekhunter.com\/pt\/blog\/node-js-tutorial-twitter-clone-full-stack-app-parte-1\/","url":"https:\/\/www.geekhunter.com\/pt\/blog\/node-js-tutorial-twitter-clone-full-stack-app-parte-1\/","name":"Node JS tutorial: Fa\u00e7a sua pr\u00f3pria vers\u00e3o do Twitter!","isPartOf":{"@id":"https:\/\/www.geekhunter.com\/pt\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.geekhunter.com\/pt\/blog\/node-js-tutorial-twitter-clone-full-stack-app-parte-1\/#primaryimage"},"image":{"@id":"https:\/\/www.geekhunter.com\/pt\/blog\/node-js-tutorial-twitter-clone-full-stack-app-parte-1\/#primaryimage"},"thumbnailUrl":"https:\/\/www.geekhunter.com/pt/blog\/wp-content\/uploads\/2020\/02\/linguagens-de-programa\u00e7\u00e3o.jpg","datePublished":"2020-02-21T17:45:10+00:00","dateModified":"2024-10-11T14:41:40+00:00","description":"Com esse Node JS tutorial voc\u00ea vai entender os conceitos e se sentir mais confiante na hora de criar aplica\u00e7\u00f5es de ponta a ponta!","breadcrumb":{"@id":"https:\/\/www.geekhunter.com\/pt\/blog\/node-js-tutorial-twitter-clone-full-stack-app-parte-1\/#breadcrumb"},"inLanguage":"pt-BR","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.geekhunter.com/pt/blog\/node-js-tutorial-twitter-clone-full-stack-app-parte-1\/"]}]},{"@type":"ImageObject","inLanguage":"pt-BR","@id":"https:\/\/www.geekhunter.com\/pt\/blog\/node-js-tutorial-twitter-clone-full-stack-app-parte-1\/#primaryimage","url":"https:\/\/www.geekhunter.com\/pt\/blog\/wp-content\/uploads\/2020\/02\/linguagens-de-programa\u00e7\u00e3o.jpg","contentUrl":"https:\/\/www.geekhunter.com/pt/blog\/wp-content\/uploads\/2020\/02\/linguagens-de-programa\u00e7\u00e3o.jpg","width":750,"height":410,"caption":"Node JS tutorial"},{"@type":"BreadcrumbList","@id":"https:\/\/www.geekhunter.com\/pt\/blog\/node-js-tutorial-twitter-clone-full-stack-app-parte-1\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"In\u00edcio","item":"https:\/\/www.geekhunter.com/pt/blog\/"},{"@type":"ListItem","position":2,"name":"Mundo TI","item":"https:\/\/www.geekhunter.com/pt/blog\/category\/mundo-ti\/"},{"@type":"ListItem","position":3,"name":"Desenvolvimiento de software","item":"https:\/\/www.geekhunter.com/pt/blog\/category\/mundo-ti\/desenvolvimiento-de-software\/"},{"@type":"ListItem","position":4,"name":"Twitter Clone full stack app &#8211; Parte 1: Node JS tutorial"}]},{"@type":"WebSite","@id":"https:\/\/www.geekhunter.com\/pt\/blog\/#website","url":"https:\/\/www.geekhunter.com\/pt\/blog\/","name":"Blog de TI","description":"Dicas para o RH e profissionais da \u00e1rea TI","publisher":{"@id":"https:\/\/www.geekhunter.com\/pt\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.geekhunter.com/pt/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"pt-BR"},{"@type":"Organization","@id":"https:\/\/www.geekhunter.com\/pt\/blog\/#organization","name":"GeekHunter","url":"https:\/\/www.geekhunter.com\/pt\/blog\/","logo":{"@type":"ImageObject","inLanguage":"pt-BR","@id":"https:\/\/www.geekhunter.com\/pt\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/www.geekhunter.com\/pt\/blog\/wp-content\/uploads\/2024\/10\/icon-1.png","contentUrl":"https:\/\/www.geekhunter.com/pt/blog\/wp-content\/uploads\/2024\/10\/icon-1.png","width":300,"height":300,"caption":"GeekHunter"},"image":{"@id":"https:\/\/www.geekhunter.com\/pt\/blog\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/geekhunterbr\/","https:\/\/x.com\/geekhunterbr","https:\/\/www.instagram.com\/geekhunterbr\/","https:\/\/www.linkedin.com\/company\/geekhunter-brasil\/","https:\/\/www.youtube.com\/@GeekHunterOfficial"]},{"@type":"Person","@id":"https:\/\/www.geekhunter.com\/pt\/blog\/#\/schema\/person\/9f62b173345139f7bbdaf55312c3d400","name":"Geekhunter","image":{"@type":"ImageObject","inLanguage":"pt-BR","@id":"https:\/\/secure.gravatar.com\/avatar\/09a0f76de8cf1fa3621f8b73417ce43344b42e1324a07a9af24f57c5eef49840?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/09a0f76de8cf1fa3621f8b73417ce43344b42e1324a07a9af24f57c5eef49840?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/09a0f76de8cf1fa3621f8b73417ce43344b42e1324a07a9af24f57c5eef49840?s=96&d=mm&r=g","caption":"Geekhunter"},"url":"https:\/\/www.geekhunter.com\/pt\/blog\/author\/matias\/"}]}},"_links":{"self":[{"href":"https:\/\/www.geekhunter.com/pt/blog\/wp-json\/wp\/v2\/posts\/9408","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.geekhunter.com/pt/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.geekhunter.com/pt/blog\/wp-json\/wp\/v2\/types\/post"}],"replies":[{"embeddable":true,"href":"https:\/\/www.geekhunter.com/pt/blog\/wp-json\/wp\/v2\/comments?post=9408"}],"version-history":[{"count":0,"href":"https:\/\/www.geekhunter.com/pt/blog\/wp-json\/wp\/v2\/posts\/9408\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.geekhunter.com/pt/blog\/wp-json\/wp\/v2\/media\/9417"}],"wp:attachment":[{"href":"https:\/\/www.geekhunter.com/pt/blog\/wp-json\/wp\/v2\/media?parent=9408"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.geekhunter.com/pt/blog\/wp-json\/wp\/v2\/categories?post=9408"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.geekhunter.com/pt/blog\/wp-json\/wp\/v2\/tags?post=9408"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}