Criando um chat com Node.js e Socket.io

Node.js é uma tecnologia com menos de 10 anos, tendo sido lançada em 2009. No entanto, tem uns 5 anos que se tornou imensamente popular, desde que grandes nomes do mundo da tecnologia como Uber, Netflix e PayPal começaram a utilizá-la em produção.

Uma das principais características do Node.js, se não for sua principal, é o fato de trabalhar de maneira assíncrona, com requisições não bloqueantes e baseadas em eventos, o que o torna muito interessante para aplicações real-time. Aliado à isso temos e um ecossistema vibrante que cresce dia-a-dia com todo tipo de tecnologia auxiliar à plataforma, como o fantástico Socket.io.

Todo mundo que mexe com web já deve ter ouvido falar de websockets, Comet, etc. Esse tipo de tecnologia muito popularizado pelo Twitter nos idos de 2010-2012, promoveu uma revolução na forma de trabalhar comunicação cliente-servidor, cliente-cliente, real-time, etc. E Socket.io é justamente isso: uma tecnologia para comunicação real-time, bi-direcional, baseada em eventos.

Nesse post vamos explorar a criação de um aplicação de chat usando Node.js e Socket.io, visando dar uma introdução à tecnologia.

Veremos neste post:

  1. Introdução e Configuração
  2. Criando a aplicação web
  3. Programando o chat
  4. Indo além

Vamos lá!

#1 – Introdução e Configuração

Para este tutorial você precisa ter o Node.js instalado na sua máquina. Baixe e instale a versão mais recente para o seu sistema operacional. Usaremos sobre o Node.js o webframework Express (que eu já ensinei a usar antes), então abra o seu terminal de linha de comando e instale globalmente o módulo express-generator, que permite facilmente criar aplicações web com Express.

Muito provavelmente você terá de ter permissão de administrador para rodar o comando acima.

Agora, vamos criar um novo projeto Express usando o comando abaixo em que eu passo a view-engine (EJS, que usa HTML+JS), deixo o projeto preparado para o Git e passo o nome da pasta onde o projeto será armazenado.

Isso irá criar toda a estrutura mínima de pastas e arquivos para termos uma aplicação web funcionando com Express. Agora entre na pasta do projeto e rode um npm install para instalar as dependências:

Isso irá baixar todas as dependências necessárias para o básico funcionar. Mas ainda precisaremos de mais dependências!

Instale a dependência do módulo Socket.io usando o comando abaixo, que irá salvar esta dependência no seu arquivo package.json:

O Socket.io é dividido em duas partes: cliente em servidor. Essa dependência é a necessária para criar o servidor que rodará em nossa aplicação Node.js. Trabalharemos com o cliente mais tarde. Para deixar nosso servidor de chat já esperando conexões, abra o seu bin/www (que não possui extensão mas pode ser aberto por qualquer editor) e inclua o seguinte trecho de código que inicializa o servidor Socket.io e imprime uma mensagem no console toda vez que alguém “entra”no chat (que aqui basta acessar a página):

Esse código deve ficar depois do comando createServer.

Depois de instalar todas dependências e deixar pronto o rascunho do servidor de chat, apenas retorne à raiz do projeto e execute-o através do comando abaixo:

Se tudo deu certo, você deve abrir no seu navegador a URL localhost:3000 e ver a imagem abaixo:

Express funcionando
Express funcionando

Agora, para ver se o servidor Socket.io está funcionando realmente, vamos ter de implementar o lado do cliente.

#2 – Criando a aplicação web

Agora que temos todo nosso ambiente configurado, funcionando e sabemos o que vamos fazer, é hora de criar o front-end da nossa aplicação, que será bem “espartano”.

Na pasta views do seu projeto você encontrará arquivos .ejs que são as interfaces/layouts da sua aplicação web. Crie um novo arquivo chat.ejs (com qualquer editor de texto) e cole o seguinte conteúdo HTML dentro dele:

Para que essa view seja exibida no navegador, precisamos criar uma rota para ela. Fazer isso é bem fácil, entre na pasta routes e abra o arquivo index.js em seu editor de texto favorito. Dentro dele, adicione o seguinte bloco de código:

Isso quer dizer que, quando chegar uma requisição GET em /chat, renderizaremos a view chat.ejs. O resultado você confere abaixo, mandando rodar e acessando /chat no seu navegador:

Tela de Chat
Tela de Chat

A experiência do usuário com essa tela é bem simples: o usuário digita uma mensagem no campo e clica em Send para enviá-la. Obviamente nada disso funciona ainda.

O próximo passo é ajustar o lado do cliente para que ele se conecte em nosso servidor assim que entrar nessa tela. Para fazer isso, abra o nosso arquivo views/chat.ejs e inclua o seguinte trecho de código antes da tag </body>, lá pelo final do documento:

Esse código carrega a inicializa a biblioteca socket.io-client, que é um JS client-side que se conecta no server-side. Aqui eu não passei a URL do servidor para a função io(), sendo assim ele vai se conectar localhost mesmo.

Para ver se isto está funcionando, mande rodar novamente a sua aplicação e acesse a tela de chat, depois olhe no console. Acesse em várias abas diferentes e verá o comportamento abaixo no console:

Chat Server
Chat Server

