<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Moprius</title>
    <description>The latest articles on Forem by Moprius (@moprius).</description>
    <link>https://forem.com/moprius</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F708604%2F64f704cb-8f38-485e-a36b-632b5b9953c5.png</url>
      <title>Forem: Moprius</title>
      <link>https://forem.com/moprius</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/moprius"/>
    <language>en</language>
    <item>
      <title>Cursor SDK, Composer 2 e a nova economia dos agentes de código</title>
      <dc:creator>Moprius</dc:creator>
      <pubDate>Fri, 15 May 2026 15:56:34 +0000</pubDate>
      <link>https://forem.com/moprius/cursor-sdk-composer-2-e-a-nova-economia-dos-agentes-de-codigo-18cd</link>
      <guid>https://forem.com/moprius/cursor-sdk-composer-2-e-a-nova-economia-dos-agentes-de-codigo-18cd</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frjhmjgi2uoownyj414f0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frjhmjgi2uoownyj414f0.png" alt="Cursor SDK" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A maneira como desenvolvedores trabalham com IA mudou radicalmente nos últimos meses, e quem está dentro dessa rotina sente a evolução semana após semana. Toda nova ferramenta exige uma adaptação do fluxo de trabalho, e logo aparece outra propondo um modelo melhor. O lançamento do SDK do Cursor em TypeScript, somado ao novo Composer 2, parece ser mais um daqueles momentos em que a fundação inteira é repensada — não apenas a ponta da interface.&lt;/p&gt;

&lt;h2&gt;
  
  
  Do CLI ao multi-agente: por que o fluxo antigo quebrou
&lt;/h2&gt;

&lt;p&gt;Por muito tempo, o fluxo "sério" de quem trabalha com agentes de código foi via CLI: várias sessões de terminal abertas, cada uma rodando um agente em um worktree distinto, às vezes em repositórios diferentes. Funciona — até um certo ponto. O ponto em questão costuma ser quatro sessões em paralelo. A partir daí, a janela de contexto da pessoa que pilota tudo aquilo passa a ser o gargalo. Não a do modelo, mas a humana.&lt;/p&gt;

&lt;p&gt;A consequência prática é familiar: você pergunta a um agente algo que estava sendo discutido com outro; manda uma instrução no terminal errado; perde o fio do que cada sessão estava fazendo; confunde o estado de um pull request aberto aguardando review com uma feature ainda em implementação local. Acrescente a isso mensagens do time chegando em paralelo — alguém de produto querendo saber como funciona uma feature flag, um bug report, um comentário de code review — e o cérebro humano simplesmente não escala.&lt;/p&gt;

&lt;p&gt;A solução não é parar de paralelizar. É parar de gerenciar o paralelismo manualmente. É exatamente esse o problema que o Cursor 3 e, mais recentemente, o SDK do Cursor se propõem a resolver.&lt;/p&gt;

&lt;h2&gt;
  
  
  O que é, de verdade, um "AI harness"
&lt;/h2&gt;

&lt;p&gt;A palavra mais importante que entrou no vocabulário do desenvolvedor de IA neste ano é &lt;em&gt;harness&lt;/em&gt;. Antes do harness, há o que muita gente assume ser o trabalho todo: o modelo. Treinar um bom modelo de código exige quantidades absurdas de dados, compute e dinheiro. Mas o modelo, sozinho, é apenas 20% do produto final. Os outros 80% — os que fazem a diferença entre uma demo bonita e um agente realmente útil — vivem no harness.&lt;/p&gt;

&lt;p&gt;O harness é a infraestrutura que cerca o modelo e transforma uma sequência de tokens em algo capaz de operar em um repositório real. Os componentes essenciais são:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Context management.&lt;/strong&gt; Antes de gerar qualquer resposta, o agente precisa encontrar o pedaço certo do código no qual atuar. Em um repositório com cem mil arquivos, isso não é trivial. As ferramentas modernas combinam várias técnicas: indexação completa do codebase, busca semântica (embeddings que encontram trechos por significado, não só por correspondência textual), busca por grep clássico (rápido e exato para nomes de função, símbolos, strings literais) e mapas de dependências para entender o que se conecta a quê. Sem isso, o LLM gasta tokens preciosos "adivinhando" onde mexer, e geralmente erra.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sandboxing.&lt;/strong&gt; Cada agente roda em uma VM dedicada, com um clone do repositório, ambiente de desenvolvimento pré-configurado e isolamento de credenciais. Esse é um detalhe que muitos subestimam. Rodar um agente diretamente na própria máquina, com acesso a chaves de produção, tokens de cloud e variáveis de ambiente sensíveis, é uma receita pronta para desastres. O sandbox transforma cada sessão em algo descartável e seguro: se o agente fizer besteira, o blast radius está contido.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Session management.&lt;/strong&gt; O agente precisa sobreviver às intempéries do mundo real. Laptop entra em modo de espera, conexão de rede cai, o computador é reiniciado. Quando o desenvolvedor volta, a sessão tem que estar lá, exatamente onde parou. Isso exige persistência de estado, checkpoints, sincronização entre cliente e backend, e tratamento robusto de falhas de rede. É o tipo de problema clássico de sistemas distribuídos que, paradoxalmente, virou central em uma ferramenta de codificação.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ferramental: MCP, hooks, skills, subagents.&lt;/strong&gt; O agente precisa de ferramentas para ler arquivos, rodar comandos, consultar APIs externas. O padrão emergente é o MCP (Model Context Protocol), que define uma forma uniforme de servir tools a qualquer modelo. Por cima disso vêm os &lt;em&gt;hooks&lt;/em&gt; (pontos de extensão que executam código do usuário em momentos específicos do ciclo do agente — antes da edição, depois do commit, antes de chamar o modelo), as &lt;em&gt;skills&lt;/em&gt; (pacotes de instruções e arquivos auxiliares que o agente carrega quando o contexto pede) e os &lt;em&gt;subagents&lt;/em&gt; (capacidade de o agente principal delegar uma subtarefa a outro agente especializado, com contexto isolado).&lt;/p&gt;

&lt;p&gt;Cada um desses componentes é, por si só, um produto. Construir um harness completo do zero, com qualidade de produção, é trabalho para um time inteiro durante muitos meses. É exatamente esse o ponto: até agora, quem queria ter um agente próprio precisava reimplementar tudo isso. O Cursor SDK muda essa equação.&lt;/p&gt;

&lt;h2&gt;
  
  
  O Cursor SDK em TypeScript
&lt;/h2&gt;

&lt;p&gt;Em três linhas de código em TypeScript, o SDK permite instanciar um agente com o mesmo runtime, o mesmo harness, o mesmo sandbox e o mesmo sistema de context management que rodam dentro do Cursor 3. O modelo padrão é o Composer 2, mas qualquer modelo suportado pode ser escolhido. A execução pode acontecer localmente para iteração rápida, na cloud do Cursor para cargas pesadas ou em ambiente self-hosted para empresas com restrições de rede e compliance.&lt;/p&gt;

&lt;p&gt;A implicação prática é grande. Quem tem um produto que poderia se beneficiar de um agente embarcado — uma ferramenta interna de DevOps, um assistente customizado para um stack específico, um automator de tarefas repetitivas em um SaaS — agora consegue colocar isso de pé em horas, não meses. O SDK assume todo o trabalho de infraestrutura, e o desenvolvedor se concentra na lógica do produto, nas tools específicas do domínio e na experiência do usuário.&lt;/p&gt;

&lt;p&gt;Outra mudança importante é filosófica. Até pouco tempo, as ferramentas de IA para código se dividiam em duas categorias estanques: produtos de prateleira (Cursor, Copilot, Windsurf) e frameworks para construir seu próprio agente (LangChain, LangGraph, frameworks customizados). Quem escolhia o primeiro caminho ficava preso à interface; quem escolhia o segundo reimplementava o mundo todo. O SDK borra essa fronteira: a mesma fundação que serve o produto de prateleira está agora disponível como biblioteca para quem quer construir algo próprio.&lt;/p&gt;

&lt;h2&gt;
  
  
  Composer 2: o caso dos modelos especializados em código
&lt;/h2&gt;

&lt;p&gt;Ao lado do SDK, há outra peça que merece atenção: o Composer 2. Ao contrário dos modelos generalistas das grandes fornecedoras, o Composer 2 foi treinado especificamente para tarefas de código. A diferença não está apenas no fine-tuning de superfície — é uma decisão de arquitetura e de pipeline de treinamento. O reinforcement learning é direcionado a tarefas de programação reais: passar testes, resolver issues de repositórios open source, executar refactorings corretos, operar em terminais.&lt;/p&gt;

&lt;p&gt;Os benchmarks específicos do domínio (SWE-bench Pro, Terminal-Bench 2.0, Cursor Bench) mostram resultados de nível "frontier" — comparáveis aos melhores modelos generalistas em tarefas de código. Mas a métrica que muda tudo é a econômica: o custo de inferência fica em uma fração do que se paga por um modelo generalista equivalente em capacidade.&lt;/p&gt;

&lt;p&gt;Esse ponto merece um momento. A indústria está percebendo que treinar um modelo gigantesco para "fazer tudo" tem retorno decrescente. Um modelo de 200 ou 300 bilhões de parâmetros que sabe escrever poesia, traduzir, programar, raciocinar sobre física e debater filosofia é caro de servir e, em qualquer uma dessas tarefas, perde para um modelo menor e bem treinado naquele domínio específico. Para tarefas de código, em que o universo de respostas válidas tem estrutura forte (sintaxe, tipos, semântica) e em que existem sinais de recompensa claros (compila? passa nos testes?), modelos especializados podem ser drasticamente mais eficientes.&lt;/p&gt;

&lt;p&gt;A consequência para o desenvolvedor é direta: o custo por tarefa cai. Um agente que antes consumia o equivalente a alguns dólares para resolver um bug agora consome centavos. Isso muda completamente o cálculo de quando vale a pena usar um agente. Tarefas que antes pareciam pequenas demais para "queimar" um modelo caro agora cabem confortavelmente no orçamento.&lt;/p&gt;

&lt;h2&gt;
  
  
  A economia do token: a nova métrica de eficiência
&lt;/h2&gt;

&lt;p&gt;Em equipes de desenvolvimento que adotam essas ferramentas em escala, uma métrica vem ganhando espaço nos dashboards: tokens consumidos por desenvolvedor por período. Não é incomum encontrar engenheiros queimando 25 milhões de tokens por semana, ou mais de 50 milhões em vinte dias. Esses números traduzidos em dólar, dependendo do modelo, podem chegar a milhares de dólares por mês por pessoa.&lt;/p&gt;

&lt;p&gt;Isso não é necessariamente um problema — se o retorno em produtividade compensa, é dinheiro bem gasto. Mas exige uma disciplina nova: a de escolher o modelo certo para cada tarefa. Usar o modelo mais caro disponível para todas as interações é o equivalente a usar um bisturi de cirurgia para cortar pão. Funciona, mas é desperdício.&lt;/p&gt;

&lt;p&gt;A regra emergente é simples na intenção e complexa na execução: tarefas de exploração e geração de código de média complexidade vão para o modelo especializado (Composer 2 ou equivalente); tarefas que exigem raciocínio profundo, decisões arquiteturais ou síntese de muitos artefatos diferentes vão para o modelo generalista de topo (Claude Opus, GPT da geração mais recente, Gemini Ultra). Tarefas pequenas — formatação, renomeação, refactorings simples — podem ir para modelos menores e mais baratos.&lt;/p&gt;

&lt;p&gt;A métrica de "eficiência por mil tokens" (custo dividido por valor entregue) está se tornando central. Reduzir essa métrica de uma fração de centavo para uma fração menor ainda, vezes dezenas de milhões de tokens por mês, vira economia real.&lt;/p&gt;

&lt;h2&gt;
  
  
  Multitarefa com agentes: o novo workflow
&lt;/h2&gt;

&lt;p&gt;Com o harness pronto e a economia equacionada, o workflow muda de forma. A interface visual de múltiplas sessões — cada uma representando uma tarefa em andamento, com seu próprio estado, repositório, branch e histórico de conversação — passa a ser o centro de operações.&lt;/p&gt;

&lt;p&gt;Algumas práticas se firmam:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pinar sessões ativas.&lt;/strong&gt; Sessões que têm um pull request aberto aguardando review, ou que envolvem uma tarefa que precisa ser retomada na manhã seguinte, ficam fixadas. Ao abrir a ferramenta, é a primeira coisa que aparece. É o equivalente moderno do "post-it na tela".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ordenar por última atualização.&lt;/strong&gt; Tarefas dormentes saem do campo de visão; tarefas que receberam input recente (um comentário de review, uma resposta de CI) sobem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Indicadores visuais de estado.&lt;/strong&gt; Saber de relance se um PR foi mergeado, fechado, ou ainda está aberto reduz drasticamente o custo cognitivo de gerenciar dezenas de tarefas simultâneas. É a diferença entre vasculhar abas de terminal e olhar para um kanban.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Separação por VM.&lt;/strong&gt; Como cada agente roda em sandbox próprio, alternar entre tarefas não tem custo de "trocar de contexto" no nível do ambiente. A branch certa está checada, as dependências instaladas, o estado do banco de testes preservado. O desenvolvedor só precisa trocar de contexto mentalmente — e mesmo isso fica mais fácil quando a interface ajuda.&lt;/p&gt;

&lt;p&gt;O resultado prático é que o limite de quatro tarefas em paralelo (típico de quem trabalha via CLI) sobe consideravelmente. Não é incomum manter dez, quinze sessões ativas, com diferentes graus de envolvimento humano em cada uma. Algumas exigem atenção constante; outras estão no modo "agente trabalha sozinho até concluir ou pedir ajuda".&lt;/p&gt;

&lt;h2&gt;
  
  
  Para onde isso aponta
&lt;/h2&gt;

&lt;p&gt;A combinação de SDK aberto, modelo especializado eficiente e infraestrutura de execução robusta indica uma transição importante. Ferramentas de IA para código deixam de ser apenas "lugares onde você programa" e passam a ser plataformas sobre as quais outras ferramentas são construídas. A linha entre "usuário do produto" e "desenvolvedor que constrói com a plataforma" começa a se dissolver.&lt;/p&gt;

&lt;p&gt;Para quem desenvolve software como atividade principal, isso significa mais alavancagem. O limite do que uma pessoa consegue produzir em um dia deixou de ser a velocidade de digitação ou o número de janelas que cabem na tela. Passa a ser a capacidade de orquestrar agentes, definir bem cada tarefa, revisar com critério o que volta e manter a visão de produto no comando das decisões importantes.&lt;/p&gt;

&lt;p&gt;Para empresas que dependem de software como meio (a maioria, hoje), o impacto é estratégico. Construir agentes próprios para fluxos internos, com a mesma qualidade técnica das ferramentas comerciais de ponta, agora cabe no orçamento e no calendário. Quem entender isso primeiro vai ter uma vantagem competitiva que não vinha da escolha de stack — vinha da escolha de quando, e em quê, aplicar agentes.&lt;/p&gt;

&lt;p&gt;E para o ecossistema como um todo, vale registrar: estamos saindo de uma fase em que o gargalo era o modelo, e entrando em uma fase em que o gargalo é a infraestrutura ao redor dele. Os próximos avanços relevantes em produtividade de desenvolvedores provavelmente não virão de modelos significativamente maiores. Virão de harnesses mais inteligentes, melhor integração com ferramentas existentes e workflows que respeitem como humanos realmente trabalham — em paralelo, de forma fragmentada, com interrupções, e precisando preservar contexto entre sessões longas.&lt;/p&gt;

&lt;p&gt;O Cursor SDK, com tudo que ele encapsula, é um marco nesse caminho. Vale prestar atenção.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>development</category>
      <category>cursos</category>
      <category>programming</category>
    </item>
    <item>
      <title>Hunyuan Preview: o gigante chinês entra de vez na corrida dos modelos abertos</title>
      <dc:creator>Moprius</dc:creator>
      <pubDate>Fri, 15 May 2026 14:18:51 +0000</pubDate>
      <link>https://forem.com/moprius/hunyuan-preview-o-gigante-chines-entra-de-vez-na-corrida-dos-modelos-abertos-o7</link>
      <guid>https://forem.com/moprius/hunyuan-preview-o-gigante-chines-entra-de-vez-na-corrida-dos-modelos-abertos-o7</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdzmtgxrpom4g82e6avhh.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdzmtgxrpom4g82e6avhh.webp" alt="Hunyuan Preview" width="800" height="276"&gt;&lt;/a&gt;&lt;br&gt;
Quando se fala em IA chinesa open source, os nomes que costumam vir à cabeça são DeepSeek, Qwen, GLM, MiniMax, Kimi — modelos saídos de laboratórios e empresas relativamente pequenas em comparação aos colossos do setor. Falta, nessa lista, um peso pesado óbvio: a Tencent. Maior empresa da China, dona do WeChat (uma espécie de WhatsApp, banco, marketplace e rede social fundidos em um único aplicativo), a Tencent até agora aparecia pouco nas conversas sobre modelos abertos competitivos. O Hunyuan Preview muda esse cenário.&lt;/p&gt;

&lt;h2&gt;
  
  
  O que é o Hunyuan Preview
&lt;/h2&gt;

&lt;p&gt;O Hunyuan-A13B Preview (também referido como HY3 Preview) é um modelo open source da Tencent, disponibilizado no Hugging Face com pesos abertos. A pronúncia do nome, para quem se aventura no mandarim, fica algo próximo de "ruan-san", e não "hai-san" — uma confusão comum entre falantes ocidentais, já que o "H" do pinyin tem som diferente do esperado.&lt;/p&gt;

&lt;p&gt;A ficha técnica chama atenção:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;295 bilhões de parâmetros totais&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;21 bilhões de parâmetros ativos&lt;/strong&gt; por token&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Arquitetura Mixture of Experts (MoE)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Janela de contexto de 256 mil tokens&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Suporte a MTP (Multi-Token Prediction)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Modos de "thinking" configuráveis&lt;/strong&gt; (low e high)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Para efeito de comparação, o Kimi K2 opera na faixa de 1 trilhão de parâmetros, enquanto modelos como GLM 4.5 e DeepSeek V3 ficam em patamares semelhantes ao Hunyuan. O ponto interessante é justamente esse: com cerca de 30% do tamanho do Kimi, o Hunyuan se propõe a chegar perto em capacidade prática.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mixture of Experts: por que importa
&lt;/h2&gt;

&lt;p&gt;A arquitetura MoE é o que torna o modelo viável fora de data centers gigantes. Em um modelo "denso" tradicional, cada token gerado precisa passar por todos os parâmetros da rede — se o modelo tem 295 bilhões de parâmetros, todos eles trabalham em cada palavra produzida. Isso é caro em memória e devastador em velocidade.&lt;/p&gt;

&lt;p&gt;No MoE, o modelo é dividido em "especialistas" — sub-redes que se ativam seletivamente. Para cada token, um roteador escolhe um pequeno subconjunto de especialistas para fazer o trabalho. No caso do Hunyuan, são 21 bilhões de parâmetros ativos por vez, de um total de 295 bilhões. O efeito prático é que a inferência tem o custo de um modelo de 21B, mas o conhecimento e a capacidade de um modelo várias vezes maior.&lt;/p&gt;

&lt;p&gt;A contrapartida é o consumo de memória: o modelo inteiro precisa estar carregado na RAM ou na VRAM, mesmo que só uma fração esteja em uso a cada passo. Por isso o MoE é especialmente amigável para arquiteturas com muita memória unificada (como os Macs da linha Studio com chips M3/M4 Ultra) e menos prático em GPUs tradicionais, onde 24 ou 32 GB de VRAM são o teto comum.&lt;/p&gt;

&lt;h2&gt;
  
  
  MTP: a moda do "Multi-Token Prediction"
&lt;/h2&gt;

&lt;p&gt;O MTP é uma das técnicas que mais ganharam tração no último ano e está presente em quase todos os modelos chineses recentes de ponta. A ideia básica é simples: em vez de gerar um token de cada vez, o modelo tenta prever vários tokens à frente de uma só vez.&lt;/p&gt;

&lt;p&gt;Na prática, funciona como uma espécie de "decodificação especulativa interna". Um modelo menor (ou cabeças auxiliares acopladas ao modelo principal) palpita vários tokens à frente, e o modelo grande apenas valida se aquela sequência faz sentido. Se faz, todos os tokens são aceitos de uma vez; se não, descarta-se a partir do ponto em que a divergência aparece, e o processo recomeça.&lt;/p&gt;

&lt;p&gt;O ganho é significativo, especialmente em hardware com largura de banda limitada de memória — o gargalo real da inferência local não costuma ser o poder de cálculo da GPU, e sim a velocidade com que os pesos do modelo são lidos da memória. Quando uma única passagem produz dois, três ou quatro tokens em vez de um, a velocidade efetiva pode dobrar ou triplicar sem grandes alterações na receita do modelo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Janela de contexto e modo de raciocínio
&lt;/h2&gt;

&lt;p&gt;Os 256 mil tokens de contexto colocam o Hunyuan no mesmo patamar dos modelos comerciais de fronteira. Para fins práticos, isso significa caber confortavelmente bases de código médias, livros inteiros ou históricos longos de conversa sem precisar recorrer a estratégias de compressão ou recuperação.&lt;/p&gt;

&lt;p&gt;O sistema de "thinking" com níveis configuráveis (low/high) segue a tendência inaugurada pelos modelos de raciocínio. No nível low, o modelo pensa pouco antes de responder — útil para tarefas diretas. No high, a cadeia de raciocínio se estende, com o modelo elaborando planos, esboçando estruturas e revisando o próprio raciocínio antes de produzir a resposta final. Em tarefas como programação de jogos completos em um único arquivo, o modo high pode consumir vários milhares de tokens só na fase de pensamento — mas o resultado tende a ser drasticamente melhor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Como ele se compara
&lt;/h2&gt;

&lt;p&gt;Nos benchmarks divulgados, o Hunyuan Preview se posiciona ombro a ombro com Gemini 3.1 Pro, GLM 5, Qwen 2.5 e GPT-5.4 — modelos da geração anterior, vale dizer, já que GLM 5.1, Qwen 2.6 e GPT-5.5 já existem. Em SWE-bench, benchmark que mede capacidade real de resolver issues em repositórios de código, o salto da geração HY2 para HY3 é considerável, maior do que os saltos vistos entre versões consecutivas de Qwen ou GLM.&lt;/p&gt;

&lt;p&gt;A advertência usual sobre benchmarks vale aqui: os números refletem desempenho em tarefas padronizadas, e a experiência no uso diário pode ser bem diferente. Mas o quadro geral indica um modelo competitivo, ainda que não líder absoluto.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comportamento na prática
&lt;/h2&gt;

&lt;p&gt;Em testes de geração de aplicações web completas — uma página HTML usando as APIs nativas de Text-to-Speech e Speech-to-Text do navegador para apoio ao estudo de mandarim, por exemplo — o modelo entrega código funcional, com um design razoável (embora não excepcional) e implementação correta das integrações com a Web Speech API. Pequenos detalhes de usabilidade, como o posicionamento dos elementos, ficam abaixo do esperado, mas nada que exija intervenção pesada.&lt;/p&gt;

&lt;p&gt;Para jogos mais complexos, o resultado é misto. Um clone de jogo de sinuca em HTML/Canvas saiu praticamente funcional, com física razoável de colisão entre bolas, controle de taco por mouse e visual em 2D. Apenas um erro de digitação no código (uma variável duplicando uma letra) impediu a execução de primeira — correção trivial.&lt;/p&gt;

&lt;p&gt;Um clone de Doom usando Three.js também funcionou na primeira tentativa, sem erros de sintaxe. O resultado é simples: uma arena aberta, alguns inimigos com movimentos básicos, uma pistola e uma shotgun. Falta munição reabastecível, sistema de fases, variedade real de inimigos. Mas para um único prompt e um único arquivo, o esqueleto está lá.&lt;/p&gt;

&lt;p&gt;Já em tarefas mais ambiciosas, como um clone de Zelda em estilo voxel com mapa, inimigos, sistema de chaves e boss — descritas em prompts longos e detalhados —, o modelo tropeça. O código gerado precisou de várias rodadas de correção, e mesmo após ajustes, parte das mecânicas especificadas no prompt acabou não sendo implementada. É o tipo de tarefa em que modelos no topo absoluto da curva (como Qwen 3.6 Max) ainda têm vantagem clara.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance local: o caso do Mac Studio
&lt;/h2&gt;

&lt;p&gt;Aqui aparece uma das discussões mais interessantes sobre o ecossistema de modelos abertos: onde rodar.&lt;/p&gt;

&lt;p&gt;Em um Mac Studio M3 Ultra com 256 GB de memória unificada, o Hunyuan Preview em quantização Q4 (aproximadamente 166 GB de pesos) roda confortavelmente entre 20 e 24 tokens por segundo no início de uma geração, caindo para 15 a 20 tokens por segundo conforme o contexto cresce e atinge 10–15 mil tokens. Não é uma experiência fulminante, mas é perfeitamente utilizável.&lt;/p&gt;

&lt;p&gt;A comparação relevante é com modelos densos rodando em GPUs Nvidia. Uma RTX 5090 com 32 GB de VRAM consegue rodar um Qwen 3.6 de 27 bilhões de parâmetros denso em Q4 a 50–60 tokens por segundo — o dobro da velocidade. O problema é que não cabe nada muito maior do que isso na VRAM. Para rodar um modelo de 295B/21B-ativos como o Hunyuan em GPUs Nvidia tradicionais, é preciso um cluster ou hardware especializado.&lt;/p&gt;

&lt;p&gt;A largura de banda explica boa parte da diferença: a 5090 tem cerca de 1,7 TB/s de banda de memória, contra ~800 GB/s do M3 Ultra. Em modelos densos, em que toda a memória precisa ser percorrida a cada token, a GPU vence com folga. Em modelos MoE como o Hunyuan, em que apenas 21B dos 295B parâmetros são tocados por token, o Mac compensa parte da desvantagem de banda com a possibilidade de carregar o modelo inteiro.&lt;/p&gt;

&lt;p&gt;A pergunta de fundo — vale mais rodar um modelo denso de 27B ou um MoE de 295B/21B-ativos? — não tem resposta única. Modelos MoE bem treinados costumam ser, em capacidade absoluta, equivalentes a modelos densos várias vezes maiores que sua contagem de parâmetros ativos. Em teoria, 21B ativos em um MoE bem feito rivalizam com um denso de 50–80B. Na prática, depende do treinamento, dos dados, da arquitetura específica e da tarefa.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quantização: o detalhe que muda tudo
&lt;/h2&gt;

&lt;p&gt;Vale uma nota sobre quantização. O modelo nativo, em precisão de 16 bits, ocuparia perto de 600 GB — fora do alcance de qualquer hardware de consumo. As versões Q4 reduzem cada peso a quatro bits, com perda mínima de qualidade na maior parte das tarefas. Versões Q8 ficam no meio do caminho: mais fiéis ao modelo original, mas com o dobro do tamanho em memória.&lt;/p&gt;

&lt;p&gt;Plataformas como o OpenRouter geralmente servem os modelos em quantização não documentada, o que torna difícil comparar diretamente o desempenho local com o desempenho via API. Para usuários que rodam o modelo no próprio hardware, a escolha da quantização é uma decisão de compromisso entre velocidade, memória e qualidade final.&lt;/p&gt;

&lt;h2&gt;
  
  
  O que isso significa para o ecossistema
&lt;/h2&gt;

&lt;p&gt;O Hunyuan Preview consolida algo que vinha se desenhando ao longo de 2025: o open source chinês não é mais uma curiosidade ou uma alternativa de segunda linha. É a frente real de inovação em modelos abertos. Empresas que antes pareciam alheias a essa corrida — como a própria Tencent — estão entrando com modelos de qualidade competitiva e licenças permissivas.&lt;/p&gt;

