Olá e boas vindas ao segundo curso de WebSockets aqui da Plataforma Alura! Sou o instrutor Antonio Evaldo que te acompanhará ao longo das aulas.
Autodescrição: Sou um homem branco com cabelos compridos pretos e ondulados presos atrás da cabeça, uso cavanhaque, tenho olhos castanhos escuros e visto uma camiseta escura sem estampa. Ao fundo, está uma parede iluminada por uma luz azul e há um quadro pendurado com o desenho de uma guitarra vermelha.
Daremos continuidade ao AluraDocs que começamos no curso anterior, e nossas amigas fictícias, a Eduarda e a Juliana, nos pediram para fazer um sistema de cadastro de pessoas usuárias ou login para poderem convidar outras amigas e amigos para utilizar a aplicação também.
Em nossa página inicial, já teremos cadastrado algumas pessoas e entraremos com "Evaldo" no campo "Usuário", por exemplo, com a senha "123" cadastrada.
Clicaremos no botão de "Login" e aparecerá no topo da tela uma mensagem do navegador dizendo "Usuário autenticado com sucesso!". Clicando em "Ok" nesta caixa de diálogo, nos redirecionaremos para a página inicial logada que já conhecemos.
No canto superior direito da tela, teremos o botão "Logout". Se clicarmos nele, receberemos outra mensagem do navegador indicando que "Usuário foi deslogado com sucesso!"
Se não logarmos no sistema e tentarmos acessar a página inicial deixando apenas "localhost:3000" na barra de endereço, iremos para a outra tela e receberemos a mensagem de "Error: JWT must be provided", ou seja, o "JWT deve ser fornecido" em português.
Portanto, já estamos conseguindo controlar os acessos ao AluraDocs, pois não conseguimos acessar a página inicial sem fazermos o login no sistema.
Implementaremos toda essa parte de cadastro, login e autorização de pessoas usuárias.
Para entendermos mais um aspecto que iremos trabalhar neste curso, abriremos a tela com os campos de texto de "Usuário" e "Senha" e faremos o login com os dados já cadastrados.
Na página inicial seguinte já logada, clicaremos em "Node" no primeiro item da lista e acessaremos a lista de pessoas conectadas ao documento.
Se abrirmos uma nova janela do navegador, abrirmos na página de login do projeto e inserirmos os dados de "Eduarda" que também já está cadastrada, iremos autenticá-la e acessar a parte de documentos.
Clicando em Node, tanto a janela anterior quanto a nova que abrimos aparecerão com o nome "Eduarda" abaixo de "Evaldo" na lista de "Usuários conectados:".
Se a usuária Eduarda se deslogar, a lista será atualizada com sucesso também.
Para implementarmos essas funcionalidades, aprenderemos a:
Esta última parte será para implementarmos a funcionalidade de pessoas usuárias conectadas em um mesmo documento, cujas informações serão guardadas de forma local.
Os pré-requisitos para acompanhar este curso é ter feito o curso anterior de WebSockets: implemente comunicações em tempo real com Socket.IO e MongoDB neste link, visto que aplicaremos conceitos básicos para avançarmos em Socket.IO.
Também é interessante termos feito o curso de Node.js: criptografia e tokens JWT neste link, já que precisaremos criptografar as senhas para deixá-las protegidas, além de manejarmos os tokens JWT para criar seções e autenticar pessoas usuárias.
Vamos lá?
Vamos trabalhar em uma versão aprimorada do AluraDocs solicitada pelas usuárias fictícias Eduarda e Juliana, contendo novas funcionalidades.
Na atividade anterior de "Preparando o ambiente", disponibilizamos essa versão com algumas novas modificações no HTML do projeto, e a abriremos no VSCode.
instalaremos as dependências abrindo o Terminal e escrevendo npm install
ou apenas npm i
na linha de comando, executando com a tecla "Enter".
Terminada a instalação, já teremos a pasta "node_modules" na aba lateral esquerda do Explorador do VSCode. Em seguida, executaremos npm run dev
no Terminal para vermos se o projeto já está rodando no navegador também.
Usamos estes termos no comando porque temos um script com este nome em "scripts":
do arquivo package.json
. O executamos no Terminal e receberemos a mensagem de que nos conectamos ao banco de dados com sucesso e de que estamos executando o Servidor na porta 3000
.
No navegador, digitaremos "localhost:3000" na barra de busca e acessaremos a página do AluraDocs. Conseguiremos ver as outras telas digitando "/login/index.html" ao final do endereço também.
Então tudo deverá estar funcionando e sendo servido pelo Node.js.
De volta ao VSCode, fecharemos o Terminal integrado e abriremos a pasta "public". Dentro dela, organizamos os arquivos HTML que criamos em pastas próprias, como a "documento" que conterá o arquivo index.html
que era o antigo documento HTML.
Já os arquivos JavaScript referentes à essa página, o documento.js
e socket-front-documento.js
, poderão ficar na mesma pasta, deixando tudo mais organizado.
Da mesma forma, fizemos as pastas de "cadastro" e "login" dentro de "public" com a mesma organização de "documento". Ambas possuem somente o index.html
porque ainda iremos criar os outros arquivos .js
para implementarmos as novas funcionalidades.
Ainda, a página inicial index.html
que mostra todos os documentos adicionados pelas pessoas usuárias Eduarda e Juliana está dentro da raiz "public" também.
A segunda diferença que temos em relação ao curso anterior é que, dentro da pasta "servidor", temos uma nova pasta chamada "db" que contém os arquivos relacionados ao banco de dados: dbConnect.js
e documentosDb.js
. O restante, servidor.js
e socket-back.js
, continua somente na pasta "servidor".
Essa pasta é a antiga "src", e a renomeamos para que fique mais intuitivo e específico, afinal, como significa "source" ou "fonte" em português, pode ter vários significados dependendo do contexto em que estamos trabalhando.
O nome "servidor" faz mais sentido em nosso projeto porque estamos lidando com front-end e back-end ao mesmo tempo, então é melhor indicarmos que se trata da parte de back-end.
Agora que sabemos as diferenças de pastas e arquivos em relação ao curso anterior, implementaremos uma refatoração.
Abrindo o socket-back.js
em "servidor", teremos io.on()
contendo "connection"
para escutarmos a conexão de cada cliente do Servidor, e estamos fazendo várias tratativas para diversos eventos, como "obter_documentos"
, "adicionar_documento"
, "texto_editor"
e "excluir_documento"
.
Porém, se mantivermos tudo em um mesmo arquivo, ficará muito grande conforme adicionamos funcionalidades. O ideal é termos basicamente um arquivo apenas com as específicas de uma página HTML, por exemplo.
Então, as funcionalidades de "obter_documentos"
e "adicionar_documento"
ficariam separadas em um arquivo relativo à página inicial onde os documentos são exibidos.
Vamos começar!
Dentro da pasta "servidor", criaremos uma nova pasta chamada "registrarEventos". Dentro dela, criaremos um novo arquivo chamado registrarEventosInicio.js
.
Copiaremos apenas o nome do arquivo para colarmos na primeira linha e criarmos a função registrarEventosInicio()
. Abriremos as chaves e inseriremos todo o código relacionado aos "ouvintes" do evento.
De volta ao socket-back.js
, recortaremos com as teclas "Ctrl + X" todo o bloco pertencente às chaves de socket.on()
com "obter_documentos"
e o de "adicionar_documento"
, e colaremos com "Ctrl + V" dentro da nova function
que criamos.
Porém, no momento em que mudamos o código de lugar, precisaremos receber algumas referências, que neste caso são o socket
e o io
. Então os passamos como parâmetro de registrarEventosInicio()
.
Também precisaremos importar as funções que estávamos usando no arquivo anterior, pois usaremos neste. No socket-back.js
, copiaremos todo o primeiro bloco de import
a partir de "./db/documentosDb.js"
e colaremos no início do arquivo registrarEventosInicio.js
.
Como não utilizaremos algumas dessas importações, apagaremos as linhas de atualizaDocumento
e excluiDocumento
. O caminho do import
também mudará, ficando "../db/documentosDb.js"
com dois pontos finais no começo porque o arquivo atual está dentro da pasta "registrarEventosInicio", e precisaremos subir um nível nas pastas.
import {
adicionarDocumento,
encontrarDocumento,
obterDocumentos,
} from "../db/documentosDb.js";
function registrarEventosInicio() {
socket.on("obter_documentos", async (devolverDocumentos) => {
const documentos = await obterDocumentos();
devolverDocumentos(documentos);
});
socket.on("adicionar_documento", async (nome) => {
const documentoExiste = (await encontrarDocumento(nome)) !== null;
if (documentoExiste) {
socket.emit("documento_existente", nome);
} else {
const resultado = await adicionarDocumento(nome);
if (resultado.acknowledged) {
io.emit("adicionar_documento_interface", nome);
}
}
});
}
Voltaremos ao socket-back.js
e apagaremos adicionarDocumento
e obterDocumentos
, afinal não o usaremos mais neste arquivo.
Em seguida, recortaremos com as teclas "Ctrl + X" todos os blocos contendo os eventos relacionados à página de um documento específico, o "selecionar_documento"
, "texto_editor"
e "excluir_documento"
.
Dentro da pasta "registrarEventos", criaremos um novo arquivo chamado registrarEventosDocumento.js
e inseriremos uma nova function registrarEventosDocumento()
que também receberá socket, io
.
Dentro da função, colaremos o que acabamos de recortar do socket-back.js
. De volta a este, copiaremos com "Ctrl + C" o primeiro bloco de import
e colaremos no topo do arquivo novo que acabamos de criar.
Precisaremos fazer a exportação das funções dos arquivos que criamos usando. Na última linha de registrarEventosDocumento.js
, escreveremos export default
de registrarEventosDocumento
.
Também atualizaremos o caminho do import
para "../db/documentosDb.js"
com dois pontos finais no começo.
import {
atualizaDocumento,
encontrarDocumento,
excluirDocumento,
} from "../db/documentosDb.js";
function registrarEventosDocumento(socket, io) {
socket.on("selecionar_documento", async (nomeDocumento, devolverTexto) => {
socket.join(nomeDocumento);
const documento = await encontrarDocumento(nomeDocumento);
if (documento) {
devolverTexto(documento.texto);
}
});
socket.on("texto_editor", async ({ texto, nomeDocumento }) => {
const atualizacao = await atualizaDocumento(nomeDocumento, texto);
if (atualizacao.modifiedCount) {
socket.to(nomeDocumento).emit("texto_editor_clientes", texto);
}
});
socket.on("excluir_documento", async (nome) => {
const resultado = await excluirDocumento(nome);
if (resultado.deletedCount) {
io.emit("excluir_documento_sucesso", nome);
}
});
}
export default registrarEventosDocumento;
Depois, iremos ao registrarEventosInicio.js
e exportaremos registrarEventosInicio
ao final do código também.
Salvaremos, retornaremos ao socket-back.js
e importaremos essas funções para que as funcionalidades sejam executadas, pois apenas fizemos uma refatoração para separá-las em diferentes arquivos.
Dentro de io.on()
contendo "connection"
, escreveremos registrarEventosInicio()
, aceitaremos o autoimport do VSCode e passaremos socket, io
como parâmetro.
Na linha seguinte, inseriremos o registrarEventosDocumento()
passando socket, io
como parâmetros também.
import registrarEventosInicio from "./registrarEventos/inicio.js";
import registrarEventosDocumento from "./registrarEventos/documento.js";
import io from "./servidor.js";
import io from "./servidor.js";
io.on("connection", (socket) => {
registrarEventosInicio(socket, io);
registrarEventosDocumento(socket, io);
});
Se salvarmos, iremos receber a mensagem de que conectamos ao banco de dados com sucesso e que o servidor está "escutando" na porta 3000
.
De volta à página no navegador, iremos para "localhost:3000" e acessaremos a página com os documentos listados, em que conseguiremos clicar, abrir um documento e até excluir se quisermos.
Portanto, as funcionalidades devem estar funcionando corretamente.
Vamos começar implementando a parte de cadastro do AluraDocs.
No navegador, abriremos a página de "localhost:3000" e adicionaremos "/cadastro/index.html" em seguida na barra de endereço.
Quando fazemos isso, aparecerá o formulário de "Cadastro" com os campos de "Usuário" e Senha". Ainda não estão funcionando, mas testaremos colocando uma informação qualquer nos inputs e clicando em "Cadastrar".
Somente atualizaremos a página e os campos estarão em branco, então vamos fazer um código JavaScript que pegará esses dados e enviará ao servidor para cadastrarmos a pessoa usuária no banco de dados.
Abrindo o VSCode, acessaremos a pasta "public > cadastro" onde teremos o index.html
. Neste arquivo, encontraremos o formulário em <form
com o atributo id="form-cadastro">
.
Copiaremos essa identificação porque iremos capturar esse formulário em um arquivo JavaScript para pegarmos os dados digitados nos campos de texto.
Dentro da pasta "cadastro", criaremos um novo arquivo chamado cadastro.js
. Faremos a importação deste arquivo no HTML. No index.html
, iremos à última linha antes do fim do </body>
e adicionaremos uma tag <script src="cadastro.js"
seguido do atributo type="module"></script>
, permitindo que utilizemos os módulos do document script.
<!DOCTYPE html>
<html lang="pt-BR">
//código omitido
<main class="w-75 mx-auto">
<hr>
<h2 class="display-6 mb-3">Cadastro</h2>
<form id="form-cadastro">
//código omitido
</main>
//código omitido
<script src="/socket.io/socket.io.js"></script>
<script src="cadastro.js" type="module"></script>
</body>
</html>
Salvaremos e voltaremos ao cadastro.js
para escrevermos const form
igual a document.getElementById()
recebendo "form-cadastro"
.
Em seguida, digitaremos form.addEventListener()
recebendo "submit"
. Adicionaremos um "ouvinte" para o submit do front-end mesmo, então assim que alguém submeter o formulário, conseguiremos executar uma função call-back que será o segundo parâmetro.
Aplicaremos uma arrow function recebendo o evento
de submissão. Como primeira linha de código dentro das chaves, teremos evento.preventDefault()
para evitarmos que o formulário atualize a página, já que é um comportamento padrão do HTML.
const form = document.getElementById("form-cadastro");
form.addEventListener("submit", (evento) => {
evento.preventDefault();
});
Para pegarmos os dados digitamos nos campos de "Usuário" e "Senha", voltaremos ao index.html
e, dentro do <form>
, teremos a tag <input>
contendo um id
chamado "input-usuario"
e, no campo de senha, teremos seu <input>
com a identificação "input-senha"
.
Copiaremos o primeiro id
e, para obtermos o valor digitado, voltaremos para cadastro.js
, escreveremos const usuario
sendo igual a form[]
contendo "input-usuario"
.
Esta é uma forma de acessarmos os inputs dos formulários de front-end, afinal serão como propriedades do objeto form
. Sabendo que "input-usuario"
é exatamente nosso campo, adicionaremos .value
para obtermos exatamente o que foi escrito no campo de texto.
Faremos a mesma coisa para o "input-senha"
na linhas eguinte. Por fim, faremos um console.log()
de usuario, senha
para vermos se está funcionando.
const form = document.getElementById("form-cadastro");
form.addEventListener("submit", (evento) => {
evento.preventDefault();
const nome = form["input-usuario"].value;
const senha = form["input-senha"].value;
console.log(usuario, senha);
});
Salvaremos e voltaremos ao navegador para atualizá-lo.
Apertando a tecla "F12", abriremos a aba lateral direita do Inspecionador de código e abriremos o "Console". Quando digitarmos algum valor no campo de "Uusuario", como "Evaldo" e "123" em "senha" que já temos cadastrado por exemplo.
Clicando no botão de "Cadastrar", exibiremos a resposta Evaldo 123
no Console do navegador.
De volta ao VSCode, emitiremos esses dados para o back-end. Ao invés de aplicarmos o console.log()
, escreveremos a função emitirCadastrarUsuario()
passando um objeto com as propreidades { usuario, senha }
que ja criamos.
Ainda iremos criá-la em um arquivo separado seguindo a mesma organização que estamos seguindo no curso anterior.
Dentro da pasta "cadastro", criaremos um novo arquivo socket-front-cadastro.js
onde teremos a nova const emitirCadastrarUsuario()
.
Para usarmos o socket
, criaremos uma const
com este nome sendo igual a io()
. Veremos se este io()
está sendo disponibilizado, mas só será através de uma tag específica.
Abrindo o index.html
de "cadastro" novamente, teremos o <script>
com src="/socket.io/socket.io.js"
. Isso já será suficiente para disponibilizarmos o io()
que estamos usando em socket-front-cadastro.js
.
Dentro das chaves da nova função que criamos, escreveremos socket.emit()
recebendo o evento "cadastrar usuário ". Depois, receberemos os
dadosde cadastro como parâmetro de
emitirCadastrarUsuario(), que também será o segundo parâmetro de
.emit()`.
Por fim, exportaremos com export
de { emitirCadastrarUsuario }
ao final do arquivo.
const socket = io();
function emitirCadastrarUsuario(dados) {
socket.emit("cadastrar_usuario", dados);
}
export { emitirCadastrarUsuario };
Salvaremos' e voltaremos ao cadastro.js
para importarmos o emitirCadastrarUsuario()
no início do arquivo a partir de "./socket-front-cadastro.js"
.
import { emitirCadastrarUsuario } from "./socket-front-cadastro.js";
const form = document.getElementById("form-cadastro");
form.addEventListener("submit", (evento) => {
evento.preventDefault();
const nome = form["input-usuario"].value;
const senha = form["input-senha"].value;
emitirCadastrarUsuario({ nome, senha });
});
Para vermos se está funcionando, iremos ao "servidor" e acessaremos a pasta "registrarEventos" onde criaremos um novo arquivo chamado registrarEventosCadastro.js
para fazer referência à página de cadastro.
Dentro dele, teremos uma nova função chamada registrarEventosCadastro()
recebendo socket, io
como parâmetros. Essa é exatamente a organização que estamos seguindo da refatoração da aula passada.
Colocaremos todas as funcionalidades de Socket.IO relacionadas à página de cadastro, sendo a primeira socket.on()
contendo "cadastrar_usuario"
. Como segundo parâmetro, teremos uma função call-back que receberá os dados como parâmetros também, e depois faremos um console.log()
de dados
para atestarmos se as informações estão chegando no servidor usadas no front-end.
Ao final do arquivo, faremos um export default
de registrarEventosCadastro
.
function registrarEventosCadastro(socket, io) {
socket.on("cadastrar_usuario", (dados) => {
console.log(dados);
});
}
export default registrarEventosCadastro;
Salvaremos e abriremos o arquivo socket-back.js
onde importaremos a nova função e a adicionaremos nas chaves de io.on()
.
Dentro dos parênteses de registrarEventosCadastro()
, passaremos socket.io
como parâmetros.
import registrarEventosDocumento from "./registrarEventos/registrarEventosDocumento.js";
import registrarEventosInicio from "./registrarEventos/registrarEventosInicio.js";
import registrarEventosCadastro from "./registrarEventos/registrarEventosCadastro.js";
import io from "./servidor.js";
io.on("connection", (socket) => {
registrarEventosInicio(socket, io);
registrarEventosDocumento(socket, io);
registrarEventosCadastro(socket, io);
});
Feito isso, salvaremos e veremos se os dados estão trafegando com sucesso.
De volta à página atualizada no navegador, escreveremos "Juliana" no campo de "Usuario" passando a senha "123". Apertaremos "Enter" ou o botão de "Cadastrar" para enviarmos.
Voltaremos ao VSCode com o Terminal aberto e veremos que há um objeto criado com a propriedade usuario:
e senha:
contendo os valores que adicionamos nos campos de texto.
A passagem de dados de front-end para back-end já estarão funcionando.
Para finalizarmos, mudaremos o nomes dos arquivos de "registrarEventos" porque os caminhos das importações estão muito grandes, e é interessante reduzi-los.
Clicaremos em registrarEventosCadastro.js
e apertaremos a tecla "F2" para renomearmos para cadastro.js
apenas. No momento em que renomeamos o arquivo, o VSCode nos mostra uma caixa perguntando se queremos atualizar as importações desses arquivos.
Clicaremos em "Sim" para fazermos a alteração automaticamente.
Faremos a mesma coisa para os outros dois arquivos, em que registrarEventosDocumento.js
será apenas documento.js
e registrarEventosInicio.js
para apenas inicio.js
, aceitando as alterações automáticas nos demais arquivos.
import registrarEventosDocumento from "./registrarEventos/documento.js";
import registrarEventosInicio from "./registrarEventos/inicio.js";
import registrarEventosCadastro from "./registrarEventos/cadastro.js";
//código omitido
Com isso, salvaremos o arquivo socket-back.js
com os caminhos das importações atualizadas e abriremos o Terminal integrado para vermos que a aplicação está funcionando normalmente.
Abrindo a página no navegador, atualizaremos o formulário e digitaremos "Juliana" com a senha "123" para cadastrarmos. Clicando no botão e abrindo o Terminal do VSCode novamente, veremos que aparecerá os valores que inserimos.
A seguir, pegaremos esses dados e começaremos a manipular o banco de dados para efetivamente criarmos as pessoas usuárias do AluraDocs.
O curso WebSockets: implemente autenticação e avance no Socket.IO possui 193 minutos de vídeos, em um total de 48 atividades. Gostou? Conheça nossos outros cursos de Node.JS em Programação, ou leia nossos artigos de Programação.
Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:
Impulsione a sua carreira com os melhores cursos e faça parte da maior comunidade tech.
1 ano de Alura
Assine o PLUS e garanta:
Formações com mais de 1500 cursos atualizados e novos lançamentos semanais, em Programação, Inteligência Artificial, Front-end, UX & Design, Data Science, Mobile, DevOps e Inovação & Gestão.
A cada curso ou formação concluído, um novo certificado para turbinar seu currículo e LinkedIn.
No Discord, você tem acesso a eventos exclusivos, grupos de estudos e mentorias com especialistas de diferentes áreas.
Faça parte da maior comunidade Dev do país e crie conexões com mais de 120 mil pessoas no Discord.
Acesso ilimitado ao catálogo de Imersões da Alura para praticar conhecimentos em diferentes áreas.
Explore um universo de possibilidades na palma da sua mão. Baixe as aulas para assistir offline, onde e quando quiser.
Acelere o seu aprendizado com a IA da Alura e prepare-se para o mercado internacional.
1 ano de Alura
Todos os benefícios do PLUS e mais vantagens exclusivas:
Luri é nossa inteligência artificial que tira dúvidas, dá exemplos práticos, corrige exercícios e ajuda a mergulhar ainda mais durante as aulas. Você pode conversar com a Luri até 100 mensagens por semana.
Aprenda um novo idioma e expanda seus horizontes profissionais. Cursos de Inglês, Espanhol e Inglês para Devs, 100% focado em tecnologia.
Transforme a sua jornada com benefícios exclusivos e evolua ainda mais na sua carreira.
1 ano de Alura
Todos os benefícios do PRO e mais vantagens exclusivas:
Mensagens ilimitadas para estudar com a Luri, a IA da Alura, disponível 24hs para tirar suas dúvidas, dar exemplos práticos, corrigir exercícios e impulsionar seus estudos.
Envie imagens para a Luri e ela te ajuda a solucionar problemas, identificar erros, esclarecer gráficos, analisar design e muito mais.
Escolha os ebooks da Casa do Código, a editora da Alura, que apoiarão a sua jornada de aprendizado para sempre.