Se quisermos saber também quando um usuário se desconectou do chat (ou seja, fechou a janela do navegador), podemos tratar isso com o evento disconnect. Lá em nosso bin/www, onde iniciamos o servidor de chat, altere o código para que seja impresso no console quando o usuário deixa a sala de chat:

Agora se iniciarmos novamente a aplicação e fizermos o mesmo procedimento de abrir e fechar abas, veremos um comportamento um pouco diferente:

Chat Server Funcionando
Chat Server Funcionando

E com isso agora temos tanto o client quanto o server da nossa aplicação de chat funcionando!

#3 – Programando o chat

A ideia principal por trás do Socket.io é que você pode enviar e receber quaisquer eventos que quiser, com os dados que quiser, usando objetos JSON ou binários mesmo.

Agora vamos programar o client-side da nossa aplicação para que quando o botão Send seja clicado, um evento ‘chat message’ seja disparado ao servidor com a mensagem escrita. Esse código deve ir no chat.ejs, no final do arquivo, substituindo o código anteriormente adicionado:

Agora temos de fazer com que nosso chat-server esteja preparado para lidar com esse evento. Sendo assim, abra o seu bin/www novamente e modifique o código do seu chat-server como abaixo:

Agora nosso chat-server já está recebendo mensagens. Se você executar a aplicação e escrever e enviar algumas mensagens, vai ver elas escritas no terminal do console.

Aqui a comunicação está sendo unidirecional: somente o server está recebendo as mensagens dos clientes. Nosso próximo passo é fazer com que as mensagens recebidas pelo server sejam propagadas para os clientes que estão na mesma sala de bate-papo. Para fazer isso, vamos mexer novamente no código do nosso chat-server em bin/www:

Para fazer a propagação da mensagem para todos os clientes, usamos o io.emit, passando o nome do evento que queremos propagar e os dados do evento, que aqui no caso é apenas a mensagem crua.

No entanto se você testar agora, verá que apesar do chat estar funcionando “por baixo dos panos”, não aparece nada na tela ainda. Isso porque ainda precisamos de uma última alteração no nosso front-end para tratar o recebimento deste evento e exibi-lo na tela do chat. Mude o bloco do client-side para o abaixo:

Agora sim, teste sua aplicação em mais de uma aba e verá que as mensagens enviadas por uma delas são repercutidas nas demais, como na imagem abaixo:

Chat Funcionando
Chat Funcionando

Legal não?!

#4 – Indo além

Esse tutorial termina aqui, mas existem diversas coisas que você pode estudar para aperfeiçoar esse chat. Dentre elas:

  • autenticação com passport;
  • persistência das conversas com MongoDB;
  • suporte a nicknames;
  • enviar um broadcast quando alguém novo entra na sala, ou quando alguém sai da mesma;
  • funcionalidade de “usuário está digitando…” através de Ajax com JQuery
  • mensagens privadas

O Socket.io é bem extensível com seu modelo aberto de eventos e dados, logo, é muito simples customizá-lo para atender todo tipo de demandas, criando seus eventos personalizados.

Espero que tenham gostado!

Fonte: este post foi claramente influenciado pela documentação oficial do Socket.io (em inglês).

Curtiu o post? Então clica no banner abaixo e dá uma conferida no meu livro sobre programação web com Node.js!

Tutorial Node.js com MS SQL Server

Hoje vou ensinar como que você pode usar Node.js com MS SQL Server. Não, essa não é uma dupla muito comum de ver no mercado, o mais normal é que Node.js seja utilizado com MongoDB, ou com MySQL. Eu cheguei a fazer umas pesquisas de tendências antes de escrever este post e MySQL é em média 10x mais utilizado com Node.js. No entanto, recentemente tive de fazer uma API que se comunicava com SQL Server e isso me fez descobrir que esse é um assunto pouco comentado na Internet, então resolvi deixar aqui este tutorial para quem precisar mais tarde (incluindo eu).

Veremos neste artigo (pule seções à vontade):

  1. Criando o banco de dados
  2. Criando e populando a tabela
  3. Criando a API
  4. Criando a listagem de clientes
  5. Criando a pesquisa de um cliente
  6. Excluindo um cliente
  7. Adicionando um cliente
  8. Atualizando um cliente
  9. Bônus 1: Executando muitas operações SQL
  10. Bônus 2: ORM

Então vamos lá!

Parte 1: Criando o banco de dados

Se você já possui um banco de dados SQL Server, ok, use ele e pule para a próxima parte.

Se você não possui um banco SQL Server, você tem duas opções:

  1. baixar e instalar o MS SQL Server Express (gratuito) na sua máquina;
  2. contratar um banco SQL Server na nuvem (pago, mas vou te ensinar um truque);

A primeira opção é um pouco mais trabalhosa, porém é free. Baixe, instale e crie um novo banco de dados para uso nesse tutorial. Durante a instalação, você irá precisar definir uma senha para o usuário ‘sa’, não esqueça dela.

A segunda opção é bem simples, mais vai te custar alguns pilas. Ou não. Na Umbler, empresa em que trabalho como evangelista tecnológico, você ganha créditos de graça para usar livremente conforme vai cumprindo algumas etapas dentro do painel. Crie um site com um domínio qualquer (mesmo que não tenha registrado) e depois vá na parte de banco de dados para criar um do tipo SQL Server. Isso te custará uns R$15 por mês, mas apenas se deixar o mês inteiro funcionando, você pode excluir tudo logo após esse tutorial, se quiser, e não será mais cobrado. Durante a criação do seu banco, você vai definir o nome dele, o usuário e a senha, anote tudo, bem como o endereço do servidor que a Umbler vai te fornecer.