&lt;p&gt;Para desenvolvedores e empresas que querem soluções de IA sem depender de APIs fechadas, o cardápio nunca foi tão amplo. Modelos densos de 20–30B para hardware modesto, MoEs gigantes para quem tem memória sobrando, modelos especializados em raciocínio, em geração de código, em multimodalidade. A "comoditização" da inferência avançada está, dia a dia, deixando de ser uma promessa.&lt;/p&gt;

&lt;p&gt;A próxima geração de hardware — Macs com chips M5 e M6, GPUs com mais VRAM, aceleradores dedicados — promete tornar essa experiência ainda mais fluida. O tempo de processamento de prefill (a fase em que o modelo "lê" o contexto antes de começar a responder), hoje um dos pontos fracos dos chips Apple comparados às GPUs Nvidia, deve melhorar substancialmente. Quando isso acontecer, rodar um modelo de 300 bilhões de parâmetros em casa será tão trivial quanto rodar um modelo de 7B é hoje.&lt;/p&gt;

&lt;p&gt;E o Hunyuan Preview, com seus 21 bilhões de parâmetros ativos e sua licença aberta, é um dos pilares dessa transição.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>agents</category>
      <category>news</category>
    </item>
    <item>
      <title>Entendendo o Linux por dentro: uma viagem pelas camadas que fazem o sistema funcionar</title>
      <dc:creator>Moprius</dc:creator>
      <pubDate>Fri, 15 May 2026 12:58:06 +0000</pubDate>
      <link>https://forem.com/moprius/entendendo-o-linux-por-dentro-uma-viagem-pelas-camadas-que-fazem-o-sistema-funcionar-2209</link>
      <guid>https://forem.com/moprius/entendendo-o-linux-por-dentro-uma-viagem-pelas-camadas-que-fazem-o-sistema-funcionar-2209</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F03mpykxs0ae8360a1j6b.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F03mpykxs0ae8360a1j6b.jpg" alt="Linux" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introdução: por que o Linux parece tão complicado (e por que ele não é, quando você sabe olhar)
&lt;/h2&gt;

&lt;p&gt;Quando você abre um navegador no Linux, digita o endereço de um site e aperta Enter, uma quantidade impressionante de engrenagens invisíveis começa a girar. Um processo conversa com outro processo. Esse outro processo pede ajuda a uma biblioteca compartilhada. Essa biblioteca, por sua vez, faz uma chamada ao núcleo do sistema operacional, que finalmente pede ao hardware que envie sinais elétricos por uma placa de rede. Tudo isso acontece em frações de segundo, sem que você precise pensar em nada disso.&lt;/p&gt;

&lt;p&gt;Para quem está começando, esse cenário parece intimidador. Existem dezenas de componentes rodando ao mesmo tempo, trocando mensagens entre si o tempo todo. E a pergunta que fica é: como compreender esse emaranhado sem se perder?&lt;/p&gt;

&lt;p&gt;A boa notícia é que existe um método. E ele tem nome: &lt;strong&gt;abstração&lt;/strong&gt;. É justamente com esse conceito que precisamos começar — e é a partir dele que conseguimos enxergar o sistema operacional não como um amontoado confuso, mas como um conjunto bem organizado de camadas com responsabilidades específicas.&lt;/p&gt;

&lt;p&gt;Esta postagem é um passeio pelos fundamentos do funcionamento do Linux, indo desde a ideia de níveis e camadas de abstração até os conceitos de usuário e permissões. A ideia é traduzir esses conceitos para uma linguagem clara, com exemplos do dia a dia, para que mesmo quem nunca abriu um terminal consiga sair daqui com uma noção sólida de como o Linux funciona por baixo do capô.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Abstração: o segredo para entender qualquer sistema complexo
&lt;/h2&gt;

&lt;p&gt;Antes de mergulhar em qualquer detalhe técnico, precisamos parar e entender o que é abstração. Uma analogia simples com um carro ajuda muito a fixar a ideia.&lt;/p&gt;

&lt;p&gt;Imagine que você é apenas um passageiro em um carro. O que você precisa saber para usufruir dessa viagem? Praticamente nada além de como abrir a porta, como afivelar o cinto e talvez como ajustar o banco. Não importa para você quais são os parafusos que prendem o motor, que tipo de óleo lubrifica os pistões, ou como foi pavimentada a estrada. Você está em um &lt;strong&gt;nível alto de abstração&lt;/strong&gt;: usa o produto final sem se preocupar com o que está por baixo.&lt;/p&gt;

&lt;p&gt;Agora suponha que você precise dirigir o carro. Subitamente, seu nível de abstração precisa diminuir um pouco. Você ainda não precisa saber montar um motor, mas precisa entender pelo menos três coisas: o próprio carro (tamanho, capacidade, particularidades), os controles (volante, acelerador, embreagem, freio) e as características da estrada (curvas, faixas, sinais de trânsito).&lt;/p&gt;

&lt;p&gt;E quando a viagem fica desconfortável, com vibrações estranhas? A abstração ajuda você a diagnosticar o problema de forma estruturada. Pode ser problema no carro? No seu modo de dirigir? Na estrada? Eliminando uma hipótese por vez, você chega à raiz do problema. Se a estrada está esburacada, talvez você queira ir ainda mais fundo: por que a estrada está assim? Houve falta de manutenção? Os operários fizeram um trabalho mal feito?&lt;/p&gt;

&lt;p&gt;Esse é exatamente o jeito como engenheiros e desenvolvedores de software pensam quando constroem sistemas operacionais. Em vez de tentar entender tudo de uma vez, eles dividem o sistema em pedaços menores e mais gerenciáveis. Cada pedaço é chamado de &lt;strong&gt;subsistema&lt;/strong&gt;, &lt;strong&gt;módulo&lt;/strong&gt;, &lt;strong&gt;pacote&lt;/strong&gt; ou, de modo mais genérico, &lt;strong&gt;componente&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A grande sacada da abstração é que, ao desenvolver um componente, o programador não precisa saber em detalhe como os outros componentes funcionam por dentro — basta saber o que cada um faz e como pedir os serviços que ele oferece. É como ligar para o serviço de entrega: você não precisa saber qual rota o entregador vai fazer ou como ele monta o veículo. Você só precisa saber o número do telefone e o que pedir.&lt;/p&gt;

&lt;p&gt;Essa lógica vai guiar todo o resto deste texto. Cada peça do Linux será apresentada não pelos detalhes internos, mas pelo papel que desempenha no funcionamento geral do sistema.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Os níveis e camadas de abstração no Linux
&lt;/h2&gt;

&lt;p&gt;A abstração ajuda a dividir o sistema em pedaços, mas, por si só, ela não basta. Precisamos de &lt;strong&gt;organização&lt;/strong&gt;. Os pedaços precisam ser arrumados em camadas, e cada camada se diferencia das outras pela sua proximidade com o hardware ou com o usuário.&lt;/p&gt;

&lt;p&gt;No topo dessa pilha, estão as coisas com as quais você interage diretamente: o navegador, o jogo, o editor de texto, o reprodutor de música. Na base, no nível mais fundamental, está o hardware puro — a memória física, os bits, os zeros e uns que circulam pelos circuitos. No meio dessa enorme distância entre o usuário e o hardware, fica o sistema operacional, ocupando várias camadas intermediárias.&lt;/p&gt;

&lt;p&gt;No Linux, podemos resumir essa organização em &lt;strong&gt;três grandes níveis&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hardware&lt;/strong&gt; — a base de tudo. Inclui a memória principal (RAM), uma ou mais unidades centrais de processamento (CPUs), discos, interfaces de rede e quaisquer outros dispositivos físicos.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Kernel&lt;/strong&gt; — o núcleo do sistema operacional. É um software que reside na memória e atua como intermediário entre o hardware e tudo o que roda acima dele. Ele controla o acesso à memória, decide qual processo usa a CPU, conversa com os dispositivos e fornece serviços essenciais para os programas.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Processos do usuário (espaço do usuário)&lt;/strong&gt; — todos os programas em execução que o kernel gerencia. Isso inclui a interface gráfica, os servidores, o shell (terminal), os navegadores, enfim, tudo o que executa "por cima" do kernel.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Vale notar que mesmo o termo "processo do usuário" é uma generalização. Quando falamos em "processo do usuário", não estamos nos referindo apenas a programas com os quais um ser humano interage diretamente. Um servidor web, por exemplo, é um processo do usuário, mesmo que nenhum ser humano esteja olhando para a tela enquanto ele atende requisições.&lt;/p&gt;

&lt;h3&gt;
  
  
  A diferença crítica entre modo kernel e modo usuário
&lt;/h3&gt;

&lt;p&gt;Aqui chegamos a um dos pontos mais importantes do assunto, e que precisa ficar bem claro: &lt;strong&gt;o kernel e os processos do usuário rodam em modos diferentes da CPU&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;O &lt;strong&gt;modo kernel&lt;/strong&gt; dá acesso irrestrito ao processador e à memória principal. É um poder enorme, mas também perigoso: um erro em código rodando em modo kernel pode corromper ou derrubar o sistema inteiro. A região da memória que só o kernel pode acessar é chamada de &lt;strong&gt;kernel space&lt;/strong&gt; (espaço do kernel).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;O &lt;strong&gt;modo usuário&lt;/strong&gt;, por outro lado, restringe o acesso a um subconjunto bem menor da memória e a operações seguras da CPU. A área da memória onde os processos do usuário rodam é chamada de &lt;strong&gt;user space&lt;/strong&gt; (espaço do usuário). Se um processo do usuário falhar — por exemplo, se o navegador travar —, o estrago fica contido: o kernel limpa a bagunça e o resto do sistema continua funcionando.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Esse isolamento é o que permite que seu navegador trave sem que o cálculo científico que está rodando em segundo plano há três dias seja perdido. Cada processo está numa caixinha protegida, e essa caixinha só pode ser aberta pelo kernel.&lt;/p&gt;

&lt;p&gt;Em teoria, um processo do usuário não consegue causar danos sérios ao sistema. Na prática, depende: se ele tiver as permissões certas, pode sim, por exemplo, apagar dados de um disco. Existem salvaguardas para evitar esse tipo de problema, mas o ponto é que o conceito de "modo" cria uma barreira clara entre o que é seguro e o que é privilegiado.&lt;/p&gt;

&lt;p&gt;Há também uma curiosidade: o kernel do Linux pode rodar &lt;strong&gt;threads de kernel&lt;/strong&gt;, que se parecem com processos comuns, mas têm acesso ao espaço do kernel. Exemplos são &lt;code&gt;kthreadd&lt;/code&gt; e &lt;code&gt;kblockd&lt;/code&gt;. Se um dia você ver esses nomes na lista de processos, saiba que eles não são programas do usuário — são parte do próprio kernel.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Hardware: a importância da memória principal
&lt;/h2&gt;

&lt;p&gt;De todo o hardware presente em um computador, talvez nenhum seja tão fundamental quanto a &lt;strong&gt;memória principal&lt;/strong&gt; (a famosa RAM). Em sua forma mais bruta, ela não passa de uma enorme área de armazenamento cheia de zeros e uns. Cada espacinho que guarda um zero ou um um é chamado de &lt;strong&gt;bit&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Pode parecer pouco glamoroso, mas é nessa memória que tudo acontece: tanto o kernel em execução quanto os processos do usuário estão ali, na forma de gigantescas coleções de bits. Toda a entrada e saída de dispositivos periféricos (teclado, mouse, disco, rede) também passa pela memória, em forma de bits. A CPU, por sua vez, é essencialmente um operador da memória: ela lê instruções e dados da memória, processa, e escreve resultados de volta na memória.&lt;/p&gt;

&lt;p&gt;Esse é um ponto que muita gente nunca para para pensar: a CPU "não conhece" os programas como nós os conhecemos. Para ela, tudo o que existe são padrões de bits a serem lidos e manipulados na memória.&lt;/p&gt;

&lt;h3&gt;
  
  
  O conceito de estado
&lt;/h3&gt;

&lt;p&gt;Quando falamos sobre memória, processos ou kernel, um termo aparece com frequência: &lt;strong&gt;estado&lt;/strong&gt;. Tecnicamente, um estado é uma configuração específica de bits em um determinado momento. Por exemplo, se você tem quatro bits, as combinações &lt;code&gt;0110&lt;/code&gt;, &lt;code&gt;0001&lt;/code&gt; e &lt;code&gt;1011&lt;/code&gt; representam três estados diferentes.&lt;/p&gt;

&lt;p&gt;Acontece que um único processo pode envolver milhões de bits. Falar em estado em termos de bits, então, fica impraticável. Por isso usamos &lt;strong&gt;descrições abstratas&lt;/strong&gt;: em vez de listar bits, dizemos coisas como "o processo está esperando entrada do usuário" ou "o processo está na etapa 2 da inicialização".&lt;/p&gt;

&lt;p&gt;E há ainda outro termo que merece atenção: &lt;strong&gt;imagem&lt;/strong&gt;. Como geralmente nos referimos ao estado de forma abstrata, quando queremos falar do arranjo físico dos bits propriamente dito, usamos a palavra "imagem". Você ouvirá isso em contextos como "imagem de processo" ou "imagem do sistema".&lt;/p&gt;




&lt;h2&gt;
  
  
  4. O Kernel: o cérebro silencioso do sistema
&lt;/h2&gt;

&lt;p&gt;Se a memória é o palco onde tudo acontece, o kernel é o diretor que organiza a peça. Praticamente tudo o que o kernel faz gira em torno da memória principal: ele a divide em pedaços, distribui esses pedaços entre os processos, mantém registros sobre quem está com o quê, e garante que ninguém invada o espaço alheio.&lt;/p&gt;

&lt;p&gt;O kernel é responsável por quatro grandes áreas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Processos&lt;/strong&gt; — decide qual processo pode usar a CPU em cada momento.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memória&lt;/strong&gt; — controla a memória, sabendo o que está alocado para quem, o que pode ser compartilhado e o que está livre.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Drivers de dispositivos&lt;/strong&gt; — atua como interface entre os processos e o hardware (disco, placa de rede, etc.).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chamadas de sistema e suporte&lt;/strong&gt; — fornece os mecanismos pelos quais os processos se comunicam com o kernel para pedir serviços.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Vamos explorar cada uma dessas áreas.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.1 Gerenciamento de processos
&lt;/h3&gt;

&lt;p&gt;Gerenciar processos significa iniciá-los, pausá-los, retomá-los, agendá-los e finalizá-los. Iniciar e terminar processos é relativamente simples de entender. O que é mais sutil é como um processo realmente usa a CPU durante seu funcionamento.&lt;/p&gt;

&lt;p&gt;Em um sistema operacional moderno, é comum que muitos processos rodem "simultaneamente". Por exemplo, você pode ter um navegador, uma planilha, um editor de texto e um tocador de música todos abertos ao mesmo tempo. Mas há uma armadilha aqui: &lt;strong&gt;eles não rodam exatamente ao mesmo tempo&lt;/strong&gt;, pelo menos não em uma CPU com apenas um núcleo.&lt;/p&gt;

&lt;p&gt;Em um sistema com CPU de um único núcleo, vários processos podem estar prontos para usar a CPU, mas apenas um efetivamente usa o processador a cada instante. Na prática, cada processo usa a CPU por uma fração mínima de segundo, depois é pausado; outro processo então usa a CPU por outra fração; e assim por diante. Esse "trocar de turno" entre processos chama-se &lt;strong&gt;context switch&lt;/strong&gt; (troca de contexto).&lt;/p&gt;

&lt;p&gt;Cada pedaço de tempo que um processo recebe é chamado de &lt;strong&gt;time slice&lt;/strong&gt; (fatia de tempo). É um intervalo curto o suficiente para que humanos não percebam a alternância, mas longo o suficiente para que computações significativas possam ser feitas. Essa habilidade de fazer parecer que vários processos rodam ao mesmo tempo recebe o nome de &lt;strong&gt;multitarefa&lt;/strong&gt; (multitasking).&lt;/p&gt;

&lt;p&gt;Quem gerencia tudo isso é o kernel. Imagine que um processo está rodando em modo usuário e sua fatia de tempo se esgota. O que acontece, passo a passo, é o seguinte:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A CPU (o hardware em si) interrompe o processo atual com base em um temporizador interno, muda para o modo kernel e devolve o controle ao kernel.&lt;/li&gt;
&lt;li&gt;O kernel registra o estado atual da CPU e da memória — informações essenciais para retomar o processo interrompido mais tarde.&lt;/li&gt;
&lt;li&gt;O kernel executa quaisquer tarefas que tenham surgido durante a fatia de tempo anterior (por exemplo, coletar dados de operações de entrada e saída, ou I/O).&lt;/li&gt;
&lt;li&gt;O kernel está agora pronto para deixar outro processo rodar. Ele examina a lista de processos prontos para executar e escolhe um.&lt;/li&gt;
&lt;li&gt;O kernel prepara a memória para esse novo processo e configura a CPU.&lt;/li&gt;
&lt;li&gt;O kernel diz à CPU quanto tempo será a fatia desse novo processo.&lt;/li&gt;
&lt;li&gt;O kernel coloca a CPU de volta no modo usuário e entrega o controle ao processo escolhido.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Essa sequência responde a uma pergunta importante: &lt;strong&gt;quando o kernel realmente roda?&lt;/strong&gt; A resposta é: ele roda entre as fatias de tempo dos processos, durante essas trocas de contexto.&lt;/p&gt;

&lt;p&gt;No caso de máquinas com múltiplos núcleos — quase todas hoje em dia —, a situação fica um pouco mais complexa, porque o kernel não precisa abrir mão do seu CPU atual para que um processo rode em outro núcleo. Mais de um processo pode efetivamente rodar ao mesmo tempo. Ainda assim, para aproveitar ao máximo os núcleos disponíveis, o kernel costuma executar as mesmas etapas descritas acima.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.2 Gerenciamento de memória
&lt;/h3&gt;

&lt;p&gt;Durante uma troca de contexto, o kernel também precisa gerenciar a memória, e essa tarefa pode ser bem complicada. Algumas condições precisam ser garantidas o tempo todo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;O kernel precisa ter sua própria área privada na memória, inacessível aos processos do usuário.&lt;/li&gt;
&lt;li&gt;Cada processo do usuário precisa ter sua própria seção de memória.&lt;/li&gt;
&lt;li&gt;Um processo do usuário não pode acessar a memória privada de outro processo.&lt;/li&gt;
&lt;li&gt;Processos do usuário podem compartilhar memória entre si, quando isso for explicitamente necessário.&lt;/li&gt;
&lt;li&gt;Parte da memória de um processo pode ser somente leitura.&lt;/li&gt;
&lt;li&gt;O sistema pode usar mais memória do que existe fisicamente, recorrendo ao espaço em disco como auxiliar.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Felizmente, o kernel não faz isso sozinho. As CPUs modernas incluem um componente chamado &lt;strong&gt;MMU (Memory Management Unit)&lt;/strong&gt;, ou Unidade de Gerenciamento de Memória. É a MMU que viabiliza um esquema chamado &lt;strong&gt;memória virtual&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Na memória virtual, um processo não acessa a memória diretamente pelo endereço físico no hardware. Em vez disso, o kernel configura cada processo como se ele estivesse rodando sozinho em uma máquina inteira. Quando o processo tenta acessar um endereço de memória, a MMU intercepta esse acesso e usa um mapa de endereços para traduzir aquele "endereço do ponto de vista do processo" para o endereço físico real na máquina.&lt;/p&gt;

&lt;p&gt;O kernel é quem mantém esse mapa atualizado. Durante uma troca de contexto, por exemplo, ele precisa trocar o mapa do processo que estava rodando pelo mapa do processo que vai rodar agora. A implementação concreta desse mapa de endereços é chamada de &lt;strong&gt;tabela de páginas&lt;/strong&gt; (page table).&lt;/p&gt;

&lt;p&gt;Esse esquema dá a cada processo a ilusão de ter uma máquina inteira só para si, com seu espaço próprio de memória, sem que ele precise se preocupar com o que os outros processos estão fazendo. É uma das abstrações mais poderosas que existem em sistemas operacionais.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.3 Drivers de dispositivos e o gerenciamento de hardware
&lt;/h3&gt;

&lt;p&gt;O papel do kernel em relação aos dispositivos é, em essência, simples: ele é o porteiro. Um dispositivo geralmente só pode ser acessado em modo kernel, porque um acesso inadequado — como um processo pedindo para desligar a fonte de energia — poderia derrubar a máquina.&lt;/p&gt;

&lt;p&gt;A complicação real está em outro lugar: dispositivos diferentes raramente compartilham a mesma interface de programação, mesmo quando fazem essencialmente a mesma coisa. Duas placas de rede de fabricantes diferentes, por exemplo, podem ser totalmente distintas por dentro, com comandos próprios, registradores próprios e quirks próprios.&lt;/p&gt;

&lt;p&gt;Por causa disso, os &lt;strong&gt;drivers de dispositivos&lt;/strong&gt; tradicionalmente fazem parte do kernel. O papel deles é tornar a vida dos desenvolvedores de software mais fácil: eles "traduzem" as particularidades de cada hardware e expõem uma interface uniforme para os processos do usuário. Assim, o programador que escreve um software para ler um arquivo não precisa saber se o disco é SSD ou HDD, nem qual é o fabricante. Ele só precisa pedir ao kernel: "leia este arquivo". O kernel, via driver, faz o resto.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.4 Chamadas de sistema e suporte aos processos
&lt;/h3&gt;