Parte 2: Criando e populando a tabela

Agora que você já tem o banco pronto, vamos criar uma tabela nele e colocar alguns dados de exemplo. Não pule esta etapa pois vamos fazer tudo isso usando Node.js!

Crie uma pasta para guardar os arquivos do seu projeto Node.js, você pode fazer isso pelo console se quiser, usaremos ele algumas vezes nesse tutorial. No exemplo abaixo, criei a pasta e depois entrei dentro dela.

Agora execute no console o comando “npm init” que o próprio NPM (gerenciador de pacotes do Node) vai te guiar para a construção do arquivo packages.json, que é o arquivo de configuração do projeto. Se ficar em dúvida ou com preguiça, segue o meu package.json abaixo:

Com o arquivo de configurações criado, vá no console novamente, na pasta do projeto e digite o seguinte comando para instalar a extensão mssql, que permite usar Node com SQL Server:

A flag “-S”diz que é pra salvar essa dependência no arquivo packages.json. Se você abrir o arquivo vai ver que tem coisa nova por lá.

Agora, crie um arquivo create-table.js dentro dessa pasta, que será o arquivo que vai criar e popular nossa tabela que usaremos neste exercícios. Também usaremos ele para entender o básico de comandos SQL, conexão com o banco, etc. Vamos começar nosso create-table.js definindo uma constante para a String de conexão com o banco e uma constante para o objeto que vai carregar a extensão mssql (e que mais tarde usaremos para conectar, executar SQL, etc). Se você já usou SQL Server com C#, a String é a mesma, caso contrário, preencha as lacunas conforme a String de exemplo abaixo:

Agora, usaremos esse objeto sql para fazer uma conexão e, em caso de sucesso, imprimir uma mensagem de sucesso. Caso contrário, se der erro, uma mensagem de falha. Note que existem diversas maneiras de programar este mesmo algoritmo em Node.js: você pode usar a forma clássica com callbacks, a forma intermediária com promises e a forma mais recente com async/await. Eu vou fazer com promises, que acho que é a que fica mais legível:

Se esse código lhe parece muito bizarro, calma, é fácil de entender. O objeto sql permite que façamos coisas no banco de dados, uma delas é a conexão (connect). No entanto, o Node.js trabalha de maneira assíncrona, o que quer dizer que ele não espera pela conexão ser estabelecida, ele retorna uma promessa (promise) de que vai executar algo (o meu ‘then’ ou o meu ‘catch’ se der erro) após a conexão for concluída. Sendo assim, no ‘then’ eu coloco uma função JS que será executada quando a conexão funcionar, e no ‘catch’ uma função JS que será executada em caso de erro.

Neste exemplo usei arrow-functions que está disponível desde o ES6 (versão 2016 do Javascript), que são formas mais simples de declarar funções pequenas. O que vem antes do ‘=>’ (arrow) são os parâmetros da função. O que vem depois são os comandos da mesma. Usarei novamente mais tarde e volto a explicar para você.

Para executar esse arquivo, abra o console (se estiver usando VS Code, apenas aperta F5 com este arquivo aberto no editor) e na pasta do projeto digite:

Agora que sabemos como conectar no SQL Server através de Node.js, é hora de executarmos o comando que vai criar a tabela e popular ela, ao invés de simplesmente imprimir “conectou”. Sendo assim, vamos criar uma função JS nesse arquivo pra fazer a criação da tabela:

Coloque a chamada desta função no ‘then’ após a conexão no banco, passando o objeto conn por parâmetro, como abaixo:

Mande rodar esse arquivo novamente e verá que ele criará o seu banco já com 3 linhas de dados. Se não quiser fazer isso dessa maneira, você pode fazer pela sua ferramenta de gerenciamento do SQL Server (como o Management Studio) ou pelo Visual Studio, se estiver usando ele para programar Node.js.

Parte 3: Criando a API

Agora que já temos nosso banco de dados SQL Server pronto, com dados de exemplo e aprendemos como fazer a conexão nele, vamos criar uma API básica usando Express para conseguir criar um CRUD com Node.js + MS SQL Server no próximo passo. Se já sabe como montar uma API básica com Node + Express, pule esta parte.

Vamos começar adicionando a dependência do Express (framework web) e do Body-Parser (parser para os POSTs futuros) no projeto via linha de comando na pasta do mesmo:

Na sequência, vamos criar um arquivo index.js na pasta do projeto onde vamos criar o nosso servidor da API para tratar as requisições que chegarão em breve. Vamos começar bem simples, apenas definindo as constantes locais que serão usadas mais pra frente:

Vamos usar apenas um pool de conexões global com o servidor MS SQL. Para isso adicione as seguintes linhas logo abaixo do bloco anterior, para conectar no banco de dados assim que a aplicação iniciar (podemos melhorar isso, mas por ora, vamos deixar assim mesmo):

Agora, logo abaixo, vamos configurar nossa aplicação (app) Express para usar o body parser que carregamos da biblioteca body-parser, permitindo que recebamos mais tarde POSTs nos formatos URLEncoded e JSON:

Na sequência, vamos criar um roteador e dentro dele definir uma regra inicial que apenas exibe uma mensagem de sucesso quando o usuário requisitar um GET na raiz da API (/) para ver se está funcionando.

Note que na última linha eu digo que requisições que chegarem na raiz devem ser mandadas para o router. Por fim, adicionamos as linhas abaixo no final do arquivo que dão o start no servidor da API:

Teste sua API executando via console o seu index.js com o comando ‘node index.js’. Você deve ver a mensagem de ‘API funcionando!’ no console, e se acessar no navegador localhost:3000 deve ver o JSON default que deixamos na rota raiz!

API Funcionando
API Funcionando

Parte 4: Criando a listagem de clientes

Agora que temos uma API funcionando, vamos adicionar uma rota /clientes que listará todos os clientes do banco de dados. Para fazer isso, primeiro vamos criar uma função que executará consultas SQL no banco usando o pool de conexões global (conn), como abaixo:

Esta função nós usaremos para consultar todos os clientes e para consultar apenas um também. Agora, vamos criar a rota /clientes logo abaixo da rota / (raiz):

Agora, ao executarmos novamente nosso projeto e acessarmos a URL localhost:3000/clientes, veremos todos os clientes cadastrados no banco de dados (no passo 2, lembra?):

Todos clientes
Todos clientes

E com isso finalizamos a listagem de todos clientes na nossa API!

Parte 5: Criando a pesquisa de um cliente

Agora, se o usuário quiser ver apenas um cliente, ele deverá passar o ID do mesmo na URL, logo após o /clientes. Para fazer isso, vamos modificar nossa rota criada no passo anterior, /clientes, para aceitar um parâmetro opcional ID. Além disso, dentro do processamento da rota, se vier o ID, devemos fazer uma consulta diferente da anterior, como mostra o código abaixo, com os ajustes na mesma rota do passo anterior:

O parseInt que coloquei é apenas uma proteção contra SQL Injection uma vez que neste caso o ID deve ser um inteiro válido. Não é a melhor forma de resolver isso, mas vai nos atender por enquanto sem ter de entrar em conceitos mais avançados. Manda rodar e teste no navegador, verá que está funcionando perfeitamente!

Apenas um
Apenas um

E com isso terminamos a pesquisa por cliente.

Parte 6: Excluindo um cliente

Para excluir um cliente vamos fazer um processo parecido com o de pesquisar um cliente, no entanto, mudaremos o verbo HTTP de GET para DELETE, como manda o protocolo. Adicione a nova rota logo após as demais:

Note que desta vez o parâmetro id na URL não é opcional (não usei ? após :id). E dentro do processamento da requisição delete do router eu mando um SQL de DELETE passando o ID numérico.

Para testar essa rota você tem duas alternativas, ou usa o POSTMAN para forjar um DELETE, como abaixo:

DELETE com POSTMAN
DELETE com POSTMAN

Ou fazer via console usando cURL (se tiver ele instalado na sua máquina):

Em ambos os casos você deve obter uma resposta 200 OK (caso não tenha dado erro) e se mandar listar todos clientes novamente, verá que o número 1 sumiu.

Parte 7: Adicionando um cliente

Agora vamos adicionar um novo cliente com um POST na rota /clientes. Adicione esta nova rota logo abaixo das anteriores.

Nela, eu pego as variáveis que devem vir junto ao POST, faço algumas validações de tamanho e tipo de dado e depois junto elas a um comando de INSERT que vai ser executado no banco de dados.

Para testar esse POST, você usar o POSTMAN, como mostrado anteriormente:

POST no POSTMAN
POST no POSTMAN

Se quiser fazer via cURL:

Também podemos permitir outras maneiras de passar os dados com a requisição POST, como através de JSON ou através da URL, mas isso foge do escopo deste artigo que deve focar mais no CRUD com SQL Server + Node.

Se testar agora vai ver que é possível inserir novos registros no banco de dados através de requisições POST.

Parte 8: Atualizando um cliente

E para finalizar o CRUD, vamos ver como podemos atualizar um cliente no banco de dados SQL Server através da nossa API Node.js. Para fazer updates podemos usar os verbos PUT ou PATCH. O protocolo diz que devemos usar PUT se pretendemos passar todos os parâmetros da entidade que está sendo atualizada, mas não vamos alterar jamais o ID, então usaremos PATCH nesta API.

Crie uma rota PATCH em /clientes esperando o ID do cliente a ser alterado.

No código acima, pegamos o ID que veio na URL e as demais informações que vieram no corpo da requisição, fazendo as mesmas validações que já havia feito antes (podemos melhorar a segurança aqui). Depois monto o UPDATE com as variáveis locais e mando para nossa função de executar SQL.

Para testar uma requisição PATCH, você pode usar o POSTMAN:

PATCH no POSTMAN
PATCH no POSTMAN

Ou o cURL:

O resultado é o mesmo: o cliente cujo ID=4 vai ter o seu nome alterado para ‘fernando’. Note que se ele não existir, ocasionará um erro que será apresentado no corpo da resposta.

E com isso finalizamos o CRUD da nossa API Node.js que usa SQL Server como persistência de dados.

Bônus 1: Executando muitas operações SQL