&lt;p&gt;Existem várias outras funcionalidades que o kernel oferece aos processos do usuário. A mais essencial delas são as &lt;strong&gt;chamadas de sistema&lt;/strong&gt; (em inglês, &lt;em&gt;system calls&lt;/em&gt;, ou simplesmente &lt;em&gt;syscalls&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;Uma chamada de sistema é um mecanismo pelo qual um processo do usuário pede ao kernel que execute alguma tarefa que o próprio processo não conseguiria fazer sozinho, ou não conseguiria fazer com segurança. Abrir arquivos, ler arquivos, escrever em arquivos — tudo isso envolve chamadas de sistema. Sem elas, processos seriam ilhas isoladas, incapazes de interagir com qualquer coisa fora de si mesmos.&lt;/p&gt;

&lt;p&gt;Duas chamadas de sistema são particularmente importantes para entender como os processos começam a existir:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;fork()&lt;/strong&gt; — quando um processo chama &lt;code&gt;fork()&lt;/code&gt;, o kernel cria uma cópia quase idêntica desse processo. Há agora dois processos rodando, ambos com o mesmo código, a mesma memória, o mesmo estado.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;exec()&lt;/strong&gt; — quando um processo chama &lt;code&gt;exec(programa)&lt;/code&gt;, o kernel carrega e inicia o programa especificado, substituindo o processo atual. O processo antigo deixa de existir, e em seu lugar surge o novo programa, mas com o mesmo identificador.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Com exceção do &lt;code&gt;init&lt;/code&gt; (o primeiríssimo processo, que é iniciado pelo kernel durante o boot), &lt;strong&gt;todos os processos do usuário em um sistema Linux começam por meio de fork()&lt;/strong&gt;. E, na maioria das vezes, esse novo processo recém-clonado imediatamente chama &lt;code&gt;exec()&lt;/code&gt; para virar outro programa.&lt;/p&gt;

&lt;p&gt;Um exemplo simples ajuda a entender essa dança. Quando você digita &lt;code&gt;ls&lt;/code&gt; em um terminal e aperta Enter, o seguinte acontece:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;O shell (o programa do terminal) chama &lt;code&gt;fork()&lt;/code&gt; para criar uma cópia de si mesmo.&lt;/li&gt;
&lt;li&gt;Essa cópia, ainda idêntica ao shell original, chama &lt;code&gt;exec(ls)&lt;/code&gt; para se transformar no programa &lt;code&gt;ls&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;O &lt;code&gt;ls&lt;/code&gt; roda, lista os arquivos do diretório atual, e termina.&lt;/li&gt;
&lt;li&gt;O shell original, que esperou todo esse tempo, volta a aceitar comandos.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Pode parecer estranho criar uma cópia inteira só para substituí-la em seguida, mas esse padrão &lt;code&gt;fork() + exec()&lt;/code&gt; é elegante porque permite ao novo processo herdar todo o ambiente do pai (variáveis, arquivos abertos, permissões) antes de mudar de identidade.&lt;/p&gt;

&lt;p&gt;Vale uma nota sobre a notação: chamadas de sistema costumam ser escritas com parênteses, como &lt;code&gt;fork()&lt;/code&gt; e &lt;code&gt;exec()&lt;/code&gt;, derivando da forma como elas seriam escritas em código C. Você não precisa saber C para entender isso — basta lembrar que uma chamada de sistema é uma interação entre um processo e o kernel.&lt;/p&gt;

&lt;p&gt;Além das chamadas de sistema tradicionais, o kernel também oferece &lt;strong&gt;pseudodispositivos&lt;/strong&gt;. Eles aparecem para os processos como se fossem dispositivos comuns, mas, na verdade, são implementados puramente em software. Um exemplo clássico é o gerador de números aleatórios &lt;code&gt;/dev/random&lt;/code&gt;. Tecnicamente, ele não precisaria estar no kernel, mas costuma ficar por razões práticas, especialmente de segurança.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Espaço do usuário: onde a ação acontece
&lt;/h2&gt;

&lt;p&gt;Como já vimos, a memória principal que o kernel reserva para os processos do usuário é o que chamamos de &lt;strong&gt;user space&lt;/strong&gt; (ou espaço do usuário). Como um processo nada mais é do que um estado na memória, o espaço do usuário acaba sendo, na prática, a memória ocupada por todos os processos rodando ao mesmo tempo. Você também pode encontrar o termo informal &lt;strong&gt;userland&lt;/strong&gt; para se referir à mesma coisa, e às vezes ele é usado para se referir aos próprios programas que rodam ali.&lt;/p&gt;

&lt;p&gt;E aqui vai uma constatação importante: &lt;strong&gt;a maior parte da ação em um sistema Linux acontece no espaço do usuário&lt;/strong&gt;. O kernel é fundamental, mas é um trabalhador silencioso, quase invisível. Tudo o que você vê — a interface gráfica, o navegador, o editor de texto, o servidor que serve suas páginas, o banco de dados que guarda seus posts — é processo do usuário.&lt;/p&gt;

&lt;p&gt;Embora, do ponto de vista do kernel, todos os processos sejam essencialmente iguais, eles desempenham papéis muito diferentes. Por isso, costuma-se organizar mentalmente o espaço do usuário em &lt;strong&gt;camadas de serviço&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Camada inferior&lt;/strong&gt; (mais próxima do kernel) — componentes pequenos, com tarefas simples e bem definidas. Por exemplo: serviços de configuração de rede, barramento de comunicação entre processos, logging de diagnóstico.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Camada intermediária&lt;/strong&gt; — componentes maiores, que cuidam de serviços como e-mail, impressão, cache de DNS, banco de dados.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Camada superior&lt;/strong&gt; — aplicações com as quais o usuário interage diretamente. A interface gráfica, o navegador web, os editores e similares.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Geralmente, quando um componente quer usar outro, esse "outro" está na mesma camada ou em uma camada abaixo. Mas vale dizer que essa hierarquia é uma aproximação. Não existem regras rígidas no espaço do usuário. Servidores web, por exemplo, podem ser considerados aplicações de altíssimo nível (porque suas tarefas são complexas), mas também podem ser vistos como serviços intermediários (porque outras aplicações dependem deles). Tudo depende do ponto de vista.&lt;/p&gt;

&lt;p&gt;Outro detalhe que merece atenção: a maioria dos programas escreve mensagens de diagnóstico, conhecidas como &lt;strong&gt;logs&lt;/strong&gt;. O padrão é usar o serviço &lt;code&gt;syslog&lt;/code&gt; para isso, mas alguns programas preferem fazer seu próprio logging. É um exemplo claro de como o espaço do usuário é flexível, sem regras absolutas.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Usuários: identidades e fronteiras
&lt;/h2&gt;

&lt;p&gt;Até aqui, falamos bastante sobre processos, mas pouco sobre &lt;strong&gt;quem é dono deles&lt;/strong&gt;. É hora de entrar no conceito de usuário.&lt;/p&gt;

&lt;p&gt;O kernel do Linux herdou do Unix a noção tradicional de usuário. Um usuário é uma entidade que pode rodar processos e ser dona de arquivos. Um usuário normalmente está associado a um nome — por exemplo, um sistema pode ter um usuário chamado &lt;code&gt;joaoz&lt;/code&gt; ou &lt;code&gt;maria&lt;/code&gt;. Mas, internamente, o kernel não trabalha com nomes. Ele identifica os usuários por números, chamados &lt;strong&gt;IDs de usuário&lt;/strong&gt; (user IDs, ou UIDs).&lt;/p&gt;

&lt;p&gt;Por que existem usuários? Por duas razões principais: &lt;strong&gt;permissões&lt;/strong&gt; e &lt;strong&gt;fronteiras&lt;/strong&gt;. Todo processo do espaço do usuário tem um "dono", e dizemos que aquele processo "roda como" aquele usuário. Esse dono pode encerrar ou modificar o comportamento dos seus próprios processos (dentro de certos limites), mas não pode interferir nos processos de outros usuários. Da mesma forma, usuários podem ser donos de arquivos e decidir se compartilham ou não esses arquivos com outros usuários.&lt;/p&gt;

&lt;p&gt;Um sistema Linux normal tem vários usuários além das pessoas reais que efetivamente usam a máquina. Existem usuários de sistema, criados para representar serviços específicos. E há um usuário que merece destaque especial: o &lt;strong&gt;root&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;O usuário &lt;strong&gt;root&lt;/strong&gt; é a grande exceção das regras de isolamento. Ele pode encerrar e alterar processos de qualquer outro usuário, e pode acessar qualquer arquivo do sistema local. Por isso, ele é conhecido como &lt;strong&gt;superusuário&lt;/strong&gt;. Uma pessoa que pode operar como root é considerada um administrador do sistema na tradição Unix.&lt;/p&gt;

&lt;p&gt;Mas atenção: ser root é poderoso e perigoso. O sistema simplesmente confia em tudo o que você manda fazer, mesmo que isso seja claramente destrutivo. Não há rede de proteção. Por isso, os designers de sistemas se esforçam constantemente para tornar o acesso root o menos necessário possível. Trocar de rede sem fio em um notebook, por exemplo, antes exigia ser root, mas hoje em dia é uma operação comum disponível para qualquer usuário comum. Vale ressaltar também que, embora o root seja poderosíssimo, ele ainda roda em &lt;strong&gt;modo usuário&lt;/strong&gt; da CPU, e não em modo kernel.&lt;/p&gt;

&lt;p&gt;Existem também os &lt;strong&gt;grupos&lt;/strong&gt;, que são conjuntos de usuários. O propósito principal dos grupos é permitir que um usuário compartilhe arquivos com vários membros ao mesmo tempo, sem precisar abrir o arquivo para todo o sistema.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Olhando para frente: o que vem agora?
&lt;/h2&gt;

&lt;p&gt;Chegamos ao final do passeio panorâmico pelos fundamentos do Linux. Vamos recapitular rapidamente o que vimos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Abstração&lt;/strong&gt; é a ferramenta mental que permite entender sistemas complexos, ignorando detalhes irrelevantes e focando em propósitos e relações.&lt;/li&gt;
&lt;li&gt;O Linux organiza seus componentes em &lt;strong&gt;três grandes níveis&lt;/strong&gt;: hardware na base, kernel no meio, processos do usuário no topo.&lt;/li&gt;
&lt;li&gt;O &lt;strong&gt;hardware&lt;/strong&gt; — especialmente a memória principal — é o palco onde tudo se desenrola. CPUs leem e escrevem memória; processos e kernel são, no fundo, padrões de bits.&lt;/li&gt;
&lt;li&gt;O &lt;strong&gt;kernel&lt;/strong&gt; é o gerente silencioso: ele cuida de processos (multitarefa, troca de contexto, fatias de tempo), gerencia memória (com a ajuda da MMU e da memória virtual), opera dispositivos (via drivers) e atende às chamadas de sistema dos processos.&lt;/li&gt;
&lt;li&gt;O &lt;strong&gt;espaço do usuário&lt;/strong&gt; é onde a ação visível acontece. Os processos do usuário se organizam em camadas — básica, intermediária e de aplicação — mas sem regras rígidas.&lt;/li&gt;
&lt;li&gt;Os &lt;strong&gt;usuários&lt;/strong&gt; existem para garantir permissões e fronteiras. O &lt;strong&gt;root&lt;/strong&gt; é o superusuário com poderes especiais.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Essa visão geral é fundamental, mas ela não substitui a prática. Não é possível aprender os detalhes de um sistema Linux apenas lendo sobre ele — é preciso colocar a mão na massa. O próximo passo natural nessa jornada seria começar a explorar os fundamentos do espaço do usuário direto no terminal, e abordar um tema que ficou de fora deste texto: o &lt;strong&gt;armazenamento de longo prazo&lt;/strong&gt; (discos, arquivos, sistemas de arquivos). Afinal, é preciso guardar programas e dados em algum lugar.&lt;/p&gt;

&lt;p&gt;Se você está começando a estudar Linux agora, talvez tenha achado tudo isso um pouco abstrato — e é exatamente esse o ponto. A primeira leitura serve para você ter um mapa mental do território. Quando começar a estudar comandos no terminal, processos, memória, redes ou qualquer outro tópico, vai conseguir encaixar cada novo conceito no lugar certo da pilha de abstrações: "ah, isso é uma chamada de sistema", "isso aqui é coisa de driver, então mora no kernel", "isso é só um processo do usuário fazendo o que sabe fazer".&lt;/p&gt;

&lt;p&gt;Esse mapa é o que separa quem usa Linux de quem &lt;strong&gt;entende&lt;/strong&gt; o Linux. E entender o Linux é entender, em grande medida, como sistemas operacionais modernos funcionam em geral — porque os mesmos conceitos de modos de execução, processos, memória virtual e chamadas de sistema aparecem, com pequenas variações, no Windows, no macOS, no FreeBSD, no Android e em praticamente qualquer outro sistema operacional sério que exista hoje.&lt;/p&gt;

&lt;p&gt;Então, da próxima vez que você abrir o terminal e digitar um comando, lembre: por trás dessa simples linha de texto, um shell vai fazer um &lt;code&gt;fork()&lt;/code&gt;, criar uma cópia de si mesmo, transformar essa cópia em outro programa via &lt;code&gt;exec()&lt;/code&gt;, conversar com o kernel via chamadas de sistema, escrever em pseudodispositivos como &lt;code&gt;/dev/tty&lt;/code&gt;, esperar troca de contexto, lidar com memória virtual mapeada pela MMU, e voltar a você com o resultado. Tudo isso em milissegundos. E agora você sabe o nome de quase todas as engrenagens envolvidas.&lt;/p&gt;

&lt;p&gt;Bem-vindo ao Linux. A viagem está só começando.&lt;/p&gt;

</description>
      <category>linux</category>
      <category>systems</category>
      <category>kernel</category>
      <category>shell</category>
    </item>
    <item>
      <title>Dominando o Express: partials, sessões, CRUD REST, filtros e páginas de erro</title>
      <dc:creator>Moprius</dc:creator>
      <pubDate>Fri, 15 May 2026 02:47:55 +0000</pubDate>
      <link>https://forem.com/moprius/dominando-o-express-partials-sessoes-crud-rest-filtros-e-paginas-de-erro-32ok</link>
      <guid>https://forem.com/moprius/dominando-o-express-partials-sessoes-crud-rest-filtros-e-paginas-de-erro-32ok</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foyb6dm4t6s6x5i2y5043.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foyb6dm4t6s6x5i2y5043.png" alt="Javascript" width="800" height="497"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Você já tem um projeto Express organizado em MVC, com sua rota inicial funcionando e uma view de login renderizando direitinho. Mas uma aplicação web de verdade exige muito mais: reaproveitamento de pedaços de HTML, controle de sessão para login e logout, um CRUD completo seguindo o padrão REST, filtros para impedir acesso indevido a áreas restritas e, claro, páginas de erro amigáveis em vez daqueles stack traces feios que assustam o usuário.&lt;/p&gt;

&lt;p&gt;Neste tutorial, vamos juntar todas essas peças. Ao final você terá uma aplicação Express completa: usuários se autenticando, criando contatos, editando, excluindo, sendo barrados quando não logam, e vendo páginas customizadas quando algo dá errado. O projeto continua sendo o Ntalk, uma agenda de contatos que vai virar também um chat em tempo real lá na frente.&lt;/p&gt;

&lt;p&gt;Bora dominar o Express?&lt;/p&gt;

&lt;h2&gt;
  
  
  Estruturando views
&lt;/h2&gt;

&lt;p&gt;Antes de mergulhar em sessões e rotas REST, vale uma melhoria simples mas que faz diferença gigante na manutenção do código: a separação das views em &lt;strong&gt;partials&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;O template engine EJS tem várias funcionalidades para programar conteúdo dinâmico dentro do HTML. Não vamos esgotar o assunto, mas vamos usar os recursos principais para renderizar dados dinâmicos e, principalmente, evitar repetição. A ideia dos partials é simples: pedaços de HTML que se repetem em várias telas (cabeçalhos, rodapés, menus laterais) ficam em arquivos próprios e são incluídos sob demanda.&lt;/p&gt;

&lt;p&gt;Vamos criar dois partials que serão reaproveitados em quase todas as páginas. O primeiro é o cabeçalho. Dentro de &lt;code&gt;views&lt;/code&gt;, crie o arquivo &lt;code&gt;header.ejs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Ntalk - Agenda de contatos&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E agora o rodapé. Crie o arquivo &lt;code&gt;footer.ejs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;footer&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;small&amp;gt;&lt;/span&gt;Ntalk - Agenda de contatos&lt;span class="nt"&gt;&amp;lt;/small&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/footer&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora podemos enxugar a homepage. Modifique a &lt;code&gt;views/home/index.ejs&lt;/code&gt; para usar os partials através da diretiva &lt;code&gt;include&lt;/code&gt; do EJS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;include&lt;/span&gt; &lt;span class="err"&gt;../&lt;/span&gt;&lt;span class="na"&gt;header&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;header&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Ntalk&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h4&amp;gt;&lt;/span&gt;Bem-vindo!&lt;span class="nt"&gt;&amp;lt;/h4&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;section&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"/entrar"&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"post"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"usuario[nome]"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Digite o nome"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;br&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"usuario[email]"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Digite o e-mail"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;br&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Entrar&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;include&lt;/span&gt; &lt;span class="err"&gt;../&lt;/span&gt;&lt;span class="na"&gt;footer&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A homepage agora está muito mais enxuta. O cabeçalho e o rodapé não estão mais duplicados em todas as views: estão em um único lugar. Se você precisar mudar o título do site ou adicionar uma tag &lt;code&gt;&amp;lt;meta&amp;gt;&lt;/code&gt; nova, edita um arquivo só, e a mudança se reflete em toda a aplicação. Esse tipo de organização vai te poupar muito tempo no futuro.&lt;/p&gt;

&lt;p&gt;Outro detalhe importante: repare que os campos do formulário usam &lt;code&gt;name="usuario[nome]"&lt;/code&gt; e &lt;code&gt;name="usuario[email]"&lt;/code&gt;. Essa sintaxe de colchetes faz com que o body parser do Express transforme automaticamente os dados em um objeto aninhado, ou seja, no servidor você vai receber &lt;code&gt;req.body.usuario.nome&lt;/code&gt; e &lt;code&gt;req.body.usuario.email&lt;/code&gt;. É uma forma elegante de manter dados relacionados agrupados.&lt;/p&gt;

&lt;h2&gt;
  
  
  Controlando as sessões de usuários
&lt;/h2&gt;

&lt;p&gt;Para o sistema fazer login e logout, precisamos de &lt;strong&gt;controle de sessão&lt;/strong&gt;. A sessão é uma área de memória no servidor que mantém dados específicos de cada usuário entre uma requisição e outra, identificada por um cookie no navegador. Trabalhar com sessão em Express é muito simples: os dados são manipulados através de um objeto JSON acessível em &lt;code&gt;req.session&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Primeiro, vamos adicionar duas novas rotas em &lt;code&gt;routes/home.js&lt;/code&gt;, uma &lt;code&gt;POST /entrar&lt;/code&gt; para receber o formulário de login e uma &lt;code&gt;GET /sair&lt;/code&gt; para destruir a sessão:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;home&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;controllers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;home&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;home&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/entrar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;home&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/sair&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;home&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logout&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora implemente as actions correspondentes no &lt;code&gt;controllers/home.js&lt;/code&gt;. Na action &lt;code&gt;login&lt;/code&gt;, vamos validar de forma simples se os campos &lt;code&gt;nome&lt;/code&gt; e &lt;code&gt;email&lt;/code&gt; foram preenchidos. Se sim, gravamos os dados na sessão e criamos um array vazio de contatos (que será usado mais tarde). Em seguida, redirecionamos para &lt;code&gt;/contatos&lt;/code&gt;. Na action &lt;code&gt;logout&lt;/code&gt;, chamamos &lt;code&gt;req.session.destroy()&lt;/code&gt; para limpar tudo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;HomeController&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;home/index&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;login&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;usuario&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nx"&gt;nome&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;usuario&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nome&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;nome&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;usuario&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;usuario&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="nx"&gt;usuario&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;contatos&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
                &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;usuario&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;usuario&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/contatos&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;logout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;destroy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;HomeController&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reinicie o servidor (CTRL+C para parar e &lt;code&gt;node app.js&lt;/code&gt; para subir de novo) e tente fazer login no sistema. Surpresa: vai dar erro. E é justamente nesse erro que vamos aprender uma das peças mais importantes do Express.&lt;/p&gt;

&lt;h3&gt;
  
  
  Faltou habilitar o body parser e a sessão na stack
&lt;/h3&gt;

&lt;p&gt;O erro acontece porque o Express, por padrão, não decodifica automaticamente os corpos de requisição vindos de formulários HTML. Ele também não tem sessão habilitada por padrão. Precisamos adicionar esses itens à stack de middlewares.&lt;/p&gt;

&lt;p&gt;Atualize a configuração da stack no &lt;code&gt;app.js&lt;/code&gt; para ficar assim, &lt;strong&gt;na ordem correta&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;views&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;__dirname&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/views&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;view engine&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ejs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cookieParser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ntalk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;session&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;urlencoded&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;static&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/public&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cada peça nova tem um papel específico:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;express.cookieParser('ntalk')&lt;/code&gt;&lt;/strong&gt; — precisa vir antes do middleware de sessão, porque o &lt;code&gt;session()&lt;/code&gt; usa o cookie parser para codificar e decodificar o SessionID, que é justamente o identificador persistido no cookie do navegador. A string &lt;code&gt;'ntalk'&lt;/code&gt; é a chave secreta usada para assinar os cookies, dificultando que alguém os forje.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;express.session()&lt;/code&gt;&lt;/strong&gt; — habilita a sessão. A partir daí, todo &lt;code&gt;req&lt;/code&gt; ganha o atributo &lt;code&gt;req.session&lt;/code&gt; que você manipula livremente.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;express.json()&lt;/code&gt;&lt;/strong&gt; e &lt;strong&gt;&lt;code&gt;express.urlencoded()&lt;/code&gt;&lt;/strong&gt; — são os parsers de corpo. Eles transformam o conteúdo bruto das requisições POST em objetos JavaScript prontos para uso em &lt;code&gt;req.body&lt;/code&gt;. O primeiro lida com requisições com &lt;code&gt;Content-Type: application/json&lt;/code&gt; (típico de APIs), e o segundo com formulários HTML padrão. É graças a eles que aquela sintaxe &lt;code&gt;name="usuario[nome]"&lt;/code&gt; vira &lt;code&gt;req.body.usuario.nome&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cuidados ao trabalhar com sessões
&lt;/h3&gt;

&lt;p&gt;Tudo que você atribuir a &lt;code&gt;req.session&lt;/code&gt; vira um atributo persistido no objeto da sessão daquele usuário. Por exemplo, &lt;code&gt;req.session.mensagem = "Olá"&lt;/code&gt; cria a propriedade &lt;code&gt;mensagem&lt;/code&gt; e ela vai estar disponível em requisições futuras do mesmo usuário.&lt;/p&gt;

&lt;p&gt;Mas atenção: &lt;strong&gt;cuidado para não sobrescrever as funções nativas da sessão&lt;/strong&gt;. Nomes como &lt;code&gt;req.session.destroy&lt;/code&gt; ou &lt;code&gt;req.session.regenerate&lt;/code&gt; são funções do próprio framework. Se você atribuir um valor a &lt;code&gt;req.session.destroy&lt;/code&gt;, vai apagar a função e perder a capacidade de destruir aquela sessão. Isso é fonte de bugs inesperados difíceis de rastrear. Escolha nomes que não colidam com a API.&lt;/p&gt;

&lt;h3&gt;
  
  
  Criando a rota /contatos
&lt;/h3&gt;

&lt;p&gt;Antes de testar tudo, precisamos criar a rota &lt;code&gt;/contatos&lt;/code&gt; para onde o login está redirecionando. Vamos criar um controller, uma rota e uma view para essa área.&lt;/p&gt;

&lt;p&gt;Crie o diretório &lt;code&gt;views/contatos&lt;/code&gt; e dentro dele o arquivo &lt;code&gt;index.ejs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;include&lt;/span&gt; &lt;span class="err"&gt;../&lt;/span&gt;&lt;span class="na"&gt;header&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;header&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Ntalk - Agenda de contatos&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;section&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Bem-vindo &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="na"&gt;-&lt;/span&gt; &lt;span class="na"&gt;usuario.nome&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;include&lt;/span&gt; &lt;span class="err"&gt;../&lt;/span&gt;&lt;span class="na"&gt;exit&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;include&lt;/span&gt; &lt;span class="err"&gt;../&lt;/span&gt;&lt;span class="na"&gt;footer&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note duas coisas: estamos exibindo o nome do usuário logado com &lt;code&gt;&amp;lt;%- usuario.nome %&amp;gt;&lt;/code&gt;, e estamos incluindo um terceiro partial chamado &lt;code&gt;exit&lt;/code&gt;. Esse partial vai conter o link de logout, reaproveitado por toda a área autenticada. Crie &lt;code&gt;views/exit.ejs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;section&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;'/sair'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Sair&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora crie o controller &lt;code&gt;controllers/contatos.js&lt;/code&gt;, com uma única action por enquanto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;ContatoController&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;usuario&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;usuario&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;usuario&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;usuario&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
            &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;contatos/index&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;ContatoController&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E o arquivo de rotas &lt;code&gt;routes/contatos.js&lt;/code&gt; (por convenção, o nome bate com o controller):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;contatos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;controllers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contatos&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/contatos&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;contatos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reinicie o servidor e tente novamente o login. Agora deve funcionar: você é redirecionado para a página de contatos, vê seu nome no topo e tem um link para sair. Parabéns, você acabou de implementar autenticação e sessão.&lt;/p&gt;

&lt;h2&gt;
  
  
  Criando rotas no padrão REST
&lt;/h2&gt;

&lt;p&gt;A agenda de contatos precisa do &lt;strong&gt;CRUD&lt;/strong&gt; clássico: criar, listar, atualizar e excluir contatos. A forma profissional de expor esse CRUD é via REST, que mapeia cada operação para um verbo HTTP específico:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;GET /contatos&lt;/code&gt; — lista todos os contatos&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /contato/:id&lt;/code&gt; — mostra os detalhes de um contato&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;POST /contato&lt;/code&gt; — cria um novo contato&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /contato/:id/editar&lt;/code&gt; — exibe o formulário de edição&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PUT /contato/:id&lt;/code&gt; — atualiza um contato existente&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DELETE /contato/:id&lt;/code&gt; — exclui um contato&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Para que tudo isso funcione, precisamos adicionar dois itens novos na stack:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;views&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;__dirname&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/views&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;view engine&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ejs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cookieParser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ntalk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;session&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;urlencoded&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;methodOverride&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;static&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/public&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O &lt;strong&gt;&lt;code&gt;express.methodOverride()&lt;/code&gt;&lt;/strong&gt; permite simular os verbos PUT e DELETE em formulários HTML (já já explico o porquê disso). O &lt;strong&gt;&lt;code&gt;app.router&lt;/code&gt;&lt;/strong&gt; é o middleware que gerencia o roteamento da aplicação. Adicioná-lo explicitamente à stack nos dá controle sobre quando o roteamento acontece em relação aos outros middlewares, o que será crucial para a parte de páginas de erro mais à frente.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementando as rotas REST
&lt;/h3&gt;

&lt;p&gt;Atualize &lt;code&gt;routes/contatos.js&lt;/code&gt; para contemplar todas as rotas do CRUD:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;contatos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;controllers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contatos&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/contatos&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;contatos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/contato/:id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;contatos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;show&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/contato&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;contatos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/contato/:id/editar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;contatos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;edit&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/contato/:id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;contatos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;update&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;del&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/contato/:id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;contatos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;destroy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Repare em &lt;code&gt;:id&lt;/code&gt; — esses são &lt;strong&gt;parâmetros de rota&lt;/strong&gt;. Qualquer valor que aparecer naquela posição da URL será capturado e disponibilizado em &lt;code&gt;req.params.id&lt;/code&gt;. Por exemplo, ao acessar &lt;code&gt;/contato/3&lt;/code&gt;, dentro da action o &lt;code&gt;req.params.id&lt;/code&gt; vai valer &lt;code&gt;"3"&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementando as actions
&lt;/h3&gt;

&lt;p&gt;Por enquanto, em vez de um banco de dados, vamos persistir os contatos na &lt;strong&gt;própria sessão do usuário&lt;/strong&gt;. Isso não é uma boa ideia para produção (a sessão é volátil e tem tamanho limitado), mas é perfeito para entender o fluxo do framework sem se preocupar com infraestrutura. Substitua o conteúdo de &lt;code&gt;controllers/contatos.js&lt;/code&gt; pelo seguinte:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;ContatoController&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;usuario&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;usuario&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nx"&gt;contatos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;usuario&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contatos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;usuario&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;usuario&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;contatos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;contatos&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
            &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;contatos/index&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;contato&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contato&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nx"&gt;usuario&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;usuario&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nx"&gt;usuario&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contatos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;contato&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/contatos&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;show&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nx"&gt;contato&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;usuario&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contatos&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;contato&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;contato&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
            &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;contatos/show&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;edit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nx"&gt;usuario&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;usuario&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nx"&gt;contato&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;usuario&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contatos&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;usuario&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;usuario&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;contato&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;contato&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
            &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;contatos/edit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;update&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;contato&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contato&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nx"&gt;usuario&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;usuario&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nx"&gt;usuario&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contatos&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;contato&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/contatos&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;destroy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;usuario&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;usuario&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nx"&gt;usuario&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contatos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;splice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/contatos&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;ContatoController&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cada action faz algo bem específico: o &lt;code&gt;index&lt;/code&gt; lista, o &lt;code&gt;create&lt;/code&gt; adiciona ao array, o &lt;code&gt;show&lt;/code&gt; exibe os detalhes de um item, o &lt;code&gt;edit&lt;/code&gt; carrega o formulário com os dados atuais, o &lt;code&gt;update&lt;/code&gt; aplica as alterações e o &lt;code&gt;destroy&lt;/code&gt; remove pela posição no array usando &lt;code&gt;splice&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Criando as views do CRUD
&lt;/h3&gt;

&lt;p&gt;Atualize &lt;code&gt;views/contatos/index.ejs&lt;/code&gt; para listar contatos e ter o formulário de cadastro:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;include&lt;/span&gt; &lt;span class="err"&gt;../&lt;/span&gt;&lt;span class="na"&gt;header&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;header&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Ntalk - Agenda de contatos&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;section&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"/contato"&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"post"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"contato[nome]"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Nome"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"contato[email]"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"E-mail"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Cadastrar&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;table&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;thead&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Nome&lt;span class="nt"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;th&amp;gt;&lt;/span&gt;E-mail&lt;span class="nt"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Ação&lt;span class="nt"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/thead&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;tbody&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;contatos.forEach&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="na"&gt;function&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="na"&gt;contato&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="na"&gt;-&lt;/span&gt; &lt;span class="na"&gt;contato.nome&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="na"&gt;-&lt;/span&gt; &lt;span class="na"&gt;contato.email&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/contato/&amp;lt;%- index %&amp;gt;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Detalhes&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="err"&gt;})&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/tbody&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/table&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;include&lt;/span&gt; &lt;span class="err"&gt;../&lt;/span&gt;&lt;span class="na"&gt;exit&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;include&lt;/span&gt; &lt;span class="err"&gt;../&lt;/span&gt;&lt;span class="na"&gt;footer&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora a view de edição, &lt;code&gt;views/contatos/edit.ejs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;include&lt;/span&gt; &lt;span class="err"&gt;../&lt;/span&gt;&lt;span class="na"&gt;header&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;header&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Ntalk - Editar contato&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;section&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"/contato/&amp;lt;%- id %&amp;gt;"&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"post"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"hidden"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"_method"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"put"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;label&amp;gt;&lt;/span&gt;Nome:&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"contato[nome]"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"&amp;lt;%- contato.nome %&amp;gt;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;label&amp;gt;&lt;/span&gt;E-mail:&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"contato[email]"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"&amp;lt;%- contato.email %&amp;gt;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Atualizar&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;include&lt;/span&gt; &lt;span class="err"&gt;../&lt;/span&gt;&lt;span class="na"&gt;exit&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;include&lt;/span&gt; &lt;span class="err"&gt;../&lt;/span&gt;&lt;span class="na"&gt;footer&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E por último a view de detalhes, &lt;code&gt;views/contatos/show.ejs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;include&lt;/span&gt; &lt;span class="err"&gt;../&lt;/span&gt;&lt;span class="na"&gt;header&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;header&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Ntalk - Dados do contato&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;section&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"/contato/&amp;lt;%- id %&amp;gt;"&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"post"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"hidden"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"_method"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"delete"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&amp;lt;label&amp;gt;&lt;/span&gt;Nome:&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="na"&gt;-&lt;/span&gt; &lt;span class="na"&gt;contato.nome&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&amp;lt;label&amp;gt;&lt;/span&gt;E-mail:&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="na"&gt;-&lt;/span&gt; &lt;span class="na"&gt;contato.email&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Excluir&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/contato/&amp;lt;%- id %&amp;gt;/editar"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Editar&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;include&lt;/span&gt; &lt;span class="err"&gt;../&lt;/span&gt;&lt;span class="na"&gt;exit&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;include&lt;/span&gt; &lt;span class="err"&gt;../&lt;/span&gt;&lt;span class="na"&gt;footer&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  O truque dos verbos PUT e DELETE em HTML
&lt;/h3&gt;

&lt;p&gt;Você deve ter notado essas linhas estranhas:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"hidden"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"_method"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"put"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"hidden"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"_method"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"delete"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esse é um detalhe importante e que pega muita gente de surpresa. A especificação atual do HTML não permite definir &lt;code&gt;method="put"&lt;/code&gt; ou &lt;code&gt;method="delete"&lt;/code&gt; na tag &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt;. Os únicos métodos suportados nativamente são &lt;code&gt;GET&lt;/code&gt; e &lt;code&gt;POST&lt;/code&gt;. Para contornar isso, existe uma convenção que praticamente todos os frameworks web adotam: a tag &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; envia como &lt;code&gt;POST&lt;/code&gt;, e um campo oculto chamado &lt;code&gt;_method&lt;/code&gt; indica o verbo HTTP real que deveria ser usado.&lt;/p&gt;

&lt;p&gt;O middleware &lt;code&gt;express.methodOverride()&lt;/code&gt; é exatamente quem faz essa mágica. Ele intercepta a requisição, lê o valor de &lt;code&gt;_method&lt;/code&gt; no corpo, e reescreve o verbo HTTP antes de chegar ao roteador. É por isso que tivemos que adicioná-lo à stack: sem ele, o servidor ignoraria o &lt;code&gt;_method&lt;/code&gt; e trataria tudo como &lt;code&gt;POST&lt;/code&gt;, jamais chegando nas actions &lt;code&gt;update&lt;/code&gt; e &lt;code&gt;destroy&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Reinicie o servidor e teste o CRUD completo. Cadastre alguns contatos, abra os detalhes, edite, exclua. Tudo deve funcionar, e os dados ficam persistidos enquanto a sessão durar (ou seja, até você fazer logout ou reiniciar o servidor).&lt;/p&gt;

&lt;h2&gt;
  
  
  Aplicando filtros antes de acessar as rotas
&lt;/h2&gt;

&lt;p&gt;Já notou que se você acessar &lt;code&gt;/contatos&lt;/code&gt; sem fazer login, a aplicação dá um erro feio? É fácil reproduzir: faça logout e tente abrir &lt;code&gt;/contatos&lt;/code&gt; diretamente no navegador. Você vê algo como "Cannot read property 'usuario' of undefined".&lt;/p&gt;

&lt;p&gt;A causa é simples. O controller tenta acessar &lt;code&gt;req.session.usuario&lt;/code&gt;, mas, sem login, esse objeto não existe na sessão. JavaScript tenta ler a propriedade &lt;code&gt;contatos&lt;/code&gt; de &lt;code&gt;undefined&lt;/code&gt; e lança a exceção.&lt;/p&gt;

&lt;p&gt;Para resolver isso, precisamos de um &lt;strong&gt;filtro de autenticação&lt;/strong&gt; que rode antes da action propriamente dita. Frameworks de outras linguagens normalmente oferecem hooks explícitos chamados &lt;code&gt;before&lt;/code&gt; ou &lt;code&gt;after&lt;/code&gt;. O Express não tem isso de forma tão direta. Mas, graças aos callbacks de JavaScript, o próprio mecanismo de roteamento do Express suporta &lt;strong&gt;callbacks encadeados&lt;/strong&gt; em uma rota. Resumindo: depois do path, você pode passar quantos callbacks quiser, e eles são executados em ordem, um após o outro.&lt;/p&gt;

&lt;p&gt;Por exemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;callback1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;callback2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;callback3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esse é o mecanismo que vamos usar para criar nossos filtros.&lt;/p&gt;

&lt;h3&gt;
  
  
  Criando o middleware de autenticação
&lt;/h3&gt;

&lt;p&gt;Na raiz do projeto, crie a pasta &lt;code&gt;middleware&lt;/code&gt; e dentro dela o arquivo &lt;code&gt;autenticador.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;usuario&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A lógica é direta: se não existe usuário na sessão, redireciona para a página inicial. Se existe, chama &lt;code&gt;next()&lt;/code&gt; para passar para o próximo callback da cadeia (que será a action propriamente dita).&lt;/p&gt;

&lt;p&gt;A função &lt;code&gt;next&lt;/code&gt; é a peça-chave de qualquer middleware Express. Chamá-la significa "terminei meu trabalho, passe a bola adiante". Se você esquecer de chamar &lt;code&gt;next()&lt;/code&gt; e não enviar uma resposta, a requisição fica travada para sempre, esperando algo que nunca vem.&lt;/p&gt;

&lt;h3&gt;
  
  
  Encaixando o filtro nas rotas
&lt;/h3&gt;

&lt;p&gt;Agora vamos injetar esse middleware em todas as rotas que exigem autenticação. Modifique &lt;code&gt;routes/contatos.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;autenticar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./../middleware/autenticador&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nx"&gt;contatos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;controllers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contatos&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/contatos&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;autenticar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;contatos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/contato/:id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;autenticar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;contatos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;show&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/contato&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;autenticar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;contatos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/contato/:id/editar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;autenticar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;contatos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;edit&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/contato/:id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;autenticar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;contatos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;update&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;del&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/contato/:id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;autenticar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;contatos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;destroy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pronto. Cada rota agora executa primeiro &lt;code&gt;autenticar&lt;/code&gt; e só chega na action de contatos se o usuário estiver autenticado. Caso contrário, é redirecionado para a tela de login.&lt;/p&gt;

&lt;p&gt;Esse é o padrão para emular um filtro &lt;code&gt;before&lt;/code&gt;. Se você quiser um filtro &lt;code&gt;after&lt;/code&gt;, basta colocar o callback do filtro &lt;strong&gt;depois&lt;/strong&gt; do callback principal da rota. A ordem dos callbacks define a ordem de execução, simples assim.&lt;/p&gt;

&lt;p&gt;Esse mesmo mecanismo pode ser usado para muito mais do que autenticação: logging por rota, verificação de permissões específicas, validação de dados, rate limiting. Cada filtro fica em seu próprio arquivo, com responsabilidade única, e você compõe-os onde precisar.&lt;/p&gt;

&lt;h2&gt;
  
  
  Indo além: criando páginas de erros amigáveis
&lt;/h2&gt;

&lt;p&gt;Por padrão, quando uma rota não existe ou quando acontece um erro não tratado, o Express devolve uma página técnica feia, com stack trace, status numérico e nenhum carinho com o usuário. Isso é péssimo para qualquer aplicação em produção. A solução é interceptar esses erros e renderizar páginas customizadas.&lt;/p&gt;

&lt;p&gt;O Express oferece dois mecanismos para isso: um para o famoso &lt;strong&gt;erro 404&lt;/strong&gt; (página não encontrada) e outro genérico para qualquer erro de servidor (geralmente o &lt;strong&gt;500&lt;/strong&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Criando as views de erro
&lt;/h3&gt;

&lt;p&gt;Primeiro, crie duas novas views dentro de &lt;code&gt;views&lt;/code&gt;. A primeira é a tela de "não encontrado", chame de &lt;code&gt;not-found.ejs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;include&lt;/span&gt; &lt;span class="na"&gt;header&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;header&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Ntalk&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h4&amp;gt;&lt;/span&gt;Infelizmente essa página não existe :(&lt;span class="nt"&gt;&amp;lt;/h4&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;hr&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Vamos voltar &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;home page?&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt; :)&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;include&lt;/span&gt; &lt;span class="na"&gt;footer&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A segunda é a tela de erro genérico, chame de &lt;code&gt;server-error.ejs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;include&lt;/span&gt; &lt;span class="na"&gt;header&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;header&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Ntalk&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h4&amp;gt;&lt;/span&gt;Aconteceu algo terrível! :(&lt;span class="nt"&gt;&amp;lt;/h4&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
    Veja os detalhes do erro:
    &lt;span class="nt"&gt;&amp;lt;br&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="na"&gt;-&lt;/span&gt; &lt;span class="na"&gt;error.message&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;hr&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Que tal voltar &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;home page?&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt; :)&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;include&lt;/span&gt; &lt;span class="na"&gt;footer&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A view de 500 recebe um objeto &lt;code&gt;error&lt;/code&gt; com a mensagem do erro. Em produção, normalmente você esconderia essa mensagem do usuário final (para não vazar detalhes da implementação), mas em desenvolvimento é útil ver o erro direto na tela.&lt;/p&gt;

&lt;h3&gt;
  
  
  Registrando os handlers de erro
&lt;/h3&gt;

&lt;p&gt;O Express tem uma regra interessante: qualquer middleware registrado &lt;strong&gt;depois&lt;/strong&gt; das rotas e que receba uma requisição que nenhuma rota tratou vira automaticamente um handler de 404. Já um middleware com &lt;strong&gt;quatro parâmetros&lt;/strong&gt; (&lt;code&gt;error, req, res, next&lt;/code&gt;) é interpretado como handler de erro genérico, recebendo a exceção que foi lançada em qualquer ponto anterior.&lt;/p&gt;

&lt;p&gt;Para organizar isso, vamos criar um arquivo dedicado em &lt;code&gt;middleware/error.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;notFound&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;not-found&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;serverError&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;server-error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E modificar a stack no &lt;code&gt;app.js&lt;/code&gt; para incluir esses handlers &lt;strong&gt;no final&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nx"&gt;load&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express-load&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./middleware/error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;views&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;__dirname&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/views&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;view engine&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ejs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cookieParser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ntalk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;session&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;urlencoded&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;static&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/public&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;notFound&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;serverError&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Repare na ordem: os handlers de erro vêm &lt;strong&gt;depois&lt;/strong&gt; do &lt;code&gt;app.router&lt;/code&gt; e do &lt;code&gt;express.static&lt;/code&gt;. Isso é fundamental. Se viessem antes, eles interceptariam todas as requisições e nenhuma rota ou arquivo estático seria atendido. A regra é "trate o erro quando todo o resto já falhou".&lt;/p&gt;

&lt;h3&gt;
  
  
  Testando
&lt;/h3&gt;

&lt;p&gt;Reinicie o servidor. Para testar o erro 404, acesse uma URL que não existe, como &lt;code&gt;http://localhost:3000/url-errada&lt;/code&gt;. Em vez do erro técnico, você vai ver sua tela amigável.&lt;/p&gt;

&lt;p&gt;Para testar o erro 500, force um bug deliberadamente. Remova o filtro &lt;code&gt;autenticar&lt;/code&gt; de uma das rotas em &lt;code&gt;routes/contatos.js&lt;/code&gt; e tente acessar &lt;code&gt;/contatos&lt;/code&gt; sem estar logado. O &lt;code&gt;req.session.usuario&lt;/code&gt; é &lt;code&gt;undefined&lt;/code&gt;, a tentativa de ler &lt;code&gt;.contatos&lt;/code&gt; lança uma exceção e o handler de erro 500 entra em ação, renderizando a tela customizada. Depois do teste, lembre-se de &lt;strong&gt;recolocar o filtro&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Existem códigos HTTP para os mais diversos tipos de erro (401 para não autenticado, 403 para acesso negado, 422 para dados inválidos, e por aí vai). À medida que sua aplicação cresce, faz sentido criar handlers e views específicas para cada um. O padrão é sempre o mesmo: registrar o middleware na stack após as rotas, definir o status com &lt;code&gt;res.status(...)&lt;/code&gt; e renderizar a view apropriada.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusão
&lt;/h2&gt;

&lt;p&gt;Você acabou de transformar um esqueleto de aplicação em algo realmente funcional. Recapitulando:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Estruturando views com partials&lt;/strong&gt;: extraímos &lt;code&gt;header.ejs&lt;/code&gt;, &lt;code&gt;footer.ejs&lt;/code&gt; e &lt;code&gt;exit.ejs&lt;/code&gt; para eliminar duplicação, usando a diretiva &lt;code&gt;&amp;lt;% include %&amp;gt;&lt;/code&gt; do EJS. Cada view agora tem só o conteúdo específico daquela tela.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Controlando sessões de usuários&lt;/strong&gt;: adicionamos &lt;code&gt;cookieParser&lt;/code&gt;, &lt;code&gt;session&lt;/code&gt;, &lt;code&gt;json&lt;/code&gt; e &lt;code&gt;urlencoded&lt;/code&gt; na stack, criamos as rotas &lt;code&gt;/entrar&lt;/code&gt; e &lt;code&gt;/sair&lt;/code&gt;, implementamos login e logout manipulando &lt;code&gt;req.session&lt;/code&gt;. Aprendemos a evitar sobrescrever funções nativas da sessão.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CRUD REST&lt;/strong&gt;: implementamos as seis actions (index, show, create, edit, update, destroy) mapeadas para os verbos &lt;code&gt;GET&lt;/code&gt;, &lt;code&gt;POST&lt;/code&gt;, &lt;code&gt;PUT&lt;/code&gt; e &lt;code&gt;DELETE&lt;/code&gt;, com parâmetros de rota via &lt;code&gt;:id&lt;/code&gt;. Descobrimos o truque do campo oculto &lt;code&gt;_method&lt;/code&gt; combinado com o middleware &lt;code&gt;methodOverride&lt;/code&gt; para simular PUT e DELETE em formulários HTML.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Filtros antes das rotas&lt;/strong&gt;: criamos nosso primeiro middleware customizado em &lt;code&gt;middleware/autenticador.js&lt;/code&gt;, aproveitando o suporte do Express a callbacks encadeados em rotas. Bloqueamos acesso à área autenticada sem precisar repetir verificação em cada action.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Páginas de erro amigáveis&lt;/strong&gt;: criamos &lt;code&gt;not-found.ejs&lt;/code&gt; e &lt;code&gt;server-error.ejs&lt;/code&gt;, dois handlers em &lt;code&gt;middleware/error.js&lt;/code&gt;, e os registramos no fim da stack. Entendemos a convenção de quatro parâmetros que define um middleware de erro no Express.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A aplicação agora tem todos os elementos básicos de um sistema web profissional: autenticação, CRUD, filtros, tratamento de erros, e tudo isso organizado de forma modular, sem código repetido, com responsabilidades bem separadas.&lt;/p&gt;

&lt;p&gt;O próximo passo natural é tirar os dados da sessão e colocá-los em um banco de dados de verdade, persistindo os contatos entre sessões. Daí vamos abrir caminho para o chat em tempo real, validação robusta dos modelos, testes automatizados e tudo o mais que transforma uma aplicação de estudo em algo digno de ir para produção.&lt;/p&gt;

&lt;p&gt;Mas isso já é assunto para o próximo tutorial. Por enquanto, aproveite o que você acabou de construir: uma aplicação Express completa, do zero, que faz tudo o que toda aplicação web precisa fazer. Boa codificação, e até lá.&lt;/p&gt;

</description>
      <category>backend</category>
      <category>javascript</category>
      <category>node</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Iniciando com Express: do primeiro scaffold à organização em MVC</title>
      <dc:creator>Moprius</dc:creator>
      <pubDate>Fri, 15 May 2026 02:29:42 +0000</pubDate>
      <link>https://forem.com/moprius/iniciando-com-express-do-primeiro-scaffold-a-organizacao-em-mvc-42pi</link>
      <guid>https://forem.com/moprius/iniciando-com-express-do-primeiro-scaffold-a-organizacao-em-mvc-42pi</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq3tglttf5dezssy5h6ub.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq3tglttf5dezssy5h6ub.png" alt="Node JS Express" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Construir aplicações web usando apenas o módulo HTTP nativo do Node.js é educativo, mas chega rápido em um teto. À medida que o projeto cresce, você se vê reimplementando coisas que praticamente todo servidor web precisa: roteamento, parsing de corpo de requisição, sessões, servir arquivos estáticos, lidar com uploads. Em poucos dias o código vira um emaranhado difícil de manter.&lt;/p&gt;

&lt;p&gt;É exatamente aí que entra o &lt;strong&gt;Express&lt;/strong&gt;, um framework minimalista que se tornou o padrão de fato para desenvolvimento web em Node.js. Neste tutorial você vai entender por que ele é tão usado, vai instalá-lo, gerar seu primeiro projeto via linha de comando, dissecar o scaffold gerado, e ainda reorganizar a estrutura de diretórios para algo profissional, escalável e amigável ao padrão MVC.&lt;/p&gt;

&lt;p&gt;Se você já passou pela dor de manter um servidor HTTP nativo cheio de &lt;code&gt;if/else&lt;/code&gt; para roteamento, prepare-se: o que vem a seguir é um alívio.&lt;/p&gt;

&lt;h2&gt;
  
  
  Por que utilizá-lo?
&lt;/h2&gt;

&lt;p&gt;Programar usando apenas a API HTTP nativa é tremendamente trabalhoso. Conforme novas funcionalidades vão sendo exigidas pelo projeto, códigos enormes são acrescentados, a complexidade aumenta de forma exponencial e qualquer manutenção futura vira um pesadelo. Para cada nova rota, você precisa adicionar mais um &lt;code&gt;if&lt;/code&gt; no roteador artesanal. Para fazer upload de arquivos, vai precisar manipular streams na mão. Para servir arquivos estáticos, vai escrever lógica de leitura de disco com tratamento de tipos MIME. E por aí vai.&lt;/p&gt;

&lt;p&gt;Foi a partir desse problema que surgiu o Express, um framework muito popular voltado ao desenvolvimento de aplicações web de qualquer porte, do pequeno protótipo single-page até sistemas com dezenas de rotas, models, views e controllers. Sua filosofia de trabalho foi fortemente inspirada em outro framework de larga adoção em Ruby, conhecido por sua sintaxe enxuta e expressiva, o que se reflete diretamente na maneira como o Express organiza rotas e middlewares.&lt;/p&gt;

&lt;p&gt;Entre as características que o Express oferece, vale destacar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MVR (Model-View-Routes)&lt;/strong&gt; — uma estrutura mais leve que separa rotas, modelos e views;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MVC (Model-View-Controller)&lt;/strong&gt; — o padrão clássico, totalmente suportado pela flexibilidade do framework;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Roteamento de URLs via callbacks&lt;/strong&gt; — cada rota é uma função simples, sem precisar de configuração XML ou arquivos auxiliares;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Middleware&lt;/strong&gt; — camadas de processamento que ficam entre a requisição e a resposta, permitindo modularizar autenticação, logs, parsing e muito mais;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interface RESTful&lt;/strong&gt; — suporte nativo aos métodos HTTP usados em APIs REST (&lt;code&gt;GET&lt;/code&gt;, &lt;code&gt;POST&lt;/code&gt;, &lt;code&gt;PUT&lt;/code&gt;, &lt;code&gt;DELETE&lt;/code&gt;);&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Suporte a file uploads&lt;/strong&gt; — pronto para receber arquivos multipart sem reinventar a roda;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configuração baseada em variáveis de ambiente&lt;/strong&gt; — facilita ter ambientes separados para desenvolvimento, teste e produção;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Suporte a helpers dinâmicos&lt;/strong&gt; — funções acessíveis dentro das views;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integração com template engines&lt;/strong&gt; — múltiplos motores de templates suportados, como EJS, Jade (Pug), Handlebars;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integração com SQL e NoSQL&lt;/strong&gt; — não impõe uma camada de persistência, deixa você escolher.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Em outras palavras: Express resolve as partes chatas do desenvolvimento web e deixa você focar no que realmente importa, que é a lógica da sua aplicação.&lt;/p&gt;

&lt;h2&gt;
  
  
  Instalação e configuração
&lt;/h2&gt;

&lt;p&gt;A instalação do Express é simples e existem algumas opções de configuração para começar um projeto. Para aproveitar todos os recursos, especialmente a ferramenta de linha de comando que ele oferece, a recomendação é instalar em modo global:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; express
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Feito isso, será necessário &lt;strong&gt;fechar e reabrir seu terminal&lt;/strong&gt; para que o comando &lt;code&gt;express&lt;/code&gt; fique disponível no PATH do sistema. Esse comando é um CLI (Command Line Interface) do framework, capaz de gerar a estrutura inicial de um projeto pronta para uso. Ele já configura suporte a sessões, inclui um template engine (por padrão, o Jade) e suporta diferentes engines de CSS (por padrão, CSS puro).&lt;/p&gt;

&lt;p&gt;Para ver todas as opções disponíveis do CLI, execute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;express &lt;span class="nt"&gt;-h&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Você vai ver uma lista de flags que controlam o template engine, o motor de CSS, e outros detalhes da geração do projeto. Vale dar uma olhada antes de começar qualquer projeto novo, porque tomar a decisão certa nessa etapa economiza um bom tempo depois.&lt;/p&gt;

&lt;h2&gt;
  
  
  Criando um projeto de verdade
&lt;/h2&gt;

&lt;p&gt;Para tornar o aprendizado mais concreto, vamos construir uma aplicação que vai evoluir ao longo do estudo: uma agenda de contatos integrada a um chat funcionando em tempo real. Vamos chamar o projeto de &lt;strong&gt;Ntalk&lt;/strong&gt; (Node talk).&lt;/p&gt;

&lt;p&gt;Os requisitos do projeto são os seguintes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;O usuário deve poder criar, editar ou excluir um contato;&lt;/li&gt;
&lt;li&gt;O usuário deve se logar informando seu nome e e-mail;&lt;/li&gt;
&lt;li&gt;O usuário deve poder se conectar ou desconectar do chat;&lt;/li&gt;
&lt;li&gt;O usuário deve enviar e receber mensagens no chat somente entre contatos que estejam online.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Para chegar lá, vamos usar uma stack bem variada, que cobre as principais peças de uma aplicação web moderna:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Node.js&lt;/strong&gt; — backend do projeto;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MongoDB&lt;/strong&gt; — banco de dados NoSQL orientado a documentos, para armazenar usuários e contatos;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Redis&lt;/strong&gt; — banco de dados NoSQL focado em estruturas chave-valor, ótimo para sessões e dados voláteis;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Express&lt;/strong&gt; — framework para a camada web;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Socket.IO&lt;/strong&gt; — módulo para comunicação em tempo real via WebSockets;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MongooseJS&lt;/strong&gt; — ODM (Object Data Mapper) que abstrai o MongoDB de forma elegante para Node.js;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Node Redis&lt;/strong&gt; — cliente Redis para Node.js;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EJS&lt;/strong&gt; — template engine para HTML dinâmico, com sintaxe próxima do JavaScript puro;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mocha&lt;/strong&gt; — framework para testes automatizados;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SuperTest&lt;/strong&gt; — módulo que emula requisições HTTP, ideal para testes de integração;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nginx&lt;/strong&gt; — servidor web de alta performance para servir arquivos estáticos em produção.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Não se assuste com o tamanho da lista. Cada uma dessas tecnologias entrará em cena no momento certo. Por enquanto, foque na base: Express e EJS.&lt;/p&gt;

&lt;p&gt;Para começar a criar o projeto usando o CLI, execute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;express ntalk &lt;span class="nt"&gt;--ejs&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;ntalk
npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O primeiro comando cria a pasta &lt;code&gt;ntalk&lt;/code&gt; com a estrutura inicial do projeto, já configurada para usar EJS como template engine. O segundo entra na pasta. O terceiro instala todas as dependências listadas no &lt;code&gt;package.json&lt;/code&gt; gerado.&lt;/p&gt;

&lt;p&gt;Parabéns: você acabou de criar a base do seu primeiro projeto Express de verdade.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gerando scaffold do projeto
&lt;/h2&gt;

&lt;p&gt;Ao entrar no diretório do projeto recém-criado, você vai encontrar uma estrutura limpa, gerada automaticamente pelo CLI. Os principais arquivos e pastas são:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;package.json&lt;/strong&gt; — contém as informações sobre a aplicação: nome, autor, versão, colaboradores, URL, dependências e muito mais. É a "carteira de identidade" do projeto para o ecossistema Node.js.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;public&lt;/strong&gt; — pasta destinada a conteúdo estático: imagens, arquivos CSS, JavaScripts do lado cliente, fontes etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;app.js&lt;/strong&gt; — arquivo que inicializa o servidor do projeto. É o ponto de entrada da aplicação, executado com &lt;code&gt;node app.js&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;routes&lt;/strong&gt; — diretório que mantém todas as rotas da aplicação.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;views&lt;/strong&gt; — diretório que contém todas as views renderizadas pelas rotas.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Quando rodamos o &lt;code&gt;npm install&lt;/code&gt;, ele instalou por padrão as dependências que já estavam declaradas no &lt;code&gt;package.json&lt;/code&gt; gerado. Inicialmente, são apenas duas: o Express e o EJS.&lt;/p&gt;

&lt;p&gt;Vamos fazer pequenas alterações no scaffold para deixá-lo limpo e didático. Primeiro, abra o &lt;code&gt;package.json&lt;/code&gt; e ajuste para deixá-lo assim:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ntalk"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Node talk - Agenda de contatos"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"private"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.0.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node app.js"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"dependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"express"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3.4.7"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"ejs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.8.5"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As mudanças foram pequenas mas importantes: adicionamos uma &lt;code&gt;description&lt;/code&gt; que documenta o propósito do projeto e setamos &lt;code&gt;private&lt;/code&gt; como &lt;code&gt;false&lt;/code&gt;. Esses metadados ajudam tanto humanos quanto ferramentas a entenderem do que se trata o projeto.&lt;/p&gt;

&lt;p&gt;Agora vamos enxugar o &lt;code&gt;app.js&lt;/code&gt;. O scaffold gera um arquivo com bastante código pronto, mas, para entendermos cada peça em "baby-steps", vale apagar tudo e começar com o mínimo possível:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nx"&gt;routes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./routes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;views&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;__dirname&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/views&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;view engine&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ejs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;static&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/public&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/usuarios&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Ntalk no ar.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Essa é a configuração mínima de uma aplicação Express funcional. Vamos dissecá-la em detalhes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A função &lt;code&gt;express()&lt;/code&gt;&lt;/strong&gt; é o ponto de partida. Ao invocá-la, recebemos uma instância configurável do framework, que armazenamos na variável &lt;code&gt;app&lt;/code&gt;. É a partir desse objeto que tudo será definido: rotas, middlewares, configurações de view, e a chamada para colocar o servidor no ar.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;app.listen(3000, ...)&lt;/code&gt;&lt;/strong&gt; funciona de forma muito parecida com o &lt;code&gt;http.listen()&lt;/code&gt; do módulo nativo. Na prática, é um atalho (alias) que coloca a aplicação no ar na porta especificada e dispara um callback quando o servidor está pronto.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Os métodos &lt;code&gt;app.get()&lt;/code&gt;, &lt;code&gt;app.post()&lt;/code&gt;, &lt;code&gt;app.put()&lt;/code&gt; e &lt;code&gt;app.del()&lt;/code&gt;&lt;/strong&gt; são as funções de roteamento. Cada uma corresponde a um método HTTP: GET, POST, PUT e DELETE, respectivamente. O primeiro parâmetro é a string com o caminho da rota, e o segundo é uma função callback que recebe a requisição e a resposta. Exemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/contatos&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// lógica da rota aqui&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A diferença em relação ao módulo HTTP nativo é gritante. Em vez de um &lt;code&gt;if/else&lt;/code&gt; gigante dentro de um único handler, agora cada rota tem sua própria função, registrada de forma declarativa. Isso é muito mais legível, testável e modular.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;O método &lt;code&gt;app.set(chave, valor)&lt;/code&gt;&lt;/strong&gt; funciona como uma estrutura simples de chave-valor mantida dentro de &lt;code&gt;app&lt;/code&gt;. Conceitualmente, é como se você estivesse fazendo &lt;code&gt;app["chave"] = "valor"&lt;/code&gt;. Um exemplo prático é a configuração das views: &lt;code&gt;app.set('views', __dirname + '/views')&lt;/code&gt; diz onde estão os templates, e &lt;code&gt;app.set('view engine', 'ejs')&lt;/code&gt; define qual engine usar para renderizá-los.&lt;/p&gt;

&lt;p&gt;A maioria das funções chamadas diretamente pela variável &lt;code&gt;express&lt;/code&gt; são herdadas de dois submódulos: o &lt;strong&gt;Connect&lt;/strong&gt; e o módulo HTTP nativo.&lt;/p&gt;

&lt;h3&gt;
  
  
  Detalhes sobre o Connect
&lt;/h3&gt;

&lt;p&gt;O Connect é um middleware para servidores HTTP. Com ele, é possível configurar aspectos do servidor através do conceito de &lt;strong&gt;pilha (stack)&lt;/strong&gt;: os primeiros itens inseridos são os primeiros a serem executados, sempre antes de a requisição chegar nos callbacks das rotas. O Express herda todas as funcionalidades do Connect, e por isso é fundamental compreender a ordem dos itens inseridos no stack de configuração.&lt;/p&gt;

&lt;p&gt;Se você não respeitar essa ordem, a aplicação pode se comportar de forma estranha, gerar erros inesperados ou simplesmente deixar de executar rotinas que você esperava que rodassem. A documentação oficial lista esses itens já na ordem em que cada um deve ser incluído. Sempre que tiver dúvida, é para lá que você deve olhar.&lt;/p&gt;

&lt;p&gt;Em nossa configuração inicial, inserimos apenas dois itens no stack: o template engine EJS e o diretório de arquivos estáticos. Outros itens, como parsers de corpo, sessões, logger, serão adicionados depois, conforme a aplicação for crescendo.&lt;/p&gt;

&lt;p&gt;Para deixar isso concreto: imagine que você queira que toda requisição passe primeiro por um logger que registra a URL acessada, depois por um parser que transforma o corpo JSON da requisição em objeto JavaScript, depois por um middleware de sessão, e só então chegue na rota propriamente dita. Em Express, isso vira algo como:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bodyParser&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/contatos&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;listarContatos&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A ordem aqui não é detalhe: é a ordem real de execução. Se você colocar o parser depois das rotas, suas rotas vão receber um corpo de requisição vazio. Se colocar o logger no final, ele nunca vai logar requisições que terminam mais cedo. Essa é a essência do conceito de middleware: cada peça processa a requisição e decide se passa adiante (chamando &lt;code&gt;next()&lt;/code&gt;) ou se encerra ali mesmo.&lt;/p&gt;

&lt;p&gt;Reparou também que registramos apenas duas rotas: &lt;code&gt;/&lt;/code&gt; e &lt;code&gt;/usuarios&lt;/code&gt;. Note como seus callbacks vieram da variável &lt;code&gt;routes&lt;/code&gt;, que por sua vez foi obtida com &lt;code&gt;require('./routes')&lt;/code&gt;. Aí tem um detalhe interessante: passamos um diretório para o &lt;code&gt;require&lt;/code&gt;, e não um arquivo. Por convenção, quando o &lt;code&gt;require&lt;/code&gt; aponta para uma pasta, ele procura por um arquivo &lt;code&gt;index.js&lt;/code&gt; dentro dela. Esse é o motivo de &lt;code&gt;routes.index&lt;/code&gt; funcionar magicamente: ele está buscando o &lt;code&gt;exports.index&lt;/code&gt; do arquivo &lt;code&gt;routes/index.js&lt;/code&gt;. Já &lt;code&gt;routes.user.index&lt;/code&gt; segue a regra normal: carrega &lt;code&gt;routes/user.js&lt;/code&gt; e acessa o método &lt;code&gt;exports.index&lt;/code&gt; exportado por ele.&lt;/p&gt;

&lt;h2&gt;
  
  
  Organizando os diretórios do projeto
&lt;/h2&gt;

&lt;p&gt;Quando o assunto é organização de código, o Express se comporta de forma bem flexível e liberal. Apesar de utilizar o scaffold inicial gerado pelo CLI, temos total liberdade para modificar a estrutura de diretórios e arquivos. A escolha vai depender da complexidade do projeto:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Se for um sistema &lt;strong&gt;single-page&lt;/strong&gt; muito enxuto, você pode até desenvolver todo o backend dentro do &lt;code&gt;app.js&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Se houver muitas rotas, views, models e controllers, o caminho natural é organizar tudo no padrão &lt;strong&gt;MVC (Model-View-Controller)&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Para o nosso projeto, vamos adotar MVC. Faltam apenas dois diretórios: &lt;code&gt;models&lt;/code&gt; e &lt;code&gt;controllers&lt;/code&gt;. Crie-os no nível raiz do projeto. A estrutura ficará assim:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ntalk/
├── app.js
├── package.json
├── public/
├── routes/
├── views/
├── controllers/
└── models/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pronto. Agora temos cada responsabilidade em seu próprio diretório.&lt;/p&gt;

&lt;p&gt;Vale aproveitar para reforçar a intenção de cada pasta dentro do MVC, porque essa clareza vai te poupar muita confusão no futuro:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;models&lt;/strong&gt; — responsáveis por representar os dados da aplicação e as regras de negócio relacionadas a eles. Aqui ficam as definições dos schemas do MongoDB, validações de campos, métodos que calculam coisas a partir dos dados. Não há nada de HTTP, nada de HTML aqui. Models não sabem que existe um navegador.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;controllers&lt;/strong&gt; — recebem a requisição, conversam com os models para obter ou alterar dados, e decidem o que renderizar de volta. São a "cola" entre os models e as views. A maior parte da lógica de aplicação fica aqui.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;views&lt;/strong&gt; — apenas exibem dados. Não fazem consultas, não tomam decisões de negócio. Apenas formatam o que receberam dos controllers em HTML, JSON ou qualquer outro formato.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;routes&lt;/strong&gt; — mapeiam URLs para actions específicas dos controllers. Devem ser bem enxutas: nada de lógica aqui, só roteamento.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Manter essa separação disciplinada desde o início é um dos hábitos que mais distingue projetos que envelhecem bem de projetos que viram bola de neve em poucos meses.&lt;/p&gt;

&lt;h3&gt;
  
  
  O problema dos requires espalhados
&lt;/h3&gt;

&lt;p&gt;Cada model que for usado em um controller normalmente precisaria de uma chamada &lt;code&gt;require('./models/nome-do-model')&lt;/code&gt;. Em um projeto com vários controllers que usam vários models, isso significa &lt;strong&gt;dezenas de chamadas de &lt;code&gt;require&lt;/code&gt; espalhadas&lt;/strong&gt; pelo código, e isso polui horrivelmente os arquivos. Para piorar, qualquer mudança de caminho ou renomeação exige procurar e ajustar essas chamadas em todo o projeto.&lt;/p&gt;

&lt;p&gt;O ideal seria usar &lt;code&gt;require&lt;/code&gt; apenas para módulos externos ou para coisas chamadas dentro do &lt;code&gt;app.js&lt;/code&gt;. Para resolver esse problema, surgiu um plugin chamado &lt;strong&gt;express-load&lt;/strong&gt;. Ele mapeia diretórios inteiros, carrega cada arquivo encontrado e injeta os módulos resultantes dentro de uma variável que você escolher.&lt;/p&gt;

&lt;p&gt;Adicione-o como dependência no &lt;code&gt;package.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"dependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"express"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3.4.7"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"express-load"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.1.8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ejs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.8.5"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Em seguida, rode &lt;code&gt;npm install&lt;/code&gt; para baixar a nova dependência.&lt;/p&gt;

&lt;p&gt;Agora vamos refatorar o &lt;code&gt;app.js&lt;/code&gt; para usar essa nova funcionalidade:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nx"&gt;load&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express-load&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// ...stack de configurações do servidor...&lt;/span&gt;

&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;models&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;controllers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;routes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;into&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// ...app.listen(3000)...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Repare na &lt;strong&gt;ordem&lt;/strong&gt; dos itens carregados pela função &lt;code&gt;load()&lt;/code&gt;. Isso é fundamental: primeiro carregamos os &lt;code&gt;models&lt;/code&gt;, depois os &lt;code&gt;controllers&lt;/code&gt; (que dependem dos models) e por último as &lt;code&gt;routes&lt;/code&gt; (que dependem dos controllers). Se inverter essa ordem, na hora em que uma rota tentar acessar um controller, ele simplesmente não vai existir ainda.&lt;/p&gt;

&lt;h3&gt;
  
  
  Refatorando as rotas
&lt;/h3&gt;

&lt;p&gt;Continuando o refactoring, exclua o arquivo &lt;code&gt;routes/user.js&lt;/code&gt; gerado pelo Express, ele não será mais necessário. Em seguida, renomeie &lt;code&gt;routes/index.js&lt;/code&gt; para &lt;code&gt;routes/home.js&lt;/code&gt;. Coloque dentro dele o seguinte código, que já está adaptado para usar a variável &lt;code&gt;app&lt;/code&gt; injetada pelo &lt;code&gt;express-load&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;home&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;controllers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;home&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;home&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O que está acontecendo aqui? O &lt;code&gt;express-load&lt;/code&gt; criou automaticamente um objeto chamado &lt;code&gt;controllers&lt;/code&gt; dentro de &lt;code&gt;app&lt;/code&gt;, espelhando a estrutura de diretórios. Ou seja, &lt;code&gt;app.controllers.home&lt;/code&gt; faz referência direta ao arquivo &lt;code&gt;controllers/home.js&lt;/code&gt;. Magia? Não. Convenção. E muito útil.&lt;/p&gt;

&lt;h3&gt;
  
  
  Criando o primeiro controller
&lt;/h3&gt;

&lt;p&gt;Para a rota funcionar, precisamos criar o controller &lt;code&gt;home&lt;/code&gt;. Em &lt;code&gt;controllers/home.js&lt;/code&gt;, coloque o seguinte código, que define uma &lt;strong&gt;action&lt;/strong&gt; chamada &lt;code&gt;index&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;HomeController&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;home/index&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;HomeController&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A função &lt;code&gt;res.render('home/index')&lt;/code&gt; é uma das mais úteis do Express: ela renderiza a view localizada em &lt;code&gt;views/home/index.ejs&lt;/code&gt; e devolve o resultado como HTML para o navegador. Note que não precisamos passar o caminho completo nem a extensão — o Express já sabe que as views estão na pasta &lt;code&gt;views&lt;/code&gt; (porque configuramos com &lt;code&gt;app.set('views', ...)&lt;/code&gt;) e que a extensão é &lt;code&gt;.ejs&lt;/code&gt; (porque configuramos com &lt;code&gt;app.set('view engine', 'ejs')&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Criando a primeira view
&lt;/h3&gt;

&lt;p&gt;Para fechar o fluxo entre route, controller e view, exclua o arquivo &lt;code&gt;views/index.ejs&lt;/code&gt; gerado pelo scaffold e crie o diretório &lt;code&gt;views/home&lt;/code&gt;. A homepage será uma simples tela de login para acessar o sistema. A lógica futura será de autocadastro: quando o usuário informar um nome novo, o sistema vai cadastrá-lo automaticamente.&lt;/p&gt;

&lt;p&gt;Dentro de &lt;code&gt;views/home&lt;/code&gt;, crie o arquivo &lt;code&gt;index.ejs&lt;/code&gt; com um formulário contendo os campos de nome e e-mail:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Ntalk - Agenda de contatos&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;header&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Ntalk&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;h4&amp;gt;&lt;/span&gt;Bem-vindo!&lt;span class="nt"&gt;&amp;lt;/h4&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;section&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"/entrar"&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"post"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"nome"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Seu nome"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;br&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Seu e-mail"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;br&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Entrar&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;footer&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;small&amp;gt;&lt;/span&gt;Ntalk - Agenda de contatos&lt;span class="nt"&gt;&amp;lt;/small&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/footer&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora vamos rodar o projeto. No terminal, execute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node app.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Em seguida, acesse &lt;code&gt;http://localhost:3000&lt;/code&gt; no navegador. Se tudo deu certo, você verá a tela de login do Ntalk, com o título, o formulário e o rodapé.&lt;/p&gt;

&lt;p&gt;Um detalhe importante: ao longo deste tutorial, não vamos focar em CSS nem em refinamentos visuais de HTML, porque a meta é dominar o lado JavaScript da aplicação. Você é livre para customizar o visual depois, com folhas de estilo na pasta &lt;code&gt;public/css&lt;/code&gt;, conforme sua preferência.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusão
&lt;/h2&gt;

&lt;p&gt;Em poucos passos, você saiu de um servidor HTTP nativo cheio de &lt;code&gt;if/else&lt;/code&gt; para uma aplicação Express organizada no padrão MVC, com diretórios bem definidos, carregamento automático de módulos via &lt;code&gt;express-load&lt;/code&gt; e o fluxo completo de uma requisição passando por rota, controller e view.&lt;/p&gt;

&lt;p&gt;Recapitulando o que vimos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Por que usar Express&lt;/strong&gt;: ele resolve as partes repetitivas do desenvolvimento web (roteamento, middlewares, parsers, views) e fornece uma API enxuta inspirada em outros frameworks expressivos. Suas características cobrem MVC, roteamento via callbacks, middleware, REST, file uploads, configuração por ambiente e integração com diversos bancos de dados e template engines.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Instalação&lt;/strong&gt;: o pacote é instalado globalmente via &lt;code&gt;npm install -g express&lt;/code&gt;, e a partir daí o comando &lt;code&gt;express&lt;/code&gt; fica disponível no terminal, gerando scaffolds completos de projeto.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Criando um projeto real&lt;/strong&gt;: definimos os requisitos do Ntalk, conhecemos a stack completa que será usada ao longo do estudo (Node, MongoDB, Redis, Express, Socket.IO, MongooseJS, EJS, Mocha, SuperTest e Nginx) e geramos o projeto via CLI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scaffold gerado&lt;/strong&gt;: entendemos o papel do &lt;code&gt;package.json&lt;/code&gt;, da pasta &lt;code&gt;public&lt;/code&gt;, do &lt;code&gt;app.js&lt;/code&gt;, da pasta &lt;code&gt;routes&lt;/code&gt; e da pasta &lt;code&gt;views&lt;/code&gt;. Aprendemos sobre &lt;code&gt;app.set()&lt;/code&gt;, &lt;code&gt;app.use()&lt;/code&gt; e os métodos de roteamento &lt;code&gt;app.get()&lt;/code&gt;, &lt;code&gt;app.post()&lt;/code&gt;, &lt;code&gt;app.put()&lt;/code&gt; e &lt;code&gt;app.del()&lt;/code&gt;. Vimos como o Express herda boa parte do seu poder do middleware Connect e como a ordem do stack de configurações importa.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Organização em MVC&lt;/strong&gt;: criamos as pastas &lt;code&gt;controllers&lt;/code&gt; e &lt;code&gt;models&lt;/code&gt;, adicionamos o &lt;code&gt;express-load&lt;/code&gt; para evitar uma chuva de &lt;code&gt;require&lt;/code&gt;s pelo projeto, refatoramos as rotas para o novo padrão e criamos um primeiro controller &lt;code&gt;home&lt;/code&gt; ligado a uma view &lt;code&gt;home/index.ejs&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A partir daqui, a aplicação está pronta para crescer. Próximos passos naturais incluem implementar a lógica de cadastro automático no &lt;code&gt;/entrar&lt;/code&gt;, criar o model de usuários, conectar ao MongoDB com Mongoose, persistir os dados e expandir o fluxo do chat em tempo real. Cada peça nova vai se encaixar nessa estrutura sem precisar reescrever nada do que já foi feito.&lt;/p&gt;

&lt;p&gt;E é justamente esse o sinal de uma boa arquitetura: ela aceita o crescimento sem dor. Você acabou de plantar essa semente. Agora é só regar.&lt;/p&gt;

&lt;p&gt;Boa codificação, e até o próximo tutorial.&lt;/p&gt;

</description>
      <category>node</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Desenvolvendo aplicações web com Node.js: do primeiro servidor ao seu próprio roteador de URLs</title>
      <dc:creator>Moprius</dc:creator>
      <pubDate>Fri, 15 May 2026 01:44:11 +0000</pubDate>
      <link>https://forem.com/moprius/desenvolvendo-aplicacoes-web-com-nodejs-do-primeiro-servidor-ao-seu-proprio-roteador-de-urls-4l7a</link>
      <guid>https://forem.com/moprius/desenvolvendo-aplicacoes-web-com-nodejs-do-primeiro-servidor-ao-seu-proprio-roteador-de-urls-4l7a</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frf7921zts5z2aon3ud30.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frf7921zts5z2aon3ud30.png" alt="NodeJS" width="760" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Se você já programa em JavaScript no navegador e quer dar o próximo passo, levando essa linguagem para o lado do servidor, então prepare-se: este tutorial vai te guiar, passo a passo, na construção de aplicações web usando apenas os módulos nativos do Node.js. Sem frameworks, sem mágica. Apenas você, o JavaScript e o motor que executa código fora do navegador.&lt;/p&gt;

&lt;p&gt;A proposta aqui é direta: ao final deste post, você terá criado seu primeiro servidor HTTP, entendido como ele funciona internamente, aprendido a tratar várias rotas, separado o HTML do código JavaScript, e ainda terá um desafio prático para consolidar tudo o que viu. Vamos lá?&lt;/p&gt;

&lt;h2&gt;
  
  
  Por que começar pelo módulo HTTP nativo?
&lt;/h2&gt;

&lt;p&gt;Antes de mergulhar no código, é importante entender o contexto. O Node.js é multiprotocolo. Isso significa que com ele você consegue trabalhar com diversos protocolos de rede: HTTP, HTTPS, FTP, SSH, DNS, TCP, UDP e WebSockets, entre outros disponibilizados pela comunidade. No entanto, quando o assunto é desenvolvimento web, o protocolo HTTP é, de longe, o mais utilizado e o que conta com a maior quantidade de módulos prontos para uso.&lt;/p&gt;

&lt;p&gt;Toda aplicação web precisa de um servidor para disponibilizar seus recursos. E aqui está uma característica interessante do Node.js: quando você desenvolve com ele, está, na prática, criando uma &lt;strong&gt;aplicação middleware&lt;/strong&gt;. Ou seja, além de programar as funcionalidades da sua aplicação, você também é responsável por escrever códigos de configuração da infraestrutura do servidor.&lt;/p&gt;

&lt;p&gt;À primeira vista, isso pode parecer trabalhoso. Afinal, o Node.js exige o mínimo de configurações para servir uma aplicação, o que deixa nas suas mãos a tarefa de definir os detalhes. Mas é justamente aí que mora a vantagem: você consegue customizar ao máximo seu servidor, ajustando detalhes que permitem desenvolver algo extremamente performático e sob controle total.&lt;/p&gt;

&lt;p&gt;Existem módulos de mais alto nível como Connect, Express, Geddy, CompoundJS, Sails e outros, que já vêm com configurações mínimas prontas, permitindo trabalhar com arquiteturas RESTful, padrões MVC e conexões em tempo real com WebSockets. Eles são ótimos para quem precisa de produtividade. Porém, mesmo que você venha a usar esses frameworks no futuro, é fundamental entender o módulo nativo HTTP, porque todos esses frameworks se apoiam nele como base.&lt;/p&gt;

&lt;p&gt;Por isso vamos começar do zero, do jeito mais cru.&lt;/p&gt;

&lt;h2&gt;
  
  
  Criando nossa primeira aplicação web
&lt;/h2&gt;

&lt;p&gt;A clássica aplicação “Hello World” é o ponto de partida ideal. Crie um arquivo chamado &lt;code&gt;hello_server.js&lt;/code&gt; e coloque o seguinte conteúdo dentro dele:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeHead&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text/html&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;h1&amp;gt;Hello World!&amp;lt;/h1&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esse é um exemplo bem enxuto, mas que já mostra a estrutura básica de qualquer servidor Node.js. Vamos dissecá-lo linha por linha:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;var http = require('http');&lt;/code&gt; — Carrega o módulo nativo &lt;code&gt;http&lt;/code&gt;. Tudo que diz respeito a servir conteúdo via HTTP está nesse pacote.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;http.createServer(...)&lt;/code&gt; — Cria uma instância de servidor. A função passada como argumento é o que chamamos de &lt;strong&gt;callback&lt;/strong&gt;, e ela só será executada quando o servidor receber uma requisição.&lt;/li&gt;
&lt;li&gt;O callback recebe dois parâmetros: &lt;code&gt;request&lt;/code&gt; (a requisição feita pelo cliente) e &lt;code&gt;response&lt;/code&gt; (o objeto usado para enviar a resposta de volta).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;response.writeHead(200, {...})&lt;/code&gt; — Escreve o cabeçalho da resposta. O número &lt;code&gt;200&lt;/code&gt; é o status HTTP de sucesso, e o objeto define que estamos enviando conteúdo HTML.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;response.write("&amp;lt;h1&amp;gt;Hello World!&amp;lt;/h1&amp;gt;");&lt;/code&gt; — Adiciona o conteúdo HTML que será enviado ao cliente.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;response.end();&lt;/code&gt; — Encerra a resposta. Sem isso, o navegador ficaria esperando indefinidamente.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;server.listen(3000);&lt;/code&gt; — Coloca o servidor para escutar requisições na porta 3000.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Agora salve o arquivo, vá até o terminal, navegue até a pasta onde ele está e rode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node hello_server.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Em seguida, abra seu navegador e acesse &lt;code&gt;http://localhost:3000&lt;/code&gt;. Você verá uma página com o cabeçalho “Hello World!” em destaque. Pronto, você acabou de subir seu primeiro servidor web em Node.js, sem precisar de Apache, Nginx ou qualquer outro intermediário. Bem-vindo ao desenvolvimento back-end com JavaScript.&lt;/p&gt;

&lt;h2&gt;
  
  
  Como funciona um servidor HTTP por baixo dos panos?
&lt;/h2&gt;

&lt;p&gt;Para evoluir de simples “copia e cola” para um desenvolvedor que realmente entende o que está fazendo, é essencial compreender o mecanismo que faz o servidor responder às requisições. E esse mecanismo se chama &lt;strong&gt;Event Loop&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Um servidor Node.js utiliza o Event Loop como peça central. Ele é o responsável por lidar com a emissão de eventos. Na prática, a função &lt;code&gt;http.createServer()&lt;/code&gt; apenas levanta o servidor. O callback que passamos como argumento (&lt;code&gt;function(request, response)&lt;/code&gt;) só é executado quando o servidor recebe uma requisição. Para que isso aconteça, o Event Loop fica constantemente verificando se o servidor foi requisitado. Quando uma requisição chega, ele emite um evento que dispara a execução do nosso callback.&lt;/p&gt;

&lt;p&gt;Esse modelo é fundamentalmente diferente do que acontece em linguagens com modelo de threads bloqueantes. Aqui, o servidor não cria uma nova thread a cada requisição: ele despacha o trabalho e segue ouvindo. É por isso que o Node.js é tão eficiente para aplicações com muita E/S (entrada e saída).&lt;/p&gt;

&lt;p&gt;O Node.js trabalha intensamente com chamadas assíncronas que respondem por meio de callbacks. Vamos ver isso na prática. Se quisermos receber uma notificação no console assim que o servidor estiver de pé, basta passar uma função como segundo argumento para &lt;code&gt;server.listen()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Servidor Hello World rodando!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O método &lt;code&gt;listen&lt;/code&gt; também é assíncrono. Isso significa que você só saberá que o servidor está de pé quando o Node invocar a sua função de callback. Não há retorno imediato dizendo “pronto, está rodando”. Em vez disso, o Node avisa quando estiver pronto.&lt;/p&gt;

&lt;p&gt;Se você está começando agora com JavaScript, pode estranhar essa prática de passar funções como argumento para todo lado. Isso se chama &lt;strong&gt;higher-order functions&lt;/strong&gt; (funções de ordem superior) e é absolutamente normal no mundo JavaScript. Mas, em algum momento, seu código pode começar a ficar difícil de ler por causa de muitos blocos aninhados. Quando isso acontecer, você pode separar essas funções e dar nomes mais significativos a elas. Veja a diferença:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;atendeRequisicao&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeHead&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text/html&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;h1&amp;gt;Hello World!&amp;lt;/h1&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;atendeRequisicao&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;servidorLigou&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Servidor Hello World rodando!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;servidorLigou&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O comportamento é exatamente o mesmo, mas o código fica mais fácil de ler. Essa técnica é especialmente útil quando os callbacks começam a crescer e a se aninhar. Manter funções pequenas e bem nomeadas é uma prática que vai te poupar muita dor de cabeça no futuro.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trabalhando com diversas rotas
&lt;/h2&gt;

&lt;p&gt;Até agora, nosso servidor só responde à rota raiz (&lt;code&gt;/&lt;/code&gt;). Independentemente do endereço acessado, ele devolve sempre o mesmo HTML. Não é assim que aplicações reais funcionam. Precisamos diferenciar requisições para &lt;code&gt;/sobre&lt;/code&gt;, &lt;code&gt;/contato&lt;/code&gt;, &lt;code&gt;/produtos&lt;/code&gt; e tantas outras rotas que uma aplicação pode ter.&lt;/p&gt;

&lt;p&gt;Vamos adicionar uma rota chamada &lt;code&gt;/bemvindo&lt;/code&gt;, que exibirá uma página de boas-vindas, e uma rota genérica que servirá como página de erro para qualquer endereço não reconhecido. Crie o arquivo &lt;code&gt;hello_server3.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeHead&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text/html&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
        &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;h1&amp;gt;Página principal&amp;lt;/h1&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/bemvindo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
        &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;h1&amp;gt;Bem-vindo :)&amp;lt;/h1&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;h1&amp;gt;Página não encontrada :(&amp;lt;/h1&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Servidor rodando!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rode o servidor com &lt;code&gt;node hello_server3.js&lt;/code&gt; e teste no navegador. Acesse &lt;code&gt;http://localhost:3000/&lt;/code&gt;, depois &lt;code&gt;http://localhost:3000/bemvindo&lt;/code&gt; e, por último, algum caminho inventado como &lt;code&gt;http://localhost:3000/qualquer-coisa&lt;/code&gt;. Você verá três páginas diferentes.&lt;/p&gt;

&lt;p&gt;Repare como o roteamento aqui foi feito de forma bem rústica: usamos uma cadeia de &lt;code&gt;if&lt;/code&gt; e &lt;code&gt;else&lt;/code&gt;, e a leitura da URL é feita por meio da propriedade &lt;code&gt;request.url&lt;/code&gt;, que devolve uma string com o caminho digitado pelo usuário. Funciona, mas em um projeto de verdade, com dezenas ou centenas de rotas, essa abordagem se tornaria um pesadelo de manutenção.&lt;/p&gt;

&lt;p&gt;Além disso, URLs reais costumam carregar informação além do caminho. Existem dois padrões muito comuns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Query strings&lt;/strong&gt;: parâmetros após o &lt;code&gt;?&lt;/code&gt; na URL, como em &lt;code&gt;?nome=joao&amp;amp;idade=30&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Path&lt;/strong&gt;: o caminho em si, como &lt;code&gt;/admin/usuarios&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Para lidar com esses padrões de forma estruturada, o Node.js oferece um módulo nativo chamado &lt;code&gt;url&lt;/code&gt;, responsável por fazer parser e formatação de URLs. Vamos ver como capturar valores de uma query string. Crie o arquivo &lt;code&gt;url_server.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;url&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeHead&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text/html&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;h1&amp;gt;Dados da query string&amp;lt;/h1&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
        &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; : &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Servidor http.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Salve, execute com &lt;code&gt;node url_server.js&lt;/code&gt; e acesse algo como &lt;code&gt;http://localhost:3000/?nome=maria&amp;amp;cidade=saopaulo&lt;/code&gt;. Você verá os pares chave-valor exibidos na página.&lt;/p&gt;

&lt;p&gt;A função &lt;code&gt;url.parse(request.url, true)&lt;/code&gt; faz o parser da URL recebida. O segundo argumento &lt;code&gt;true&lt;/code&gt; indica que a query string deve ser convertida em um objeto JavaScript (caso contrário, ela viria como uma string só). O retorno dessa função traz vários atributos úteis, que vale a pena conhecer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;href&lt;/strong&gt;: a URL completa. Exemplo: &lt;code&gt;http://user:pass@host.com:8080/p/a/t/h?query=string#hash&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;protocol&lt;/strong&gt;: o protocolo usado. Exemplo: &lt;code&gt;http&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;host&lt;/strong&gt;: o domínio com a porta. Exemplo: &lt;code&gt;host.com:8080&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;auth&lt;/strong&gt;: dados de autenticação embutidos. Exemplo: &lt;code&gt;user:pass&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;hostname&lt;/strong&gt;: apenas o domínio. Exemplo: &lt;code&gt;host.com&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;port&lt;/strong&gt;: a porta. Exemplo: &lt;code&gt;8080&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;pathname&lt;/strong&gt;: o caminho. Exemplo: &lt;code&gt;/p/a/t/h&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;search&lt;/strong&gt;: a query string em formato bruto. Exemplo: &lt;code&gt;?query=string&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;path&lt;/strong&gt;: a concatenação de &lt;code&gt;pathname&lt;/code&gt; com &lt;code&gt;search&lt;/code&gt;. Exemplo: &lt;code&gt;/p/a/t/h?query=string&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;query&lt;/strong&gt;: a query string já convertida em JSON. Exemplo: &lt;code&gt;{ query: 'string' }&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;hash&lt;/strong&gt;: âncora da URL. Exemplo: &lt;code&gt;#hash&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Em resumo, o módulo &lt;code&gt;url&lt;/code&gt; permite organizar toda e qualquer URL da aplicação de maneira estruturada, deixando seu código muito mais limpo e legível do que se você tivesse que separar tudo manualmente com &lt;code&gt;split&lt;/code&gt; e expressões regulares.&lt;/p&gt;

&lt;h2&gt;
  
  
  Separando o HTML do JavaScript
&lt;/h2&gt;

&lt;p&gt;Você já deve ter percebido um problema: até aqui, nosso HTML está escrito como string dentro do código JavaScript. Para páginas simples isso pode até funcionar, mas em qualquer aplicação real teremos HTML extenso, com CSS, imagens e estruturas complexas. Misturar tudo em strings JavaScript é receita para um código impossível de manter.&lt;/p&gt;

&lt;p&gt;A boa prática é separar o HTML em arquivos próprios, com a extensão &lt;code&gt;.html&lt;/code&gt;, e fazer a aplicação ler esses arquivos quando precisar respondê-los ao usuário. Para isso, vamos usar mais um módulo nativo: o &lt;strong&gt;File System&lt;/strong&gt;, conhecido pela abreviação &lt;code&gt;fs&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;O módulo &lt;code&gt;fs&lt;/code&gt; é responsável por manipular arquivos e diretórios do sistema operacional. O que ele tem de mais interessante é oferecer praticamente todas as suas funções em duas versões: uma &lt;strong&gt;assíncrona&lt;/strong&gt; e outra &lt;strong&gt;síncrona&lt;/strong&gt;. Por convenção, as funções com sufixo &lt;code&gt;Sync&lt;/code&gt; são as síncronas. Veja o exemplo abaixo, que mostra as duas formas de ler um arquivo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Forma assíncrona&lt;/span&gt;
&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/index.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;erro&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;arquivo&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;erro&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;erro&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arquivo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Forma síncrona&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;arquivo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/index.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arquivo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A função &lt;code&gt;fs.readFile()&lt;/code&gt; faz uma leitura assíncrona. Depois que o arquivo termina de carregar, a função callback é invocada com dois argumentos: o erro (caso tenha ocorrido) e o conteúdo do arquivo. Já a &lt;code&gt;fs.readFileSync()&lt;/code&gt; faz uma leitura síncrona: o programa para tudo e espera o arquivo ser completamente lido para depois prosseguir.&lt;/p&gt;

&lt;p&gt;No mundo Node.js, a forma assíncrona é quase sempre a melhor escolha. O servidor não pode ficar bloqueado esperando uma leitura de disco terminar enquanto outras requisições chegam. Por isso, vamos usar a versão assíncrona daqui em diante.&lt;/p&gt;

&lt;p&gt;Uma observação importante: o módulo File System não é 100% consistente entre os sistemas operacionais. Algumas funções são específicas para Linux, OS X e Unix, e outras só funcionam no Windows. Sempre que for usar funções menos comuns, vale a pena consultar a documentação oficial do Node.js para evitar surpresas.&lt;/p&gt;

&lt;p&gt;Agora vamos juntar HTTP com File System para servir uma página HTML real. Crie o arquivo &lt;code&gt;site_pessoal.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="c1"&gt;// A constante __dirname retorna o diretório raiz da aplicação.&lt;/span&gt;
    &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/index.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
        &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text/html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Executando Site Pessoal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Repare em dois detalhes muito importantes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A constante &lt;code&gt;__dirname&lt;/code&gt; é fornecida automaticamente pelo Node.js e devolve o caminho absoluto do diretório onde o arquivo atual está sendo executado. Sem isso, dependendo de onde o comando é rodado, o caminho relativo poderia quebrar.&lt;/li&gt;
&lt;li&gt;A leitura é assíncrona: a renderização do HTML acontece dentro do callback, ou seja, só depois que o arquivo for completamente lido.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Para esse código funcionar, você precisa criar um arquivo &lt;code&gt;index.html&lt;/code&gt; no mesmo diretório. Algo simples como:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Olá este é o meu site pessoal!&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Bem vindo ao meu site pessoal&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rode &lt;code&gt;node site_pessoal.js&lt;/code&gt; e acesse &lt;code&gt;http://localhost:3000&lt;/code&gt;. A diferença em relação aos exemplos anteriores é que, agora, qualquer alteração no &lt;code&gt;index.html&lt;/code&gt; pode ser feita sem mexer no código JavaScript. Só recarregar o navegador e pronto. Bem-vindo à separação de responsabilidades.&lt;/p&gt;

&lt;h2&gt;
  
  
  Desafio: implementar um roteador de URLs
&lt;/h2&gt;

&lt;p&gt;Agora chegou a melhor parte: colocar a mão na massa e juntar tudo o que você aprendeu em um único projeto. Você já conhece os três módulos nativos essenciais para servir conteúdo web: o &lt;code&gt;http&lt;/code&gt; para subir o servidor, o &lt;code&gt;url&lt;/code&gt; para fazer parser das rotas e o &lt;code&gt;fs&lt;/code&gt; para ler arquivos HTML do disco. Hora de combiná-los em algo útil.&lt;/p&gt;

&lt;p&gt;O desafio é simples na descrição, mas exige atenção: você vai construir um pequeno roteador de URLs, que renderiza arquivos HTML diferentes dependendo do caminho que o usuário digitar no navegador.&lt;/p&gt;

&lt;h3&gt;
  
  
  Regras do desafio
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Crie 3 arquivos HTML: &lt;code&gt;artigos.html&lt;/code&gt;, &lt;code&gt;contato.html&lt;/code&gt; e &lt;code&gt;erro.html&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Coloque qualquer conteúdo dentro de cada um dos arquivos. Pode ser um simples &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; com o nome da página.&lt;/li&gt;
&lt;li&gt;Quando o usuário digitar o path &lt;code&gt;/artigos&lt;/code&gt; no navegador, o servidor deve renderizar o &lt;code&gt;artigos.html&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Quando o usuário digitar &lt;code&gt;/contato&lt;/code&gt;, o servidor deve renderizar o &lt;code&gt;contato.html&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Para qualquer outro path diferente de &lt;code&gt;/artigos&lt;/code&gt; e &lt;code&gt;/contato&lt;/code&gt;, o servidor deve renderizar o &lt;code&gt;erro.html&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Toda a leitura de arquivos HTML deve ser feita de forma assíncrona.&lt;/li&gt;
&lt;li&gt;A rota principal &lt;code&gt;/&lt;/code&gt; (raiz) deve renderizar o &lt;code&gt;artigos.html&lt;/code&gt; como padrão.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Dicas importantes
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt; Use o retorno da função &lt;code&gt;url.parse()&lt;/code&gt; para capturar o &lt;code&gt;pathname&lt;/code&gt; digitado e renderizar o HTML correspondente. Se o &lt;code&gt;pathname&lt;/code&gt; estiver vazio ou for &lt;code&gt;/&lt;/code&gt;, significa que deve renderizar a página de artigos. Se o valor for diferente do nome de qualquer arquivo HTML que você tem, renderize a página de erro.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.&lt;/strong&gt; Você pode inserir o conteúdo HTML diretamente dentro da função &lt;code&gt;response.end(html)&lt;/code&gt;, economizando uma linha de código. Em vez de fazer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Você pode fazer apenas:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O comportamento é o mesmo, mas o código fica mais conciso.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3.&lt;/strong&gt; Você pode usar uma estrutura condicional simples para decidir qual arquivo carregar, baseando-se no &lt;code&gt;pathname&lt;/code&gt;. Algo na linha de “se o pathname é &lt;code&gt;/artigos&lt;/code&gt; ou &lt;code&gt;/&lt;/code&gt;, carregue &lt;code&gt;artigos.html&lt;/code&gt;; se é &lt;code&gt;/contato&lt;/code&gt;, carregue &lt;code&gt;contato.html&lt;/code&gt;; caso contrário, carregue &lt;code&gt;erro.html&lt;/code&gt;”.&lt;/p&gt;

&lt;h3&gt;
  
  
  Estratégia recomendada
&lt;/h3&gt;

&lt;p&gt;Antes de sair codando, pense no fluxo da aplicação:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Subir um servidor HTTP na porta 3000.&lt;/li&gt;
&lt;li&gt;Em cada requisição, obter a URL chamada.&lt;/li&gt;
&lt;li&gt;Fazer parser dessa URL para extrair o &lt;code&gt;pathname&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Decidir qual arquivo HTML carregar com base nesse &lt;code&gt;pathname&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Ler o arquivo de forma assíncrona.&lt;/li&gt;
&lt;li&gt;Devolver o conteúdo do arquivo como resposta HTTP com status 200 e content-type &lt;code&gt;text/html&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Esse exercício, embora pareça pequeno, te coloca em contato com a arquitetura básica de qualquer aplicação web em Node.js. Frameworks como o Express, que você provavelmente vai aprender mais tarde, fazem exatamente isso por baixo dos panos: leem a URL, decidem o que fazer, e respondem.&lt;/p&gt;

&lt;h3&gt;
  
  
  Por que esse desafio importa?
&lt;/h3&gt;

&lt;p&gt;Quando você implementa o roteador manualmente, sem depender de bibliotecas externas, você passa a entender o que acontece quando configura uma rota em qualquer framework. Você sabe que o framework está lendo &lt;code&gt;request.url&lt;/code&gt;, fazendo um parser, escolhendo um handler e respondendo. Esse conhecimento é o que separa quem usa ferramentas “por mágica” de quem realmente domina a tecnologia.&lt;/p&gt;

&lt;p&gt;Tente fazer o desafio sozinho antes de procurar uma solução pronta. Se travar, releia as seções anteriores deste tutorial. Todas as peças que você precisa já foram apresentadas. O exercício é justamente combiná-las.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusão
&lt;/h2&gt;

&lt;p&gt;Em poucas linhas de código, você passou de zero a um pequeno servidor web funcional. E mais do que isso, você entendeu &lt;strong&gt;por que&lt;/strong&gt; cada peça do quebra-cabeça existe.&lt;/p&gt;

&lt;p&gt;Recapitulando o caminho percorrido:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Você descobriu que o Node.js é multiprotocolo e que, no contexto web, o HTTP é o protocolo mais usado e mais bem suportado.&lt;/li&gt;
&lt;li&gt;Aprendeu que cada aplicação Node.js é também uma aplicação middleware, exigindo que você programe não só o seu domínio, mas também aspectos da infraestrutura, com a vantagem da customização total.&lt;/li&gt;
&lt;li&gt;Construiu seu primeiro servidor com o módulo nativo &lt;code&gt;http&lt;/code&gt;, entendendo cada parâmetro: &lt;code&gt;writeHead&lt;/code&gt;, &lt;code&gt;write&lt;/code&gt;, &lt;code&gt;end&lt;/code&gt; e &lt;code&gt;listen&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Compreendeu o papel do &lt;strong&gt;Event Loop&lt;/strong&gt; como motor que orquestra a execução dos callbacks de forma assíncrona, sem bloquear o servidor.&lt;/li&gt;
&lt;li&gt;Implementou um roteamento simples com &lt;code&gt;if/else&lt;/code&gt; usando &lt;code&gt;request.url&lt;/code&gt;, e depois evoluiu para o uso do módulo nativo &lt;code&gt;url&lt;/code&gt; com &lt;code&gt;url.parse&lt;/code&gt;, conhecendo todos os atributos disponíveis: &lt;code&gt;href&lt;/code&gt;, &lt;code&gt;protocol&lt;/code&gt;, &lt;code&gt;host&lt;/code&gt;, &lt;code&gt;auth&lt;/code&gt;, &lt;code&gt;hostname&lt;/code&gt;, &lt;code&gt;port&lt;/code&gt;, &lt;code&gt;pathname&lt;/code&gt;, &lt;code&gt;search&lt;/code&gt;, &lt;code&gt;path&lt;/code&gt;, &lt;code&gt;query&lt;/code&gt; e &lt;code&gt;hash&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Aplicou boas práticas separando o HTML do JavaScript, lendo arquivos com o módulo &lt;code&gt;fs&lt;/code&gt;, e entendendo as diferenças entre leitura síncrona (&lt;code&gt;readFileSync&lt;/code&gt;) e assíncrona (&lt;code&gt;readFile&lt;/code&gt;), além de conhecer a utilíssima constante &lt;code&gt;__dirname&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Recebeu um desafio que combina todos esses conceitos em uma única aplicação prática.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tudo isso usando apenas módulos nativos, sem instalar uma única dependência via &lt;code&gt;npm&lt;/code&gt;. Esse é o tipo de fundamento que vai te acompanhar pelo resto da sua jornada com Node.js, independentemente do framework que você venha a adotar.&lt;/p&gt;

&lt;p&gt;A partir daqui, o céu é o limite. Você pode evoluir esse roteador para suportar parâmetros dinâmicos (algo como &lt;code&gt;/artigos/:id&lt;/code&gt;), pode adicionar suporte a métodos HTTP diferentes (GET, POST, PUT, DELETE) e pode até começar a servir conteúdo JSON em vez de HTML, criando assim uma API REST completa, ainda sem frameworks.&lt;/p&gt;

&lt;p&gt;Mas se em algum momento você sentir que está reinventando a roda, saiba que existe um ecossistema gigantesco de bibliotecas prontas para te ajudar. E quando chegar nesse ponto, você não vai ser apenas mais um copiador de exemplos de internet: vai saber exatamente o que aquelas bibliotecas estão fazendo, porque já fez na unha.&lt;/p&gt;

&lt;p&gt;Boa codificação, e até o próximo tutorial.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>node</category>
      <category>npm</category>
    </item>
    <item>
      <title>Visualização de dados em Python</title>
      <dc:creator>Moprius</dc:creator>
      <pubDate>Thu, 20 Nov 2025 15:50:02 +0000</pubDate>
      <link>https://forem.com/moprius/visualizacao-de-dados-em-python-43j0</link>
      <guid>https://forem.com/moprius/visualizacao-de-dados-em-python-43j0</guid>
      <description>&lt;p&gt;Quando alguém fala em “limpar dados”, a primeira imagem que costuma vir à cabeça é abrir um Jupyter Notebook e começar a brincar com pandas. Mas tem uma coisa que pouca gente lembra: uma parte bem poderosa dessa faxina pode acontecer direto no terminal, usando ferramentas que já vêm instaladas no sistema.&lt;/p&gt;

&lt;p&gt;Aqui você vai ver como usar utilitários básicos de linha de comando para limpar, transformar e explorar arquivos de dados. Nada de instalar mil coisas: só o seu terminal aberto e alguns arquivos CSV na pasta, como se você estivesse mexendo numa planilha cheia de problemas de cadastro de clientes.&lt;/p&gt;

&lt;p&gt;Antes de sair digitando comando, vale entender por que isso é tão útil no dia a dia:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ferramentas de linha de comando são simples de usar, rápidas e eficientes, principalmente quando o arquivo é grande, tipo log de sistema, extrato gigante em CSV ou export de ERP.&lt;/li&gt;
&lt;li&gt;Elas já vêm no Linux e no macOS, e existem versões para Windows também.&lt;/li&gt;
&lt;li&gt;São ótimas para dar aquela primeira olhada nos dados antes de carregar tudo no Python ou em outra linguagem.&lt;/li&gt;
&lt;li&gt;É fácil encadear comandos em scripts e reaproveitar essa rotina de limpeza como parte de uma automação, seja num cron job, num pipeline de dados ou num script de manutenção.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Vamos colocar a mão no teclado.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Dica: imagine que este texto está acompanhado de um script Bash em um repositório no GitHub. A ideia é que você abra o terminal, deixe o arquivo ao lado e vá copiando os comandos para sentir na pele como funciona.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  Criando um conjunto de dados “sujo”
&lt;/h1&gt;

&lt;p&gt;Primeiro passo: montar um CSV bem bagunçado para brincar. Ele simula problemas reais que aparecem em cadastro de RH, planilhas de vendas, lista de alunos e por aí vai.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; messy_data.csv &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
name,age,salary,department,email
John Lee,32,50000,Engineering,john@example.com
Jane Smith,28,55000,Marketing,jane@example.com
   Bob Davis    ,35,60000,Engineering,bob@example.com
Alice Williams,29,,Marketing,alice@example.com
Charlie Brown,45,70000,Sales,charlie@example.com
Dave Wilson,31,52000,Engineering,
Emma Davis,,58000,Marketing,emma@example.com
Frank Miller,38,65000,Sales,frank@example.com
John Lee,32,50000,Engineering,john@example.com
Grace Lee,27,51000,Engineering,grace@example.com
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esse mini–conjunto de dados já vem com vários problemas típicos: espaço sobrando antes e depois de alguns nomes, valores faltando, linhas duplicadas. É o tipo de coisa que aparece quando você junta dados de sistemas diferentes, importa planilhas de várias pessoas ou baixa relatórios de plataformas distintas. Ótimo cenário para aprender.&lt;/p&gt;




&lt;h1&gt;
  
  
  1. Explorando seus dados com &lt;code&gt;head&lt;/code&gt;, &lt;code&gt;tail&lt;/code&gt; e &lt;code&gt;wc&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;Antes de tentar arrumar qualquer coisa, você precisa entender com o que está lidando. É como abrir um armário bagunçado e dar aquela primeira olhada antes de decidir o que vai pro lixo, o que vai pra doação e o que volta pra prateleira.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Ver as 5 primeiras linhas (incluindo o cabeçalho)&lt;/span&gt;
&lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 5 messy_data.csv

&lt;span class="c"&gt;# Ver as 3 últimas linhas&lt;/span&gt;
&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 3 messy_data.csv

&lt;span class="c"&gt;# Contar o total de linhas (incluindo o cabeçalho)&lt;/span&gt;
&lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; messy_data.csv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O que está rolando aqui?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;head -n 5&lt;/code&gt; mostra as 5 primeiras linhas, uma prévia rápida para ver se o formato do arquivo está dentro do esperado.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tail -n 3&lt;/code&gt; mostra as 3 últimas, ótimo para checar se o arquivo terminou direito ou se ficou alguma linha cortada no final.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;wc -l&lt;/code&gt; conta quantas linhas existem; tirando 1 (do cabeçalho), você descobre quantos registros tem de verdade.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Saída de exemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;name,age,salary,department,email
John Lee,32,50000,Engineering,john@example.com
Jane Smith,28,55000,Marketing,jane@example.com
   Bob Davis    ,35,60000,Engineering,bob@example.com
Alice Williams,29,,Marketing,alice@example.com
Frank Miller,38,65000,Sales,frank@example.com
John Lee,32,50000,Engineering,john@example.com
Grace Lee,27,51000,Engineering,grace@example.com
11 messy_data.csv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;É como dar aquela olhada rápida numa planilha no LibreOffice só para ver se os dados “parecem” certos.&lt;/p&gt;




&lt;h1&gt;
  
  
  2. Visualizando colunas específicas com &lt;code&gt;cut&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;Nem sempre você quer ver o arquivo todo. Às vezes, só quer saber o nome das pessoas e o setor onde trabalham, tipo quando sua chefe pede “me manda só a lista de nomes com o departamento, o resto não importa”.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="s1"&gt;','&lt;/span&gt; &lt;span class="nt"&gt;-f1&lt;/span&gt;,4 messy_data.csv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Entendendo por partes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;cut&lt;/code&gt; é uma ferramenta para “recortar” pedaços de cada linha.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-d','&lt;/code&gt; diz qual é o separador de campos; aqui é vírgula, como em qualquer CSV padrão.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-f1,4&lt;/code&gt; indica que você quer as colunas 1 e 4 (name e department).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Você também pode usar intervalos de colunas: &lt;code&gt;-f1-3&lt;/code&gt; pegaria as colunas de 1 até 3.&lt;/p&gt;

&lt;p&gt;Saída:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;name,department
John Lee,Engineering
Jane Smith,Marketing
   Bob Davis    ,Engineering
Alice Williams,Marketing
Charlie Brown,Sales
Dave Wilson,Engineering
Emma Davis,Marketing
Frank Miller,Sales
John Lee,Engineering
Grace Lee,Engineering
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Isso lembra muito filtrar colunas numa planilha, só que aqui você faz com um comando de uma linha e pode encaixar isso num script reutilizável.&lt;/p&gt;




&lt;h1&gt;
  
  
  3. Removendo linhas duplicadas com &lt;code&gt;sort&lt;/code&gt; e &lt;code&gt;uniq&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;Reparou que “John Lee” aparece duas vezes no arquivo? Em bancos de dados reais isso é clássico: cadastro duplicado de cliente, aluno repetido, funcionário que entrou duas vezes na lista de pagamento.&lt;/p&gt;

&lt;p&gt;Vamos tratar isso.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Salvar o cabeçalho primeiro&lt;/span&gt;
&lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 1 messy_data.csv &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; cleaned_data.csv

&lt;span class="c"&gt;# Remover duplicatas do restante (sem o cabeçalho)&lt;/span&gt;
&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; +2 messy_data.csv | &lt;span class="nb"&gt;sort&lt;/span&gt; | &lt;span class="nb"&gt;uniq&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; cleaned_data.csv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O que cada parte faz?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;head -n 1&lt;/code&gt; pega só a primeira linha, o cabeçalho, e salva em &lt;code&gt;cleaned_data.csv&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tail -n +2&lt;/code&gt; pega tudo a partir da segunda linha, pulando o cabeçalho.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sort&lt;/code&gt; ordena as linhas; isso é importante porque o &lt;code&gt;uniq&lt;/code&gt; só consegue remover duplicatas que estão uma abaixo da outra.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;uniq&lt;/code&gt; elimina linhas repetidas adjacentes.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt; acrescenta o resultado ao final do arquivo (em vez de sobrescrever como &lt;code&gt;&amp;gt;&lt;/code&gt; faria).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;É como pegar uma lista de chamada, ordenar por nome e riscar os duplicados, deixando tudo organizadinho.&lt;/p&gt;




&lt;h1&gt;
  
  
  4. Buscando e filtrando com &lt;code&gt;grep&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;Agora vamos falar de buscas e filtros. Quer localizar só quem está no setor de Engenharia? Ou encontrar linhas com campo vazio? A ferramenta que resolve isso direto no terminal é o &lt;code&gt;grep&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Encontrar todas as linhas de Engenharia&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"Engineering"&lt;/span&gt; messy_data.csv

&lt;span class="c"&gt;# Encontrar linhas com campos vazios (duas vírgulas seguidas)&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;",,"&lt;/span&gt; messy_data.csv

&lt;span class="c"&gt;# Excluir linhas com dados faltando&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;",,"&lt;/span&gt; messy_data.csv &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; no_missing.csv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Como funciona:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;grep "padrão"&lt;/code&gt; procura por linhas que contenham aquele texto.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;grep -v&lt;/code&gt; faz o contrário: mostra só as linhas que &lt;strong&gt;não&lt;/strong&gt; batem com o padrão.&lt;/li&gt;
&lt;li&gt;No caso de &lt;code&gt;",,"&lt;/code&gt;, estamos assumindo que um valor faltando vira duas vírgulas seguidas, algo comum quando um campo num CSV fica em branco.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Isso é muito útil quando você quer, por exemplo, separar uma lista “limpa” de clientes com todos os campos preenchidos e outra lista só com os registros problemáticos para corrigir depois.&lt;/p&gt;




&lt;h1&gt;
  
  
  5. Tirando espaços em excesso com &lt;code&gt;sed&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;Repara no registro de “Bob Davis”: o nome está com espaços sobrando antes e depois. Isso atrapalha na hora de comparar, agrupar ou até de mostrar o nome num relatório bonitinho.&lt;/p&gt;

&lt;p&gt;Vamos remover esse excesso.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'s/^[ \t]*//; s/[ \t]*$//'&lt;/span&gt; messy_data.csv &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; trimmed_data.csv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O que está acontecendo aqui?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;sed&lt;/code&gt; é um “editor de fluxo”, uma ferramenta para editar texto enquanto ele passa pelo comando, sem abrir editor gráfico.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;s/padrão/substituição/&lt;/code&gt; é a sintaxe de substituição.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;^[ \t]*&lt;/code&gt; combina espaços ou tabulações &lt;strong&gt;no começo&lt;/strong&gt; da linha.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;[ \t]*$&lt;/code&gt; combina espaços ou tabulações &lt;strong&gt;no fim&lt;/strong&gt; da linha.&lt;/li&gt;
&lt;li&gt;O ponto e vírgula separa duas operações: primeiro limpamos o começo, depois o final.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Em termos práticos, é como usar “remover espaços extras” numa planilha, só que em qualquer arquivo texto que estiver passando pelo seu pipeline.&lt;/p&gt;




&lt;h1&gt;
  
  
  6. Substituindo valores com &lt;code&gt;sed&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;Às vezes você precisa padronizar termos ou corrigir um erro de digitação. Imagine que o setor “Engineering” precisasse aparecer como “Tech” nos relatórios internos.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Trocar todas as ocorrências de "Engineering" por "Tech"&lt;/span&gt;
&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'s/Engineering/Tech/g'&lt;/span&gt; messy_data.csv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora, vamos supor que você queira preencher e-mails vazios com um valor padrão, do tipo “&lt;a href="mailto:no-email@example.com"&gt;no-email@example.com&lt;/a&gt;”, só para não deixar o campo em branco em nenhuma linha.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Substituir e-mails vazios por "no-email@example.com"&lt;/span&gt;
&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'s/,$/,no-email@example.com/'&lt;/span&gt; messy_data.csv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Entendendo o comando:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;O &lt;code&gt;g&lt;/code&gt; no final significa “global”: substitui todas as ocorrências na linha, não apenas a primeira.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;,$&lt;/code&gt; identifica uma vírgula no final da linha, sinal de que o último campo está vazio.&lt;/li&gt;
&lt;li&gt;Você pode emendar várias substituições usando &lt;code&gt;;&lt;/code&gt; entre elas.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;É como fazer “localizar e substituir” num editor de texto, mas com a vantagem de rodar isso em scripts e automatizar a correção sempre que um arquivo novo chegar.&lt;/p&gt;




&lt;h1&gt;
  
  
  7. Contando e resumindo com &lt;code&gt;awk&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;O &lt;code&gt;awk&lt;/code&gt; é uma espécie de canivete suíço quando você precisa trabalhar com campos, somas e estatísticas simples. Vamos usar para analisar um pouco o nosso arquivo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Contar registros por departamento&lt;/span&gt;
&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; +2 messy_data.csv | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="s1"&gt;','&lt;/span&gt; &lt;span class="nt"&gt;-f4&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; | &lt;span class="nb"&gt;uniq&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt;

&lt;span class="c"&gt;# Calcular idade média (ignorando cabeçalho e valores vazios)&lt;/span&gt;
&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; +2 messy_data.csv | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;&lt;span class="s1"&gt;','&lt;/span&gt; &lt;span class="s1"&gt;'{if($2) sum+=$2; if($2) count++} END {print "Average age:", sum/count}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O que esse &lt;code&gt;awk&lt;/code&gt; faz?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-F','&lt;/code&gt; define a vírgula como separador de campos.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$2&lt;/code&gt; é o segundo campo, nossa coluna de idade.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;if($2)&lt;/code&gt; garante que só vamos considerar linhas em que a idade não está vazia.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sum += $2&lt;/code&gt; vai acumulando as idades.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;count++&lt;/code&gt; conta quantos registros válidos entraram no cálculo.&lt;/li&gt;
&lt;li&gt;O bloco &lt;code&gt;END { ... }&lt;/code&gt; roda depois de passar por todas as linhas e imprime a média.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Saída típica:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;      5 Engineering
      3 Marketing
      2 Sales
Average age: 33
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;É como pedir para o Excel fazer uma Tabela Dinâmica e uma média, só que tudo na linha de comando. Útil pra caramba quando você está no servidor sem interface gráfica ou montando um job automatizado.&lt;/p&gt;




&lt;h1&gt;
  
  
  8. Combinando comandos com pipes
&lt;/h1&gt;

&lt;p&gt;A verdadeira força da linha de comando aparece quando você começa a encadear várias ferramentas usando o famoso &lt;code&gt;|&lt;/code&gt;, o “pipe”. Ele pega a saída de um comando e joga direto como entrada do próximo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Pegar departamentos únicos, em ordem alfabética&lt;/span&gt;
&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; +2 messy_data.csv | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="s1"&gt;','&lt;/span&gt; &lt;span class="nt"&gt;-f4&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; | &lt;span class="nb"&gt;uniq&lt;/span&gt;

&lt;span class="c"&gt;# Encontrar engenheiros com salário &amp;gt; 55000&lt;/span&gt;
&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; +2 messy_data.csv | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"Engineering"&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;&lt;span class="s1"&gt;','&lt;/span&gt; &lt;span class="s1"&gt;'$3 &amp;gt; 55000'&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="s1"&gt;','&lt;/span&gt; &lt;span class="nt"&gt;-f1&lt;/span&gt;,3

&lt;span class="c"&gt;# Contar funcionários por departamento, com contagem&lt;/span&gt;
&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; +2 messy_data.csv | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="s1"&gt;','&lt;/span&gt; &lt;span class="nt"&gt;-f4&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; | &lt;span class="nb"&gt;uniq&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aqui acontece o seguinte:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cada &lt;code&gt;|&lt;/code&gt; passa o resultado do comando anterior para o seguinte.&lt;/li&gt;
&lt;li&gt;Você monta uma espécie de “esteira” de processamento, como linha de produção de fábrica: um comando recorta, outro filtra, outro agrupa, outro ordena.&lt;/li&gt;
&lt;li&gt;No último exemplo, &lt;code&gt;sort -rn&lt;/code&gt; ordena em ordem numérica reversa (dos maiores para os menores).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Saída ilustrativa:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Engineering
Marketing
Sales
   Bob Davis    ,60000
      5 Engineering
      3 Marketing
      2 Sales
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dá para perceber como as peças vão se encaixando? Você começa com um CSV simples e, em poucos comandos, tem um mini–relatório pronto.&lt;/p&gt;




&lt;h1&gt;
  
  
  9. Convertendo formatos de dados
&lt;/h1&gt;

&lt;p&gt;Em alguns momentos você precisa mudar o “jeito” do arquivo, seja para importar em outro sistema, seja para abrir melhor em alguma ferramenta. Um caso comum é trocar vírgulas por tabulações, criando um TSV.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Converter CSV para TSV (tab separado)&lt;/span&gt;
&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'s/,/\t/g'&lt;/span&gt; messy_data.csv &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; data.tsv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora vamos adicionar uma coluna fixa, por exemplo, o ano “2024” para todas as linhas. Pode ser algo como o ano do relatório ou da base de referência.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;&lt;span class="s1"&gt;','&lt;/span&gt; &lt;span class="s1"&gt;'BEGIN{OFS=","} {print $0, "2024"}'&lt;/span&gt; messy_data.csv &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; data_with_year.csv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Detalhando esse &lt;code&gt;awk&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;BEGIN{OFS=","}&lt;/code&gt; define que o separador de saída será vírgula.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$0&lt;/code&gt; representa a linha inteira de entrada.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;print $0, "2024"&lt;/code&gt; imprime a linha original e, na sequência, a nova coluna “2024”.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;É jeito rápido de enriquecer seus dados sem abrir editor algum, como se você estivesse inserindo uma coluna inteira numa planilha com o mesmo valor para todo mundo.&lt;/p&gt;




&lt;h1&gt;
  
  
  10. Montando um pipeline completo de limpeza
&lt;/h1&gt;

&lt;p&gt;Agora vamos juntar tudo em um fluxo só, uma sequência de comandos que pega o arquivo sujo e cospe um arquivo bem mais apresentável.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Salvar o cabeçalho&lt;/span&gt;
&lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 1 messy_data.csv &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; final_clean.csv

&lt;span class="c"&gt;# Limpar os dados: remover duplicatas, aparar espaços, excluir linhas com faltas&lt;/span&gt;
&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; +2 messy_data.csv | &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'s/^[ \t]*//; s/[ \t]*$//'&lt;/span&gt; | &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;",,"&lt;/span&gt; | &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nb"&gt;sort&lt;/span&gt; | &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nb"&gt;uniq&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; final_clean.csv

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Cleaning complete! Check final_clean.csv"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O que esse pipeline faz, na prática?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Garante que o cabeçalho fique preservado, indo direto para &lt;code&gt;final_clean.csv&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Pula o cabeçalho e trabalha só nas linhas de dados.&lt;/li&gt;
&lt;li&gt;Remove espaços extras no começo e no fim de cada linha.&lt;/li&gt;
&lt;li&gt;Joga fora qualquer linha que tenha valores faltando (detectados pelo &lt;code&gt;",,"&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Ordena as linhas e elimina duplicatas.&lt;/li&gt;
&lt;li&gt;Anexa o resultado limpo ao arquivo final.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;É quase como criar um “filtro inteligente” para planilhas, só que em forma de script. Você pode rodar isso todo dia em um diretório de arquivos novos, por exemplo, e ter sempre dados mais organizados antes de mandar para o banco ou para o Python.&lt;/p&gt;




&lt;h1&gt;
  
  
  Encerrando a ideia
&lt;/h1&gt;

&lt;p&gt;Limpar dados na linha de comando é uma habilidade silenciosa, que não aparece em gráfico bonito, mas que faz uma diferença enorme na vida de quem lida com CSV, logs, exportações de sistemas e planilhas bagunçadas. Essas ferramentas são rápidas, confiáveis e convivem muito bem com Python e outras linguagens: você continua usando seus notebooks, só que chega neles com menos sujeira.&lt;/p&gt;

&lt;p&gt;O mais interessante é que tudo isso não serve só para quem se vê como “cientista de dados”. Manipular dados direto no terminal ajuda em engenharia de dados, DevOps, administração de sistemas e qualquer rotina em que você precise mexer com arquivos de texto grandes de forma repetitiva.&lt;/p&gt;

&lt;p&gt;Se você começar a praticar com seus próprios arquivos — lista de clientes, extrato exportado do banco, CSV de vendas da loja online, relatório de sistema acadêmico — vai perceber que muita tarefa chata some da sua rotina. E, com o tempo, vai ter cada vez mais vontade de resolver pequenos problemas de dados com meia dúzia de comandos no terminal em vez de abrir um notebook pesado só para uma limpeza rápida.&lt;/p&gt;

</description>
      <category>python</category>
      <category>datascience</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>CamOver — ferramenta para exploração de vulnerabilidades em câmeras de rede</title>
      <dc:creator>Moprius</dc:creator>
      <pubDate>Wed, 20 Nov 2024 23:27:51 +0000</pubDate>
      <link>https://forem.com/moprius/camover-ferramenta-para-exploracao-de-vulnerabilidades-em-cameras-de-rede-16h8</link>
      <guid>https://forem.com/moprius/camover-ferramenta-para-exploracao-de-vulnerabilidades-em-cameras-de-rede-16h8</guid>
      <description>&lt;p&gt;Hoje vamos falar sobre uma ferramenta interessante: &lt;strong&gt;CamOver&lt;/strong&gt;, utilizada para explorar vulnerabilidades de câmeras de rede, obter suas senhas e realizar diversos tipos de ataques. Os ataques acontecem explorando vulnerabilidades de modelos populares de câmeras, como CCTV, GoAhead e Netwave. Abaixo, explicarei detalhadamente como instalar e usar o CamOver.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Hackeando câmeras de rede com CamOver&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Este artigo é destinado exclusivamente para fins educacionais e para o aprendizado de hackers éticos. O acesso não autorizado a câmeras de rede é ilegal e considerado crime. Nem o site &lt;strong&gt;spy-soft.net&lt;/strong&gt;, nem o autor são responsáveis pelas suas ações.&lt;/p&gt;
&lt;h3&gt;
  
  
  Funcionalidades do CamOver:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Exploração de vulnerabilidades em modelos populares de câmeras de rede (CCTV, GoAhead, Netwave).
&lt;/li&gt;
&lt;li&gt;Suporte a múltiplas câmeras simultaneamente, graças à funcionalidade de multithreading.
&lt;/li&gt;
&lt;li&gt;Interface amigável para uso por linha de comando ou API.
&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;
  
  
  &lt;strong&gt;Instalação do CamOver&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Para instalar a ferramenta, basta usar o seguinte comando:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip3 &lt;span class="nb"&gt;install &lt;/span&gt;git+https://github.com/EntySec/CamOver
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;strong&gt;Uso do CamOver&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Após instalar, basta iniciar o CamOver com o comando:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;camover
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Parâmetros disponíveis ao iniciar o CamOver:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-h, --help            Exibe a mensagem de ajuda e sai.  
-t, --threads         Usa multithreading para acelerar o processo.  
-o OUTPUT, --output OUTPUT  Salva os resultados em um arquivo.  
-i INPUT, --input INPUT  Arquivo com endereços das câmeras.  
-a ADDRESS, --address ADDRESS  Um único endereço de câmera.  
--shodan SHODAN       Chave de API do Shodan para explorar câmeras pela internet.  
--zoomeye ZOOMEYE     Chave de API do ZoomEye para explorar câmeras pela internet.  
-p PAGES, --pages PAGES  Número de páginas a ser buscado no ZoomEye.  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;strong&gt;Exemplo de uso:&lt;/strong&gt;
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Exploração de uma única câmera
&lt;/h4&gt;

&lt;p&gt;Suponha que exista uma câmera com o endereço IP &lt;code&gt;192.168.99.100&lt;/code&gt;. Para verificar se ela pode ser explorada, execute o seguinte comando:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;camover &lt;span class="nt"&gt;-a&lt;/span&gt; 192.168.99.100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h4&gt;
  
  
  Exploração de câmeras pela internet
&lt;/h4&gt;

&lt;p&gt;Para encontrar câmeras pela internet utilizando o Shodan, execute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;camover &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="nt"&gt;--shodan&lt;/span&gt; PSKINdQe1GyxGgecYz2191H2JoS9qvgD
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;A chave de API Shodan (&lt;code&gt;PSKINdQe1GyxGgecYz2191H2JoS9qvgD&lt;/code&gt;) é fornecida como exemplo. Você pode usar essa ou sua própria chave.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h4&gt;
  
  
  Exploração de câmeras a partir de um arquivo
&lt;/h4&gt;

&lt;p&gt;Se você tem uma lista de endereços de câmeras em um arquivo chamado &lt;code&gt;cameras.txt&lt;/code&gt;, pode tentar explorá-las e salvar as senhas obtidas em &lt;code&gt;passwords.txt&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;camover &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; cameras.txt &lt;span class="nt"&gt;-o&lt;/span&gt; passwords.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;strong&gt;Uso de API&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;O CamOver também fornece uma API em Python para integrar a ferramenta em seu código. O exemplo abaixo mostra como criar um objeto CamOver, explorar uma câmera por IP e exibir as credenciais obtidas:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;camover&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;CamOver&lt;/span&gt;

&lt;span class="n"&gt;camover&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CamOver&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;creds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;camover&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exploit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;192.168.99.100&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;creds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Explicação do código:
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;from camover import CamOver&lt;/code&gt;: Importa a classe CamOver da biblioteca.
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;camover = CamOver()&lt;/code&gt;: Cria um objeto CamOver para acessar seus métodos.
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;creds = camover.exploit('192.168.99.100')&lt;/code&gt;: Usa o método &lt;code&gt;exploit&lt;/code&gt; para tentar explorar uma câmera pelo endereço IP &lt;code&gt;192.168.99.100&lt;/code&gt;. Se for bem-sucedido, retorna as credenciais (login e senha) da câmera.
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;print(creds)&lt;/code&gt;: Exibe as credenciais obtidas.
&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Conclusão&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;O CamOver é uma ferramenta poderosa para explorar vulnerabilidades em câmeras de rede. Caso você se interesse por esse tema, ele pode ser um recurso valioso para aprendizado sobre segurança de redes e testes de penetração.  &lt;/p&gt;




&lt;p&gt;⚠️ &lt;strong&gt;Aviso legal&lt;/strong&gt;: O uso de ferramentas como o CamOver para atividades não autorizadas é crime e pode acarretar sérias penalidades. Utilize essas informações apenas para fins legais e éticos.&lt;/p&gt;

</description>
      <category>python</category>
      <category>security</category>
      <category>network</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Substituição e Correção de Palavras com NLTK em Python</title>
      <dc:creator>Moprius</dc:creator>
      <pubDate>Wed, 31 Jul 2024 15:29:07 +0000</pubDate>
      <link>https://forem.com/moprius/substituicao-e-correcao-de-palavras-com-nltk-em-python-173n</link>
      <guid>https://forem.com/moprius/substituicao-e-correcao-de-palavras-com-nltk-em-python-173n</guid>
      <description>&lt;p&gt;Quando a gente fala de processamento de linguagem natural (PLN), uma das tarefas mais importantes é a substituição e correção de palavras. Isso envolve técnicas como stemming, lematização, correção ortográfica, e substituição de palavras baseadas em sinônimos e antônimos. Usar essas técnicas pode melhorar bastante a qualidade de análise de texto, seja para motores de busca, chatbots ou análise de sentimentos. Vamos explorar como a biblioteca NLTK em Python ajuda nessas tarefas.&lt;/p&gt;

&lt;h4&gt;
  
  
  Stemming: Cortando Sufixos
&lt;/h4&gt;

&lt;p&gt;Stemming é uma técnica que remove os sufixos das palavras, deixando só a raiz. Por exemplo, a palavra "correndo" tem a raiz "corr". Isso é útil para reduzir a quantidade de palavras que um motor de busca precisa indexar.&lt;/p&gt;

&lt;p&gt;No NLTK, a gente pode usar o &lt;code&gt;PorterStemmer&lt;/code&gt; para fazer stemming. Vamos ver como funciona:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;nltk.stem&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;PorterStemmer&lt;/span&gt;

&lt;span class="n"&gt;stemmer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PorterStemmer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stemmer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;correndo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;# Saída: corr
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stemmer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;correção&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;# Saída: correc
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aqui, a gente viu que o stemming corta os sufixos e deixa só a raiz das palavras. Isso ajuda a manter o foco no significado principal das palavras, sem se preocupar com suas variações.&lt;/p&gt;

&lt;h4&gt;
  
  
  Lemmatização: Voltando à Forma Base
&lt;/h4&gt;

&lt;p&gt;A lematização é parecida com o stemming, mas ao invés de cortar sufixos, ela converte a palavra para a sua forma base, ou lemma. Por exemplo, "correndo" vira "correr". Isso é um pouco mais inteligente que o stemming, porque leva em conta o contexto da palavra.&lt;/p&gt;

&lt;p&gt;Para fazer lematização no NLTK, a gente usa o &lt;code&gt;WordNetLemmatizer&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;nltk.stem&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;WordNetLemmatizer&lt;/span&gt;

&lt;span class="n"&gt;lemmatizer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;WordNetLemmatizer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lemmatizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lemmatize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;correndo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;v&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;# Saída: correr
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lemmatizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lemmatize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;correções&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;# Saída: correção
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nesse exemplo, a gente usa a função &lt;code&gt;lemmatize&lt;/code&gt; e, pra verbos, a gente especifica a parte do discurso (pos) como 'v'. Isso ajuda a NLTK a entender melhor o contexto da palavra.&lt;/p&gt;

&lt;h4&gt;
  
  
  Expressões Regulares para Substituição
&lt;/h4&gt;

&lt;p&gt;Às vezes, a gente quer substituir palavras específicas ou padrões no texto. Pra isso, expressões regulares (regex) são muito úteis. Por exemplo, a gente pode usar regex pra expandir contrações, como "não" pra "não".&lt;/p&gt;

&lt;p&gt;Aqui está como a gente pode fazer isso com NLTK:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;

&lt;span class="n"&gt;texto&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Eu não posso ir à festa. Você não vai?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;expansoes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;não&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;não&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;expandir_contracoes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;texto&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expansoes&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;contraido&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expandido&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;expansoes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;texto&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;\b&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;contraido&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;\b&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expandido&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;texto&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;texto&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;expandir_contracoes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;texto&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expansoes&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;# Saída: Eu não posso ir à festa. Você não vai?
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nesse exemplo, a função &lt;code&gt;expandir_contracoes&lt;/code&gt; usa regex pra encontrar e substituir palavras contraídas no texto.&lt;/p&gt;

&lt;h4&gt;
  
  
  Correção Ortográfica com Enchant
&lt;/h4&gt;

&lt;p&gt;Outra tarefa importante é a correção ortográfica. Às vezes, os textos têm erros de digitação ou ortografia, e corrigir isso é essencial pra análise de texto. A biblioteca &lt;code&gt;pyenchant&lt;/code&gt; é ótima pra isso.&lt;/p&gt;

&lt;p&gt;Primeiro, a gente precisa instalar a biblioteca &lt;code&gt;pyenchant&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;pyenchant
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Depois, a gente pode usar o Enchant pra corrigir palavras:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;enchant&lt;/span&gt;

&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;enchant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pt_BR&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;palavra&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;corrigindo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;palavra&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;palavra&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; está correta&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;palavra&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; está incorreta, sugestões: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;suggest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;palavra&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Se a palavra estiver incorreta, o Enchant sugere correções.&lt;/p&gt;

&lt;h4&gt;
  
  
  Substituição de Sinônimos
&lt;/h4&gt;

&lt;p&gt;Substituir palavras por seus sinônimos pode enriquecer um texto, evitando repetições e melhorando o estilo. Com o WordNet, a gente pode encontrar sinônimos facilmente.&lt;/p&gt;

&lt;p&gt;Aqui está como a gente pode fazer isso:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;nltk.corpus&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;wordnet&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;substituir_sinonimos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;palavra&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;sinonimos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;syn&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;wordnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;synsets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;palavra&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;por&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;lemma&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;syn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lemmas&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="n"&gt;sinonimos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lemma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sinonimos&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;substituir_sinonimos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bom&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;# Saída: {'bom', 'legal', 'ótimo', 'excelente'}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nesse exemplo, a função &lt;code&gt;substituir_sinonimos&lt;/code&gt; retorna uma lista de sinônimos pra palavra dada.&lt;/p&gt;

&lt;h4&gt;
  
  
  Substituição de Antônimos
&lt;/h4&gt;

&lt;p&gt;Assim como sinônimos, antônimos também são úteis, especialmente pra tarefas como análise de sentimentos. A gente pode usar o WordNet pra encontrar antônimos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;substituir_antonimos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;palavra&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;antonimos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;syn&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;wordnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;synsets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;palavra&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;por&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;lemma&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;syn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lemmas&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;lemma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;antonyms&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
                &lt;span class="n"&gt;antonimos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lemma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;antonyms&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;antonimos&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;substituir_antonimos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bom&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;# Saída: {'mau', 'ruim'}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Essa função encontra antônimos pra palavra dada.&lt;/p&gt;

&lt;h4&gt;
  
  
  Aplicações Práticas
&lt;/h4&gt;

&lt;p&gt;Vamos ver algumas aplicações práticas dessas técnicas.&lt;/p&gt;

&lt;h5&gt;
  
  
  Análise de Sentimentos
&lt;/h5&gt;

&lt;p&gt;A análise de sentimentos envolve determinar a polaridade (positiva, negativa ou neutra) de um texto. Substituição de palavras pode melhorar essa análise.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;texto&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Eu adorei o filme, mas a comida estava ruim.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;palavras&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;word_tokenize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;texto&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;portuguese&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;polaridade&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;palavra&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;palavras&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;sinsets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;wordnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;synsets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;palavra&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;por&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;sinsets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;syn&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;sinsets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;polaridade&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;syn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pos_score&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;syn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;neg_score&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Polaridade do texto:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;polaridade&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Saída: Polaridade do texto: 0.25 (por exemplo)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  Normalização de Texto
&lt;/h5&gt;

&lt;p&gt;A normalização de texto envolve transformar o texto em uma forma consistente. Isso pode incluir a correção ortográfica, remoção de stopwords, e substituição de sinônimos.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;stopwords&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stopwords&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;words&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;portuguese&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;texto&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;A análise de textos é uma área fascinante do PLN.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;palavras&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;word_tokenize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;texto&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;portuguese&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;palavras_filtradas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;palavras&lt;/span&gt; &lt;span class="n"&gt;se&lt;/span&gt; &lt;span class="n"&gt;não&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;stopwords&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;texto_normalizado&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;palavras_filtradas&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;texto_normalizado&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Saída: "análise textos área fascinante PLN"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  Melhoria da Busca em Textos
&lt;/h5&gt;

&lt;p&gt;Em motores de busca, a substituição de sinônimos pode melhorar os resultados da busca, encontrando documentos que usam sinônimos das palavras-chave buscadas.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;consulta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bom filme&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;consulta_expandidas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;palavra&lt;/span&gt; &lt;span class="n"&gt;em&lt;/span&gt; &lt;span class="n"&gt;consulta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;sinonimos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;substituir_sinonimos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;palavra&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;consulta_expandidas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sinonimos&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Consulta expandida:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;consulta_expandidas&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;# Saída: "bom legal ótimo excelente filme"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Conclusão
&lt;/h3&gt;

&lt;p&gt;Neste texto, exploramos várias técnicas de substituição e correção de palavras usando a biblioteca NLTK em Python. Vimos como fazer stemming, lematização, usar expressões regulares para substituir palavras, correção ortográfica com Enchant, e substituição de sinônimos e antônimos com o WordNet. Também discutimos aplicações práticas dessas técnicas em análise de sentimentos, normalização de texto e motores de busca.&lt;/p&gt;

&lt;p&gt;O uso dessas técnicas pode melhorar significativamente a qualidade da análise de texto, tornando os resultados mais precisos e relevantes. O NLTK oferece uma gama poderosa de ferramentas para quem trabalha com processamento de linguagem natural, e entender como utilizar essas ferramentas é essencial para qualquer projeto de PLN.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>python</category>
      <category>nltk</category>
      <category>nlp</category>
    </item>
    <item>
      <title>Introdução à Tokenização e Básicos do WordNet com Python e NLTK</title>
      <dc:creator>Moprius</dc:creator>
      <pubDate>Wed, 31 Jul 2024 15:21:09 +0000</pubDate>
      <link>https://forem.com/moprius/introducao-a-tokenizacao-e-basicos-do-wordnet-com-python-e-nltk-2ip5</link>
      <guid>https://forem.com/moprius/introducao-a-tokenizacao-e-basicos-do-wordnet-com-python-e-nltk-2ip5</guid>
      <description>&lt;p&gt;O processamento de linguagem natural (PLN) é um campo fascinante que combina linguística e computação para entender, interpretar e manipular a linguagem humana. Uma das ferramentas mais poderosas para isso é a Natural Language Toolkit (NLTK) em Python. Neste texto, vamos explorar os conceitos de tokenização e o uso do WordNet, uma base lexical para a língua inglesa, que é amplamente utilizada em PLN. &lt;/p&gt;

&lt;h4&gt;
  
  
  O que é Tokenização?
&lt;/h4&gt;

&lt;p&gt;Tokenização é o processo de dividir um texto em unidades menores, chamadas tokens. Esses tokens podem ser palavras, frases ou até mesmo caracteres individuais. A tokenização é um passo crucial no processamento de textos porque permite que os algoritmos compreendam e analisem o texto de forma mais eficaz.&lt;/p&gt;

&lt;p&gt;Por exemplo, considere a frase "Olá, mundo!". A tokenização dessa frase pode resultar em três tokens: ["Olá", ",", "mundo", "!"]. Essa divisão permite que cada parte do texto seja analisada individualmente, facilitando tarefas como análise de sentimentos, tradução automática e reconhecimento de entidades nomeadas.&lt;/p&gt;

&lt;p&gt;No NLTK, a tokenização pode ser feita de várias maneiras. Vamos ver alguns exemplos práticos.&lt;/p&gt;

&lt;h4&gt;
  
  
  Tokenizando Textos em Sentenças
&lt;/h4&gt;

&lt;p&gt;Dividir um texto em sentenças é o primeiro passo em muitas tarefas de PLN. O NLTK facilita isso com a função &lt;code&gt;sent_tokenize&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;nltk&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;nltk.tokenize&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sent_tokenize&lt;/span&gt;

&lt;span class="n"&gt;texto&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Olá mundo! Bem-vindo ao tutorial de NLTK. Vamos aprender a tokenizar textos.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;sentencas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sent_tokenize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;texto&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;portuguese&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sentencas&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O resultado será:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;['Olá mundo!', 'Bem-vindo ao tutorial de NLTK.', 'Vamos aprender a tokenizar textos.']
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aqui, o texto foi dividido em três sentenças. Isso é útil para análises mais detalhadas, onde cada sentença pode ser processada individualmente.&lt;/p&gt;

&lt;h4&gt;
  
  
  Tokenizando Sentenças em Palavras
&lt;/h4&gt;

&lt;p&gt;Depois de dividir o texto em sentenças, o próximo passo geralmente é dividir essas sentenças em palavras. A função &lt;code&gt;word_tokenize&lt;/code&gt; do NLTK é usada para isso.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;nltk.tokenize&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;word_tokenize&lt;/span&gt;

&lt;span class="n"&gt;frase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Olá mundo!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;palavras&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;word_tokenize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;portuguese&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;palavras&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O resultado será:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;['Olá', 'mundo', '!']
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora, temos cada palavra e símbolo de pontuação como tokens separados. Isso é essencial para tarefas como análise de frequência de palavras, onde precisamos contar quantas vezes cada palavra aparece em um texto.&lt;/p&gt;

&lt;h4&gt;
  
  
  Usando Expressões Regulares para Tokenização
&lt;/h4&gt;

&lt;p&gt;Em alguns casos, você pode querer uma tokenização mais personalizada. As expressões regulares (regex) são uma ferramenta poderosa para isso. O NLTK fornece a classe &lt;code&gt;RegexpTokenizer&lt;/code&gt; para criar tokenizadores personalizados.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;nltk.tokenize&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;RegexpTokenizer&lt;/span&gt;

&lt;span class="n"&gt;tokenizer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RegexpTokenizer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;\w+&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tokenize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Vamos aprender NLTK.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O resultado será:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;['Vamos', 'aprender', 'NLTK']
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aqui, usamos uma expressão regular que seleciona apenas palavras compostas por caracteres alfanuméricos, ignorando a pontuação.&lt;/p&gt;

&lt;h4&gt;
  
  
  Introdução ao WordNet
&lt;/h4&gt;

&lt;p&gt;O WordNet é uma base de dados lexical que agrupa palavras em conjuntos de sinônimos chamados synsets, fornece definições curtas e gerais, e registra várias relações semânticas entre essas palavras. No NLTK, o WordNet é utilizado para encontrar sinônimos, antônimos, hipônimos e hiperônimos, entre outras relações.&lt;/p&gt;

&lt;p&gt;Para usar o WordNet, precisamos importar o módulo &lt;code&gt;wordnet&lt;/code&gt; do NLTK.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;nltk.corpus&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;wordnet&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Buscando Synsets
&lt;/h4&gt;

&lt;p&gt;Um synset, ou conjunto de sinônimos, é um grupo de palavras que compartilham o mesmo significado. Para buscar os synsets de uma palavra, usamos a função &lt;code&gt;synsets&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;sinonimos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;wordnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;synsets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dog&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sinonimos&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O resultado será uma lista de synsets que representam diferentes sentidos da palavra "dog".&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;Synset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dog.n.01&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nc"&gt;Synset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;frump.n.01&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nc"&gt;Synset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dog.n.03&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nc"&gt;Synset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cad.n.01&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nc"&gt;Synset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;frank.n.02&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nc"&gt;Synset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;pawl.n.01&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nc"&gt;Synset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;andiron.n.01&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cada synset é identificado por um nome que inclui a palavra, a parte do discurso (n para substantivo, v para verbo, etc.), e um número que distingue diferentes sentidos.&lt;/p&gt;

&lt;h4&gt;
  
  
  Definições e Exemplos
&lt;/h4&gt;

&lt;p&gt;Podemos obter a definição e exemplos de uso de um synset específico.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;sinonimo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;wordnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;synset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dog.n.01&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sinonimo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;definition&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sinonimo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;examples&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O resultado será:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;a domesticated carnivorous mammal (Canis familiaris) that typically has a long snout, an acute sense of smell, non-retractile claws, and a barking, howling, or whining voice
['the dog barked all night']
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Isso nos dá uma compreensão clara do significado e do uso de "dog" neste contexto.&lt;/p&gt;

&lt;h4&gt;
  
  
  Buscando Sinônimos e Antônimos
&lt;/h4&gt;

&lt;p&gt;Para encontrar sinônimos e antônimos de uma palavra, podemos explorar os lemmas dos synsets.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;sinonimos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="n"&gt;antonimos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;syn&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;wordnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;synsets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;good&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;lemma&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;syn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lemmas&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;sinonimos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lemma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;lemma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;antonyms&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="n"&gt;antonimos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lemma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;antonyms&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sinonimos&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;antonimos&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O resultado será uma lista de sinônimos e antônimos para a palavra "good".&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;skillful&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;proficient&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;practiced&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;unspoiled&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;goodness&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;good&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dependable&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sound&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;right&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;safe&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;respectable&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;effective&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;trade_good&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;adept&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;good&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;full&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;commodity&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;estimable&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;honorable&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;undecomposed&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;serious&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;secure&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dear&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ripe&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;evilness&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;evil&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ill&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Calculando Similaridade Semântica
&lt;/h4&gt;

&lt;p&gt;O WordNet também permite calcular a similaridade semântica entre palavras. A similaridade é baseada na distância entre os synsets no gráfico de hipônimos/hiperônimos.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;nltk.corpus&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;wordnet&lt;/span&gt;

&lt;span class="n"&gt;cachorro&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;wordnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;synset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dog.n.01&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;gato&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;wordnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;synset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cat.n.01&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;similaridade&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cachorro&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wup_similarity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gato&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;similaridade&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O resultado será um valor de similaridade entre 0 e 1.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0.8571428571428571
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esse valor indica que "dog" e "cat" são bastante similares semanticamente.&lt;/p&gt;

&lt;h4&gt;
  
  
  Filtrando Stopwords
&lt;/h4&gt;

&lt;p&gt;Stopwords são palavras comuns que geralmente não adicionam muito significado ao texto, como "e", "a", "de". Remover essas palavras pode ajudar a focar nas partes mais importantes do texto. O NLTK fornece uma lista de stopwords para várias línguas.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;nltk.corpus&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;stopwords&lt;/span&gt;

&lt;span class="n"&gt;stop_words&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stopwords&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;words&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;portuguese&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;palavras&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Olá&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mundo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;é&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;um&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lugar&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bonito&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;palavras_filtradas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;palavras&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;stop_words&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;palavras_filtradas&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O resultado será:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;['Olá', 'mundo', 'lugar', 'bonito']
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aqui, as stopwords foram removidas da lista original de palavras.&lt;/p&gt;

&lt;h3&gt;
  
  
  Aplicações Práticas
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Análise de Sentimentos
&lt;/h4&gt;

&lt;p&gt;A análise de sentimentos é uma aplicação comum de PLN onde o objetivo é determinar a opinião ou emoção expressa em um texto. Tokenização e o uso de WordNet são passos importantes nesse processo.&lt;/p&gt;

&lt;p&gt;Primeiro, dividimos o texto em palavras e removemos as stopwords. Em seguida, podemos usar os synsets para entender melhor o contexto e a polaridade das palavras.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;texto&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Eu amo programação em Python!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;palavras&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;word_tokenize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;texto&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;portuguese&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;palavras_filtradas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;palavras&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;stop_words&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;polaridade&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;palavra&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;palavras_filtradas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;synsets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;wordnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;synsets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;palavra&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;por&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;synsets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;syn&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;synsets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;polaridade&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;syn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pos_score&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;syn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;neg_score&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Polaridade do texto:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;polaridade&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nesse exemplo simplificado, estamos somando os scores positivos e negativos dos synsets das palavras filtradas para determinar a polaridade geral do texto.&lt;/p&gt;

&lt;h4&gt;
  
  
  Reconhecimento de Entidades Nomeadas
&lt;/h4&gt;

&lt;p&gt;Outra aplicação é o reconhecimento de entidades nomeadas (NER), que identifica e classifica nomes de pessoas, organizações, locais, etc., em um texto.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;nltk&lt;/span&gt;
&lt;span class="n"&gt;nltk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;download&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;maxent_ne_chunker&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;nltk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;download&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;words&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;frase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Barack Obama foi o 44º presidente dos Estados Unidos.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;palavras&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;word_tokenize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;portuguese&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nltk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pos_tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;palavras&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;entidades&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nltk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ne_chunk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entidades&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O resultado será uma árvore que identifica "Barack Obama" como uma pessoa e "Estados Unidos" como um local.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusão
&lt;/h3&gt;

&lt;p&gt;Neste texto, exploramos os conceitos básicos de tokenização e uso do WordNet com a biblioteca NLTK em Python. Vimos como dividir textos em sentenças e palavras, como buscar sinônimos e antônimos, calcular similaridades semânticas, e aplicações práticas como análise de sentimentos e reconhecimento de entidades nomeadas. A NLTK é uma ferramenta poderosa para qualquer pessoa interessada em processamento de linguagem natural, oferecendo uma ampla gama de funcionalidades para transformar e analisar textos de forma eficaz.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>python</category>
      <category>nltk</category>
      <category>nlp</category>
    </item>
    <item>
      <title>Como Escanear Portas em um Website com Python</title>
      <dc:creator>Moprius</dc:creator>
      <pubDate>Thu, 27 Jun 2024 00:05:12 +0000</pubDate>
      <link>https://forem.com/moprius/como-escanear-portas-em-um-website-com-python-bdm</link>
      <guid>https://forem.com/moprius/como-escanear-portas-em-um-website-com-python-bdm</guid>
      <description>&lt;p&gt;Você já deve ter ouvido falar do Nmap e de escaneamento de portas em servidores, bem, nesse script feito em Python vamos fazer algo bem semelhante, vamos verificar as portas abertas em websites. Vamos explorar um pouco de maneira simples e fácil de entender&lt;/p&gt;

&lt;h2&gt;
  
  
  Introdução
&lt;/h2&gt;

&lt;p&gt;Portas abertas em um servidor são como portas de entrada para diferentes serviços. Saber quais portas estão abertas pode ajudar você a entender melhor a segurança do seu site ou simplesmente satisfazer sua curiosidade sobre o funcionamento interno de um site. Vamos mergulhar em um script que escaneia essas portas usando Python.&lt;/p&gt;

&lt;h2&gt;
  
  
  Código completo
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import socket
import argparse
from concurrent.futures import ThreadPoolExecutor

# Função para verificar uma única porta
def scan_port(host, port):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.settimeout(1)  # Define um timeout de 1 segundo
    try:
        sock.connect((host, port))
        return port, True
    except (socket.timeout, socket.error):
        return port, False
    finally:
        sock.close()

# Função para escanear uma lista de portas
def scan_ports(host, ports, max_workers=100):
    open_ports = []
    with ThreadPoolExecutor(max_workers=max_workers) as executor:
        futures = [executor.submit(scan_port, host, port) for port in ports]
        for future in futures:
            port, is_open = future.result()
            if is_open:
                open_ports.append(port)
    return open_ports

# Mapear portas para serviços comuns
port_service_map = {
    21: 'ftp', 22: 'ssh', 23: 'telnet', 25: 'smtp', 53: 'domain', 80: 'http',
    110: 'pop3', 123: 'ntp', 135: 'msrpc', 139: 'netbios-ssn', 143: 'imap',
    161: 'snmp', 194: 'irc', 389: 'ldap', 443: 'https', 445: 'microsoft-ds',
    465: 'smtps', 512: 'exec', 513: 'login', 514: 'shell', 587: 'submission',
    636: 'ldaps', 873: 'rsync', 990: 'ftps', 993: 'imaps', 995: 'pop3s',
    1080: 'socks', 1194: 'openvpn', 1433: 'ms-sql-s', 1434: 'ms-sql-m',
    1521: 'oracle', 1723: 'pptp', 3306: 'mysql', 3389: 'ms-wbt-server',
    5060: 'sip', 5432: 'postgresql', 5900: 'vnc', 5984: 'couchdb', 6379: 'redis',
    6667: 'irc', 8000: 'http-alt', 8080: 'http-proxy', 8443: 'https-alt',
    8888: 'sun-answerbook', 9000: 'cslistener', 9200: 'wap-wsp', 10000: 'webmin',
    11211: 'memcached', 27017: 'mongodb'
}

def main():
    # Configurar o parser de argumentos
    parser = argparse.ArgumentParser(description="Scan ports on a specified website")
    parser.add_argument("website", help="The website to scan, e.g., www.example.com")
    args = parser.parse_args()

    # Obter o site a partir dos argumentos
    website = args.website

    # Definir as portas a serem escaneadas (ports mais comuns escaneadas pelo nmap)
    ports_to_scan = list(port_service_map.keys())

    # Obter o endereço IP do site
    try:
        host = socket.gethostbyname(website)
    except socket.gaierror:
        print(f"Não foi possível resolver o hostname: {website}")
        return

    print(f"Iniciando escaneamento de {website} ({host})...")

    # Escanear as portas e exibir as portas abertas
    open_ports = scan_ports(host, ports_to_scan)

    print(f"PORTA     ESTADO       SERVIÇO")
    for port in ports_to_scan:
        state = "aberta" if port in open_ports else "fechada"
        service = port_service_map.get(port, "unknown")
        print(f"{port:&amp;lt;9} {state:&amp;lt;12} {service}")

    print("Escaneamento concluído.")

if __name__ == "__main__":
    main()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explicação do Script
&lt;/h3&gt;

&lt;p&gt;Nosso script utiliza algumas bibliotecas essenciais do Python: &lt;code&gt;socket&lt;/code&gt;, &lt;code&gt;argparse&lt;/code&gt; e &lt;code&gt;concurrent.futures.ThreadPoolExecutor&lt;/code&gt;. Aqui está um passo a passo do que cada parte do script faz:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Importações e Configurações Iniciais&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import socket
import argparse
from concurrent.futures import ThreadPoolExecutor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esses comandos importam os módulos necessários para o nosso script. &lt;code&gt;socket&lt;/code&gt; é usado para criar conexões de rede, &lt;code&gt;argparse&lt;/code&gt; para lidar com argumentos de linha de comando, e &lt;code&gt;concurrent.futures.ThreadPoolExecutor&lt;/code&gt; para executar tarefas em paralelo, aumentando a eficiência do nosso escaneamento de portas.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Função para Verificar uma Porta&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def scan_port(host, port):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.settimeout(1)  # Define um timeout de 1 segundo
    try:
        sock.connect((host, port))
        return port, True
    except (socket.timeout, socket.error):
        return port, False
    finally:
        sock.close()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A função &lt;code&gt;scan_port&lt;/code&gt; tenta conectar a uma porta específica em um host. Se a conexão for bem-sucedida, a porta está aberta; caso contrário, está fechada. O timeout de 1 segundo garante que a tentativa de conexão não demore muito.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Função para Escanear Múltiplas Portas&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def scan_ports(host, ports, max_workers=100):
    open_ports = []
    with ThreadPoolExecutor(max_workers=max_workers) as executor:
        futures = [executor.submit(scan_port, host, port) for port in ports]
        for future in futures:
            port, is_open = future.result()
            if is_open:
                open_ports.append(port)
    return open_ports
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A função &lt;code&gt;scan_ports&lt;/code&gt; utiliza &lt;code&gt;ThreadPoolExecutor&lt;/code&gt; para escanear várias portas ao mesmo tempo. Ela cria uma lista de tarefas, cada uma verificando uma porta diferente, e armazena as portas abertas em uma lista.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Mapeamento de Portas para Serviços Comuns&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;port_service_map = {
    21: 'ftp', 22: 'ssh', 23: 'telnet', 25: 'smtp', 53: 'domain', 80: 'http',
    110: 'pop3', 123: 'ntp', 135: 'msrpc', 139: 'netbios-ssn', 143: 'imap',
    161: 'snmp', 194: 'irc', 389: 'ldap', 443: 'https', 445: 'microsoft-ds',
    465: 'smtps', 512: 'exec', 513: 'login', 514: 'shell', 587: 'submission',
    636: 'ldaps', 873: 'rsync', 990: 'ftps', 993: 'imaps', 995: 'pop3s',
    1080: 'socks', 1194: 'openvpn', 1433: 'ms-sql-s', 1434: 'ms-sql-m',
    1521: 'oracle', 1723: 'pptp', 3306: 'mysql', 3389: 'ms-wbt-server',
    5060: 'sip', 5432: 'postgresql', 5900: 'vnc', 5984: 'couchdb', 6379: 'redis',
    6667: 'irc', 8000: 'http-alt', 8080: 'http-proxy', 8443: 'https-alt',
    8888: 'sun-answerbook', 9000: 'cslistener', 9200: 'wap-wsp', 10000: 'webmin',
    11211: 'memcached', 27017: 'mongodb'
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aqui, temos um dicionário que mapeia números de portas para seus serviços comuns, como FTP, SSH e HTTP. Isso ajuda a identificar rapidamente quais serviços estão rodando nas portas abertas.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Função Principal&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def main():
    # Configurar o parser de argumentos
    parser = argparse.ArgumentParser(description="Scan ports on a specified website")
    parser.add_argument("website", help="The website to scan, e.g., www.example.com")
    args = parser.parse_args()

    # Obter o site a partir dos argumentos
    website = args.website

    # Definir as portas a serem escaneadas (ports mais comuns escaneadas pelo nmap)
    ports_to_scan = list(port_service_map.keys())

    # Obter o endereço IP do site
    try:
        host = socket.gethostbyname(website)
    except socket.gaierror:
        print(f"Não foi possível resolver o hostname: {website}")
        return

    print(f"Iniciando escaneamento de {website} ({host})...")

    # Escanear as portas e exibir as portas abertas
    open_ports = scan_ports(host, ports_to_scan)

    print(f"PORTA     ESTADO       SERVIÇO")
    for port in ports_to_scan:
        state = "aberta" if port in open_ports else "fechada"
        service = port_service_map.get(port, "unknown")
        print(f"{port:&amp;lt;9} {state:&amp;lt;12} {service}")

    print("Escaneamento concluído.")

if __name__ == "__main__":
    main()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A função &lt;code&gt;main&lt;/code&gt; faz todo o trabalho de configurar o escaneamento. Primeiro, ela define os argumentos de linha de comando e obtém o website a ser escaneado. Depois, ela resolve o nome do host para um endereço IP e começa a escanear as portas mais comuns. Finalmente, ela exibe o estado de cada porta (aberta ou fechada) junto com o serviço correspondente.&lt;/p&gt;

&lt;h3&gt;
  
  
  Exemplo:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[user@hostname ]$ python scan.py www.google.com  
Iniciando escaneamento de www.google.com (142.250.79.4)...  
PORTA     ESTADO       SERVIÇO  
21        fechada      ftp  
22        fechada      ssh  
23        fechada      telnet  
25        fechada      smtp  
53        fechada      domain  
80        aberta       http  
110       fechada      pop3  
123       fechada      ntp  
135       fechada      msrpc  
139       fechada      netbios-ssn  
143       fechada      imap  
161       fechada      snmp  
194       fechada      irc  
389       fechada      ldap  
443       aberta       https  
445       fechada      microsoft-ds  
465       fechada      smtps  
512       fechada      exec  
513       fechada      login  
514       fechada      shell  
587       fechada      submission  
636       fechada      ldaps  
873       fechada      rsync  
990       fechada      ftps  
993       fechada      imaps  
995       fechada      pop3s  
1080      fechada      socks  
1194      fechada      openvpn  
1433      fechada      ms-sql-s  
1434      fechada      ms-sql-m  
1521      fechada      oracle  
1723      fechada      pptp  
3306      fechada      mysql  
3389      fechada      ms-wbt-server  
5060      fechada      sip  
5432      fechada      postgresql  
5900      fechada      vnc  
5984      fechada      couchdb  
6379      fechada      redis  
6667      fechada      irc  
8000      fechada      http-alt  
8080      fechada      http-proxy  
8443      fechada      https-alt  
8888      fechada      sun-answerbook  
9000      fechada      cslistener  
9200      fechada      wap-wsp  
10000     fechada      webmin  
11211     fechada      memcached  
27017     fechada      mongodb  
Escaneamento concluído.  
[user@hostname ]$
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Considerações finais
&lt;/h3&gt;

&lt;p&gt;Este script em Python é uma maneira simples e prática de verificar quais portas estão abertas em um site, o que pode ser útil para fins de segurança ou simples curiosidade.. Sinta-se à vontade para personalizar e expandir este script conforme suas necessidades e busque aprender mais sobre o mundo da programação de redes.&lt;/p&gt;

</description>
      <category>python</category>
      <category>website</category>
      <category>network</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Webview em QML (Qt Modeling Language)</title>
      <dc:creator>Moprius</dc:creator>
      <pubDate>Wed, 26 Jun 2024 22:06:05 +0000</pubDate>
      <link>https://forem.com/moprius/webview-em-qml-qt-modeling-language-67k</link>
      <guid>https://forem.com/moprius/webview-em-qml-qt-modeling-language-67k</guid>
      <description>&lt;h2&gt;
  
  
  Introdução
&lt;/h2&gt;

&lt;p&gt;O código a seguir é um exemplo de uma aplicação básica em QML (Qt Modeling Language) que cria uma janela de aplicação que incorpora um visualizador de web (WebEngineView) para exibir a página do Google. Ele inclui funcionalidades como um menu de contexto, ícone de bandeja do sistema (SystemTrayIcon), e um histórico de URLs visitadas. A aplicação também possui vários itens de menu para interações adicionais, como copiar, colar, cortar, selecionar tudo, atualizar, zoom in, zoom out, e abrir o URL em um navegador externo. Vamos as explicação do código&lt;/p&gt;

&lt;h3&gt;
  
  
  Código completo
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import QtWebEngine 1.10
import Qt.labs.platform 1.1

ApplicationWindow {
    id: window
    visible: true
    width: 1024
    height: 768
    title: "Google"

    WebEngineView {
        id: webEngineView
        anchors.fill: parent
        url: "https://www.google.com"

        onUrlChanged: {
            historyModel.append({url: webEngineView.url.toString()})
        }
    }

    ListModel {
        id: historyModel
    }

    ListView {
        id: historyView
        width: parent.width
        height: 200
        model: historyModel
        delegate: Text {
            text: url
        }
        visible: false // Defina como true para visualizar o histórico
    }

    MouseArea {
        anchors.fill: parent
        acceptedButtons: Qt.RightButton
        onPressed: {
            if (mouse.button == Qt.RightButton) {
                contextMenu.open(mouse.x, mouse.y)
            }
        }
    }

    Menu {
        id: contextMenu
        MenuItem {
            text: qsTr("Copy")
            onTriggered: webEngineView.triggerWebAction(WebEngineView.Copy)
        }
        MenuItem {
            text: qsTr("Paste")
            onTriggered: webEngineView.triggerWebAction(WebEngineView.Paste)
        }
        MenuItem {
            text: qsTr("Cut")
            onTriggered: webEngineView.triggerWebAction(WebEngineView.Cut)
        }
        MenuItem {
            text: qsTr("Select All")
            onTriggered: webEngineView.triggerWebAction(WebEngineView.SelectAll)
        }
        MenuItem {
            text: qsTr("Refresh")
            onTriggered: webEngineView.triggerWebAction(WebEngineView.Reload)
        }
        MenuItem {
            text: qsTr("Zoom In")
            onTriggered: { webEngineView.zoomFactor += 0.1; }
        }
        MenuItem {
            text: qsTr("Zoom Out")
            onTriggered: { webEngineView.zoomFactor -= 0.1; }
        }
        MenuItem {
            text: qsTr("Open in external browser")
            onTriggered: {
                Qt.openUrlExternally(webEngineView.url)
            }
        }
    }

 SystemTrayIcon {
    id: trayIcon
    visible: true
    icon.source: "./imagens/google.png" // No caso a pasta onde está a imagem do ícone

    menu: Menu {
        MenuItem {
            text: "Open"
            onTriggered: {
                window.show()
                window.raise()
                window.requestActivate()
            }
        }
        MenuItem {
            text: "Open Specific URL"
            onTriggered: {
                webEngineView.url = "http://www.google.com"
                window.show()
                window.raise()
                window.requestActivate()
            }
        }
        MenuItem {
            text: "Toggle History View"
            onTriggered: {
                historyView.visible = !historyView.visible
            }
        }
        MenuItem {
            text: "Settings"
            onTriggered: {
                // Implemente a lógica para abrir a janela de configurações
            }
        }
        MenuItem {
            text: "Exit"
            onTriggered: Qt.quit()
        }
    }

    onActivated: reason =&amp;gt; {
        if (reason === SystemTrayIcon.Trigger) {
            if (window.visible) {
                window.hide()
            } else {
                window.show()
                window.raise()
                window.requestActivate()
            }
        }
    }
}

onClosing: {
    close.accepted = false;
    window.hide();
}

Component.onCompleted: {
    trayIcon.show()
}

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explicação do Código
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import QtWebEngine 1.10
import Qt.labs.platform 1.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esses comandos importam os módulos necessários para a aplicação. QtQuick é usado para interfaces de usuário, QtQuick.Controls para controles padrão (como botões e menus), QtQuick.Layouts para layouts, QtWebEngine para exibir páginas web, e Qt.labs.platform para acessar funcionalidades específicas da plataforma.&lt;/p&gt;






&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ApplicationWindow {
    id: window
    visible: true
    width: 1024
    height: 768
    title: "Google"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;ApplicationWindow&lt;/code&gt; define a janela principal da aplicação, com uma largura de 1024 pixels, altura de 768 pixels, e o título "Google".&lt;/p&gt;






&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WebEngineView {
    id: webEngineView
    anchors.fill: parent
    url: "https://www.google.com"

    onUrlChanged: {
        historyModel.append({url: webEngineView.url.toString()})
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;WebEngineView&lt;/code&gt; é o componente que carrega e exibe a página web. Quando o URL muda, a nova URL é adicionada ao modelo de histórico (&lt;code&gt;historyModel&lt;/code&gt;).&lt;/p&gt;






&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ListModel {
    id: historyModel
}

ListView {
    id: historyView
    width: parent.width
    height: 200
    model: historyModel
    delegate: Text {
        text: url
    }
    visible: false // Defina como true para visualizar o histórico
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;ListModel&lt;/code&gt; armazena as URLs visitadas. &lt;code&gt;ListView&lt;/code&gt; exibe esse histórico, mas está inicialmente invisível (&lt;code&gt;visible: false&lt;/code&gt;).&lt;/p&gt;






&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MouseArea {
    anchors.fill: parent
    acceptedButtons: Qt.RightButton
    onPressed: {
        if (mouse.button == Qt.RightButton) {
            contextMenu.open(mouse.x, mouse.y)
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;MouseArea&lt;/code&gt; detecta cliques do mouse. Se o botão direito do mouse for pressionado, o menu de contexto (&lt;code&gt;contextMenu&lt;/code&gt;) é aberto na posição do clique.&lt;/p&gt;






&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Menu {
    id: contextMenu
    MenuItem {
        text: qsTr("Copy")
        onTriggered: webEngineView.triggerWebAction(WebEngineView.Copy)
    }
    MenuItem {
        text: qsTr("Paste")
        onTriggered: webEngineView.triggerWebAction(WebEngineView.Paste)
    }
    MenuItem {
        text: qsTr("Cut")
        onTriggered: webEngineView.triggerWebAction(WebEngineView.Cut)
    }
    MenuItem {
        text: qsTr("Select All")
        onTriggered: webEngineView.triggerWebAction(WebEngineView.SelectAll)
    }
    MenuItem {
        text: qsTr("Refresh")
        onTriggered: webEngineView.triggerWebAction(WebEngineView.Reload)
    }
    MenuItem {
        text: qsTr("Zoom In")
        onTriggered: { webEngineView.zoomFactor += 0.1; }
    }
    MenuItem {
        text: qsTr("Zoom Out")
        onTriggered: { webEngineView.zoomFactor -= 0.1; }
    }
    MenuItem {
        text: qsTr("Open in external browser")
        onTriggered: {
            Qt.openUrlExternally(webEngineView.url)
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Menu&lt;/code&gt; define um menu de contexto com várias opções, como copiar, colar, cortar, selecionar tudo, atualizar, zoom in, zoom out, e abrir o URL em um navegador externo.&lt;/p&gt;






&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SystemTrayIcon {
    id: trayIcon
    visible: true
    icon.source: "./imagens/google.png" // No caso a pasta onde está a imagem do ícone

    menu: Menu {
        MenuItem {
            text: "Open"
            onTriggered: {
                window.show()
                window.raise()
                window.requestActivate()
            }
        }
        MenuItem {
            text: "Open Specific URL"
            onTriggered: {
                webEngineView.url = "http://www.google.com"
                window.show()
                window.raise()
                window.requestActivate()
            }
        }
        MenuItem {
            text: "Toggle History View"
            onTriggered: {
                historyView.visible = !historyView.visible
            }
        }
        MenuItem {
            text: "Settings"
            onTriggered: {
                // Implemente a lógica para abrir a janela de configurações
            }
        }
        MenuItem {
            text: "Exit"
            onTriggered: Qt.quit()
        }
    }

    onActivated: reason =&amp;gt; {
        if (reason === SystemTrayIcon.Trigger) {
            if (window.visible) {
                window.hide()
            } else {
                window.show()
                window.raise()
                window.requestActivate()
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;SystemTrayIcon&lt;/code&gt; cria um ícone na bandeja do sistema com um menu. As opções do menu permitem abrir a aplicação, abrir um URL específico, alternar a visualização do histórico, abrir configurações e sair da aplicação. A aplicação pode ser minimizada para a bandeja do sistema e restaurada a partir dela.&lt;/p&gt;






&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;onClosing: {
    close.accepted = false;
    window.hide();
}

Component.onCompleted: {
    trayIcon.show()
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esses manipuladores de evento garantem que a aplicação não seja completamente fechada ao clicar em fechar, mas sim escondida. &lt;code&gt;Component.onCompleted&lt;/code&gt; exibe o ícone na bandeja quando a aplicação é iniciada.&lt;/p&gt;




&lt;h3&gt;
  
  
  Conclusão
&lt;/h3&gt;

&lt;p&gt;O código exemplifica uma aplicação QML que utiliza &lt;code&gt;WebEngineView&lt;/code&gt; para carregar o Google, inclui um menu de contexto para várias operações, e um ícone de bandeja do sistema para facilitar o acesso e controle da aplicação. Os elementos chave como &lt;code&gt;MouseArea&lt;/code&gt; e &lt;code&gt;SystemTrayIcon&lt;/code&gt; melhoram a usabilidade, permitindo que os usuários interajam com a aplicação de maneira intuitiva e eficiente. Este exemplo é uma boa base para criar aplicativos web integrados com funcionalidades de interface de usuário ricas e interativas.&lt;/p&gt;

</description>
      <category>qml</category>
      <category>webview</category>
      <category>website</category>
      <category>qt</category>
    </item>
  </channel>
</rss>