Ao contrário dos bancos não-relacionais que estamos acostumados a usar com Node.js, o SQL Server leva mais a sério a escrita dos dados e o faz de maneira síncrona e um tanto lenta para uma plataforma veloz como o Node.js. Isso faz com que, se você for executar um loop de comandos no banco de dados, provavelmente vai dar erro pois o banco não conseguirá responder apropriadamente a uma enxurrada de requisições da sua aplicação Node.

Para resolver isso eu costumo encadear as execuções uma a uma, simulando uma execução assíncrona através de chamadas recursivas, como no exemplo abaixo:

Neste exemplo eu possuía um array de itens que eram endereços de email que eu queria excluir do banco de dados. O i passado por parâmetro inicialmente é 0, quando chamarmos esta função a primeira vez e depois vai sendo incrementado recursivamente. Já o objeto conn é a conexão do banco de dados, que você pode omitir se estiver usando ela global.

O primeiro if do código garante a condição de parada da recursão e o catch na promise da query te contará se houver algum erro. Nada muito rebuscado, mas me foi bem útil!

Bônus 2: ORM

Se você não curte muito a ideia de ficar usando SQL no meio dos seus códigos JS, experimente usar alguma biblioteca ORM (Object-Relational Mapping) como o Sequelize. Nunca usei, mas muita gente recomenda por possuir suporte a MySQL, SQL Server, PostgreSQL e SQLite.

Até a próxima!

Curtiu o post? Então clica no banner abaixo e dá uma conferida no meu livro sobre programação web com Node.js!

Tutorial CRUD em Node.js com driver nativo do MongoDB

Workshop Node+Mongo no TDC
Workshop Node+Mongo no TDC SP

Tem algum tempo que escrevi um post ensinando como usar Node.js e MongoDB pra fazer listagem e cadastro de usuários (apenas o CR do CRUD), mas na ocasião usei Mongoose, um ORM, para abstrair o acesso ao MongoDB. No entanto, essa abstração pode ter causado confusão na cabeça de alguns leitores e resolvi escrever este outro post aqui, desta vez fazendo um CRUD completo usando Node.js (com Express + EJS) e MongoDB, mas com o driver nativo do mesmo, que é quase idêntico a como usamos MongoDB via terminal.

O conteúdo deste post também vai servir de apoio para os alunos do workshop de Node.js + MongoDB que ministrei recentemente no TDC SP e cujos slides você confere no final do post.

Neste artigo você vai ver:

  1. Configurando o Node.js
  2. Entendendo o projeto Express
  3. Configurando o MongoDB
  4. Conectando no MongoDB com Node
  5. Cadastrando no banco
  6. Atualizando clientes
  7. Excluindo clientes

#1 – Configurando o Node.js

Nesta parte vamos instalar e configurar o ambiente necessário para o restante do tutorial.

Passo 1: Instalar Node.js

Bem fácil: apenas clique no link do site oficial e depois no grande  botão verde para baixar o executável certo para o seu sistema operacional. Esse executável irá instalar o Node.js e o NPM, que é o gerenciador de pacotes do Node.

Uma vez com o NPM instalado, vamos instalar o módulo que será útil mais pra frente. Crie uma pasta para guardar os seus projetos Node no computador (recomendo C:\node) e dentro dela, via terminal de comando com permissão de administrador (sudo no Mac), rode o comando abaixo:

Passo 2: Crie um projeto Express

O Express é o web framework mais famoso da atualidade para Node.js. Com ele você consegue criar aplicações e APIs web muito rápida e facilmente.

Você pode rapidamente criar a estrutura básica de um projeto Express via linha de comando, da seguinte maneira (aqui considero que você salva seus projetos na pasta C:\node):

O “-e” é para usar a view-engine (motor de renderização) EJS, ao invés do tradicional Jade/Pug. Já o “–git” deixa seu projeto preparado para versionamento com Git. Aperte Enter e o projeto será criado (talvez ele peça uma confirmação, apenas digite ‘y’ e confirme).

Depois entre na pasta e mande instalar as dependências com npm install:

Ainda no terminal de linha de comando e, dentro da pasta do projeto, digite:

Isso vai fazer com que a aplicação default inicie sua execução em localhost:3000, que você pode acessar pelo seu navegador.

Capture

Happy Hello World!

#2 – Entendendo o projeto Express

O conteúdo desta parte 2 você pode conferir também no vídeo abaixo, se preferir:

Vamos abrir agora o arquivo app.js, que fica dentro do diretório da sua aplicação NodeJS (nodetest1 no meu caso). Este arquivo é o coração da sua aplicação, embora não exista nada muito surpreendente dentro. Você deve ver algo parecido com isso logo no início:

Isto define um monte de variáveis JavaScript e referencia elas a alguns pacotes, dependências, funcionalidades do Node e rotas. Rotas são como uma combinação de models e controllers nesta configuração – elas direcionam o tráfego e contém também alguma lógica de programação (embora você consiga, se quiser, fazer um MVC mais puro se desejar). Quando criamos o projeto Express, ele criou estes códigos JS pra gente e vamos ignorar a rota ‘users’ por enquanto e nos focar no index, controlado pelo arquivo c:\node\nodetest1\routes\index.js.

Na sequência você deve ver:

Este é bem importante. Ele instancia o Express e associa nossa variável app à ele. A próxima seção usa esta variável para configurar coisas do Express.

Isto diz ao app onde ele encontra suas views, qual engine usar para renderizar as views (EJS) e chama alguns métodos para fazer com que as coisas funcionem. Note também que esta linha final diz ao Express para acessar os objetos estáticos a partir de uma pasta /public/, mas no navegador elas aparecerão como se estivessem na raiz do projeto. Por exemplo, a pasta images fica em c:\node\workshoptdc\public\images mas é acessada em http://localhost:3000/images

Estes são manipuladores de erros para desenvolvimento e produção (além dos 404). Não vamos nos preocupar com eles agora, mas resumidamente você tem mais detalhes dos erros quando está operando em desenvolvimento.

Uma parte importantíssima do Node é que basicamente todos os módulos exportam um objeto que pode ser facilmente chamado em qualquer lugar no código. Nosso app master exporta seu objeto app.

OK! Vamos em frente, agora mexendo com persistência de dados.

#3 – Configurando o MongoDB

OK, agora que temos, entendemos e aprendemos a alterar a estrutura básica vamos fazer mais alguns ajustes em um arquivo que fica na raiz do seu projeto chamado package.json. Ele é o arquivo de configuração do seu projeto e determina, por exemplo, quais as bibliotecas que você possui dependência no seu projeto.

Passo 1: Instalar o MongoDB

Agora vamos deixar nosso editor de texto um pouco de lado e voltar ao prompt de comando. Na verdade vamos primeiro usar nosso navegador para acessar o site oficial do MongoDB e baixar o Mongo. Clique no link de download e busque a versão de produção mais recente (3.4?) para o seu sistema operacional. Baixe o arquivo e, no caso do Windows, rode o executável que extrairá os arquivos na sua pasta de Arquivos de Programas, seguido de uma pasta server/versão, o que é está ok para a maioria dos casos, mas que eu prefiro colocar em C:\Mongo.

Passo 2: Executar mongod e mongo

Dentro da pasta do seu projeto Node, que aqui chamei de workshoptdc, deve existir uma subpasta data. Você pode criar manualmente ou via terminal:

Nesta pasta vamos armazenar nossos dados do MongoDB. Se este diretório não for criado, teremos problemas mais tarde.

Pelo prompt de comando, entre na subpasta bin dentro da pasta de instalação do seu MongoDB e digite (no caso de Mac e Linux, coloque um ./ antes do mongod):

Isso irá iniciar o servidor do Mongo. Uma vez que apareça no prompt “[initandlisten] waiting for connections on port 27017”, está pronto, o servidor está executando corretamente.

Agora abra outro prompt de comando (o outro ficará executando o servidor) e novamente dentro da pasta bin do Mongo, digite:

Após a conexão funcionar, se você olhar no prompt onde o servidor do Mongo está rodando, verá que uma conexão foi estabelecida. mongod é o executável do servidor, e mongo é o executável de cliente, que você acabou de conectar.

Opcionalmente você pode usar ferramentas visuais como Studio3T, que particularmente eu gosto de utilizar (antiga MongoChef, gratuita).

Passo 3: Criando uma base de dados

No console do cliente mongo, digite:

Agora estamos usando a base “workshoptdc.” No entanto, ela somente será criada de verdade quando adicionarmos registros nela, o que faremos a partir do próprio cliente para exemplificar.

Passo 4: Inserindo alguns dados

Uma de minhas coisas favoritas sobre MongoDB é que ele usa JSON como estrutura de dados, o que significa curva de aprendizagem zero para quem já conhece o padrão. Caso não seja o seu caso, terá que buscar algum tutorial de JSON na Internet antes de prosseguir.

Vamos adicionar um registro à nossa coleção (o equivalente do Mongo às tabelas do SQL). Para este tutorial teremos apenas uma base de customers (clientes), sendo o nosso formato de dados como abaixo:

O atributo _id pode ser omitido, neste caso o próprio Mongo gera um guid pra você. No seu cliente mongo, digite:

Uma coisa importante aqui: “db” é a base de dados na qual estamos conectados no momento, que um pouco antes havíamos definido como sendo “workshoptdc”. A parte “customers” é o nome da nossa coleção, que passará a existir assim que adicionarmos um objeto JSON nela. Tecle Enter para que o comando seja enviado ao servidor. Se tudo deu certo, uma respostas com “nInserted: 1” (number of inserted) deve aparecer.

Para ver se o registro foi parar no banco realmente, digite:

O pretty() no final do comando find() é para identar o resultado, que retornará:

Claro, o seu _id pode ser diferente desse, uma vez que o Mongo irá gerá-lo automaticamente. Isto é tudo que precisamos saber de MongoDB no momento, o que me parece bem fácil, aliás! Para saber mais sobre o MongoDB, Google!

Agora vamos adicionar mais alguns registros no seu console mongo:

Nesse exemplo passei um array com vários objetos para nossa coleção. Usando novamente o comando db.customers.find().pretty() irá mostrar que todos foram salvos no banco. Agora sim, vamos interagir de verdade com o web server + MongoDB.

#4 – Conectando no MongoDB com Node

Agora sim vamos juntar as duas tecnologias-alvo deste post!

Passo 1: Instalando a dependência do Mongo

Precisamos adicionar uma dependência para que o MongoDB funcione com essa aplicação usando o driver nativo. Usaremos o NPM via linha de comando de novo:

Com isso, uma dependência nova será baixada para sua pasta node_modules e uma novas linha de dependência será adicionada no package.json para dar suporte a MongoDB.

Passo 2: Organizando o acesso ao banco

Primeiramente, para organizar nosso acesso à dados, vamos criar um novo arquivo chamado db.js na raiz da nossa aplicação Express (workshoptdc). Esse arquivo será o responsável pela conexão e manipulação do nosso banco de dados, usando o driver nativo do MongoDB. Adicione estas linhas:

Estas linhas carregam o objeto mongoClient  a partir do módulo ‘mongodb’ e depois fazem uma conexão em nosso banco de dados localhost, sendo 27017 a porta padrão do MongoDB. Essa conexão é armazenada globalmente, para uso posterior e em caso de erro, o mesmo é logado no console.

 

A última linha ignore por enquanto, usaremos ela mais tarde. Agora abra o arquivo www que fica na pasta bin do seu projeto Node e adicione a seguinte linha no início dele:

Nesta linha nós estamos carregando o módulo db que acabamos de criar e guardamos o resultado dele em uma variável global. Ao carregarmos o módulo db, acabamos fazendo a conexão com o Mongo e retornamos aquele objeto vazio do module.exports, lembra? Usaremos ele mais tarde, quando possuir mais valor.

A seguir, vamos modificar a nossa rota para que ela mostre dados vindos do banco de dados, usando esse db.js que acabamos de criar.

Passo 3: Criando a função de consulta

Para conseguirmos fazer uma listagem de clientes, o primeiro passo é ter uma função que retorne todos os clientes em nosso módulo db.js (arquivos JS que criamos são chamados de módulos se possuírem um module.exports no final), assim, adicione a seguinte função ao seu db.js:

Nesta função ‘findAll’, esperamos uma função de callback por parâmetro que será executada quando a consulta no Mongo terminar. Isso porque as consultas no Mongo são assíncronas e o único jeito de conseguir saber quando ela terminou é executando um callback.

A consulta aqui é bem direta: usamos a conexão global conn para navegar até a collection de customers e fazer um find sem filtro algum. O resultado desse find é um cursor, então usamos o toArray para convertê-lo para um array e quando terminar, chamamos o callback para receber o retorno.

Agora no final do mesmo db.js, modifique o module.exports para retornar a função findAll. Isso é necessário para que ela possa ser chamada fora deste arquivo:

Agora , vamos programar a lógica que vai usar esta função. Abra o arquivo C:\node\workshoptdc\routes\index.js no seu editor de texto (sugiro o Visual Studio Code).  Dentro dele temos apenas a rota default, que é um get no path raiz. Vamos editar essa rota da seguinte maneira:

router.get define a rota que trata essas requisições com o verbo GET. Quando recebemos um GET /, a função de callback dessa rota é disparada e com isso usamos o findAll que acabamos de programar. Por parâmetro passamos a função callback que será executada quando a consulta terminar, exibindo um erro ou renderizando a view index com os docs como model.

Passo 4: Editando a view de listagem

Agora vamos arrumar a nossa view para listar os clientes. Entre na pasta C:\node\workshoptdc\views\ e edite o arquivo index.ejs para que fique desse jeito:

Aqui estamos dizendo que o objeto docs, que será retornado pela rota que criamos no passo anterior, será iterado com um forEach e seus objetos utilizados um-a-um para compor uma lista não-ordenada com seus nomes.

Isto é o bastante para a listagem funcionar. Salve o arquivo e reinicie o servidor Node.js. Ainda se lembrar de como fazer isso? Abra o prompt de comando, derrube o processo atual (se houver) com Ctrl+C e depois:

Agora abra seu navegador, acesse http://localhost:3000/userlist e maravilhe-se com o resultado.

Lista de Clientes
Lista de Clientes

 

Se você viu a página acima é porque sua conexão com o banco de dados está funcionando!

Agora vamos seguir adiante com coisas mais incríveis em nosso projeto de CRUD!

Parte 5 – Cadastrando no banco

Listar dados é moleza e salvar dados no MondoDB não é algo particularmente difícil. Essencialmente precisamos definir uma rota para receber um POST, ao invés de um GET.

Passo 1: Criando sua view de cadastro

Primeiro vamos criar a nossa tela de cadastro de usuário com dois clássicos e horríveis campos de texto à moda da década de 90. Dentro da pasta views, crie um new.ejs com o seguinte HTML dentro:

Agora vamos voltar à pasta routes e abrir o nosso arquivo de rotas, o index.js onde vamos adicionar duas novas rotas. A primeira, é a rota GET para acessar a página new quando acessarmos /new no navegador:

Se você reiniciar seu servidor Node e acessar http://localhost:3000/newuser verá a página abaixo:

Novo Cadastro
Novo Cadastro

 

Se você preencher esse formulário agora e clicar em salvar, dará um erro 404. Isso porque ainda não criamos a rota que receberá o POST desse formulário!.

Passo 2: Codificando para cadastrar clientes

Primeiro, vamos alterar nosso db.js para incluir uma nova função, desta vez para inserir clientes usando a conexão global e, novamente, executando um callback ao seu término:

Não esqueça de adicionar essa nova função no module.exports no final do arquivo:

Agora vamos criar uma rota para que, quando acessada via POST, nós chamaremos o objeto global db para salvar os dados no Mongo. A rota será a mesma /new, porém com o verbo POST. Então abra novamente o arquivo /routes/index.js e adicione o seguinte bloco de código logo após as outras rotas e antes do modules.export:

Obviamente no mundo real você irá querer colocar validações, tratamento de erros e tudo mais. Aqui, apenas pego os dados que foram postados no body da requisição HTTP usando o objeto re (request/requisição). Crio um JSON com essas duas variáveis e envio para função insert que criamos agora a pouco.

Na função de callback exigida pelo insert colocamos um código que imprime o erro se for o caso ou redireciona para a index novamente para que vejamos a lista atualizada. Apenas para finalizar, edite o views/index.ejs para incluir um link para a página /new:

Resultado:

Cadastro funcionando
Cadastro funcionando

 

#6 – Atualizando clientes

Para atualizar clientes (o U do CRUD) não é preciso muito esforço diferente do que estamos fazendo até agora. No entanto, são várias coisas menores que precisam ser feitas e é bom tomar cuidado para não deixar nada pra trás.

Passo 1: Arrumando a tela de listagem

Primeiro, vamos editar nossa views/index.ejs para que quando clicarmos no nome do cliente, joguemos ele para uma tela de edição. Fazemos isso com uma âncora ao redor do nome, construída no EJS:

Note que este link aponta para uma rota /edit que ainda não possuímos, e que após a rota ele adiciona o _id do customer, que servirá para identificá-lo na página seguinte. Com esse _id em mãos, teremos de fazer uma consulta no banco para carregar seus dados no formulário permitindo um posterior update. Por ora, apenas mudou a aparência da tela de listagem:

Listagem com Edição
Listagem com Edição

Passo 2: Carregando os dados antigos

Sendo assim, vamos começar criando uma nova função no db.js que retorna apenas um cliente, baseado em seu _id:

Como nosso filtro do find será o id, ele deve ser convertido para ObjectId, pois virá como string na URL e o Mongo não entende Strings como _ids. Não esqueça de incluir esta nova função no module.exports, que está crescendo:

Agora vamos criar a respectiva rota GET em nosso routes/index.js que carregará os dados do cliente para edição no mesmo formulário de cadastro:

Esta rota está um pouco mais complexa que o normal. Aqui nós pedimos ao db que encontre o cliente cujo id veio como parâmetro da requisição (req.params.id). Após ele encontrar o dito cujo, mandamos renderizar a mesma view de cadastro, porém com um model inteiramente novo contendo apenas um documento (o cliente a ser editado) e a action do form da view ‘new.ejs’.

Mas antes de editar a view new.ejs, vamos editar a rota GET em /new para incluir no model dela o cliente e a action, mantendo a uniformidade entre as respostas:

Agora sim, vamos na new.ejs e vamos editá-la para preencher os campos do formulário com o model recebido, bem como configurar o form com o mesmo model:

Agora, se você mandar rodar e clicar em um link de edição, deve ir para a tela de cadastro mas com os campos preenchidos com os dados daquele cliente em questão:

Edição quase funcionando
Edição quase funcionando

Passo 3: Codificando o update

Agora estamos muito perto de terminar a edição. Primeiro precisamos criar um novo método no db.js para fazer update, como abaixo:

O processo não é muito diferente do insert, apenas temos de passar o filtro do update para saber qual documento será afetado (neste caso somente aquele que possui o id específico). Também já inclui o module.exports atualizado na última linha para que você não esqueça.

Para encerrar, apenas precisamos configurar uma rota para receber o POST em /edit com o id do cliente que está sendo editado, chamando a função que acabamos de criar:

Aqui carreguei o id que veio como parâmetro na URL, e os dados de nome e idade no body da requisição (pois foram postados via formulário). Apenas passei esses dados nas posições corretas da função de update e passei um callback bem simples parecido com os anteriores, mas que redireciona o usuário para a index do projeto em caso de sucesso, voltando à listagem.

E com isso finalizamos o update!

#7 – Excluindo clientes

Agora em nossa última parte do tutorial, faremos o D do CRUD, D de Delete!

Passo 1: Editando a listagem

Vamos voltar à listagem e adicionar um link específico para exclusão, logo ao lado do nome de cada cliente, incluindo uma confirmação de exclusão nele via JavaScript, como abaixo:

Note que não sou muito bom de front-end, então sinta-se à vontade para melhorar os layouts sugeridos. Aqui, o link de cada cliente aponta para uma rota /delete passando _id do mesmo. Essa rota será acessada via GET, afinal é um link, e devemos configurar isso mais tarde.

Exclusão funcionando
Exclusão funcionando

Passo 2: Codificando a exclusão

Vamos no db.js adicionar nossa última função, de delete:

Essa função é bem simples de entender se você fez todas as operações anteriores. Na sequência, vamos criar a rota GET /delete no routes/index.js:

Nessa rota, após excluirmos o cliente usando a função da variável global.db, redirecionamos o usuário de volta à tela de listagem para que a mesma se ostre atualizada.

E com isso finalizamos nosso CRUD!

Se quiser saber onde pode publicar o seu projeto em Node.js com MongoDB, eu sugiro a Umbler, que é a empresa onde trabalho!

Curtiu o post? Então clica no banner abaixo e dá uma conferida no meu livro sobre programação web com Node.js!