
Compartilhar:
Educador de tecnologia com um jeito amigável, pai de família, defensor da diversidade, provavelmente discuto um pouco demais. Ex-engenheiro de backend. Fale comigo sobre JavaScript (front-end ou back-end), o incrível Vue.js, DevOps, DevSecOps e tudo relacionado ao JamStack. Escritor no DEV.to
Criando um clone do Slack usando Vue – Parte 1
Tempo de leitura: 69 minutos
Criação de uma aplicação de bate-papo em Vue.js semelhante ao Slack
Você já pensou em criar um aplicativo de bate-papo, mas ficou em dúvida sobre quais recursos incluir ou simplesmente sobre como desenvolvê-lo? Nesta postagem, você vai criar um clone do software de bate-papo favorito de todo mundo, o Slack. Usando o Vue.js, o framework favorito de todo mundo. E a Conversation API da Vonage, o serviço de conversação favorito de todo mundo.
Esta postagem é a primeira parte de uma série de tutoriais que vai nos levar de um diretório vazio até uma aplicação real, com muitos dos recursos que definem o gênero do Slack.
Aqui estão algumas das coisas que você aprenderá neste post:
Se você estiver interessado no aplicativo de demonstração completo, sem seguir o guia, confira o repositório no GitHub do meu clone do Slack em Vue.js até o momento.
Pré-requisitos
Node e NPM
Para começar, você precisará ter o Node e o NPM instalados. Este guia utiliza o Node 8 e o NPM 6. Verifique se eles estão instalados e atualizados.
Tanto o Node quanto o NPM precisam estar instalados e na versão correta. Acesse nodejs.org, baixe e instale a versão correta, caso ainda não a tenha.
Nossa CLI
Para configurar seu aplicativo, você precisará instalar nossa CLI. Instale-a usando o NPM no terminal.
Você pode verificar se está usando a versão correta com este comando. No momento em que escrevi este artigo, eu estava usando a versão 0.4.9-beta-3.
Para acompanhar as etapas deste artigo, lembre-se de criar um Account gratuito na Vonage e configurar a CLI com a chave e o segredo da API encontrados no seu painel de controle.
CLI do Express.js
Instalar Express Generator. Você usará essa biblioteca para gerar um servidor Express.js básico.
Você pode verificar se está usando a versão correta com este comando. No momento em que escrevi este artigo, eu estava usando a versão 4.16.1.
CLI do Vue.js
Instale o Vue CLI. Você usará essa biblioteca para gerar um aplicativo cliente básico em Vue.js.
Você pode verificar se está usando a versão correta com este comando. No momento em que escrevi este artigo, eu estava usando a versão 4.1.2 da @vue/cli.
Começando do zero
Esta série vai guiá-lo desde um diretório vazio até a criação de um aplicativo de bate-papo real, utilizando o Express.js como servidor.
Criar uma pasta de projeto
Antes de mais nada, crie uma pasta para o seu trabalho.
E, mude para o novo diretório.
Gerar um servidor Express.js
Em seguida, crie um servidor básico usando o gerador do Express.js. O que eu adoro nessa CLI é que ela configura o executável do servidor e o aplicativo de forma independente um do outro. Ou seja, ela segue a filosofia do extremamente leve e bacana Express Hello World. Ele divide isso em um arquivo executável igualmente bacana para configurar o servidor e o ambiente bin/wwwe o próprio aplicativo app.js.
Como o aplicativo é, em sua essência, uma API, é melhor evitar a instalação de qualquer componente destinado ao gerenciamento de arquivos de modelo. Para isso, use a --no-view opção.
Se você pretende usar git como seu sistema de controle de versão, você deve considerar o uso --git para gerar o .gitignore arquivo.
Como você já está no diretório do projeto, especifique a --force opção e use . como diretório. Assim, a ferramenta irá gerar o aplicativo no diretório atual sem problemas.
Em seguida, instale as dependências.
Executar o servidor Express.js localmente
Depois que o servidor for criado e as dependências instaladas, você pode iniciá-lo para verificar se tudo está funcionando conforme o esperado.
Você pode verificar se está funcionando na URL padrão, localhost:3000.

Rotas e controladores
O aplicativo gerado inclui o roteamento necessário. O roteamento se refere à determinação de como um aplicativo lida com uma solicitação direcionada a uma URL e um método específicos (GET, POST etc.). Os controladores, por outro lado, são responsáveis pelo fluxo de execução do aplicativo. O aplicativo gerado não cria controladores e utiliza os roteadores para retornar uma resposta.
Crie um novo diretório para os controladores.
Crie um novo controlador neste diretório com o nome server.js.
Abrir controllers/server.js e crie o primeiro método para o servidor.
// controllers/server.js
exports.status = function(req, res, next) {
res.json({
status: 'ok'
});
};Esse controlador poderia, posteriormente, ser responsável por fornecer ao cliente uma condição, com base em várias verificações, como se o serviço de chat está em funcionamento ou se é possível se conectar aos dados. A ideia é que, caso ocorra algum problema no servidor, o cliente receba a mensagem de erro, lide com ela de maneira adequada e informe ao usuário o que aconteceu.
Para solicitar esse método do controlador, crie uma nova rota no diretório de rotas existente com o nome server.js.
Abra routes/server.js e adicione o código mostrado abaixo.
// routes/server.js
var express = require('express');
var router = express.Router();
var serverController = require('../controllers/server');
router.get('/status', serverController.status);
module.exports = router;Isso direciona uma rota (/status) para um método do controlador (serverController.status). A rota entrega o resultado do método do controlador ao cliente como uma resposta.
Para adicionar essa rota ao aplicativo, você precisa editar app.js e fazer essas alterações.
// app.js
- var indexRouter = require('./routes/index');
- var usersRouter = require('./routes/users');
...
- app.use('/', indexRouter);
- app.use('/users', usersRouter);
+ app.use('/api/server', require('./routes/server'));Então você pode prosseguir e excluir o routes/index.js e routes/users.js arquivos.
Reinicie o aplicativo com npm start; em seguida, você poderá acessar a nova rota em localhost:3000/api/server/status.

Criação de um cliente
Use o Vue CLI para criar um novo aplicativo cliente.
Gerar um cliente Vue.js
Execute o comando `create` com o Vue CLI. Essa ferramenta gera um aplicativo Vue simples que servirá de base para o nosso cliente de bate-papo. Ela exibe algumas opções, e você pode selecionar as predefinidas.
O cliente é gerado no client diretório especificado no comando. Ele também é executado npm install automaticamente.
Agora, vá para o client diretório.
Para executar o cliente, use este comando. Observe que é diferente da forma como você executa o servidor.
Então, você pode acessar seu cliente em localhost:8080. Você vai perceber que ele usa uma porta diferente por padrão e, no ambiente de desenvolvimento, isso nos ajuda, como você verá a seguir, quando executarmos o servidor e o cliente simultaneamente.

Recarregamento dinâmico dos arquivos do servidor Express.js
Normalmente, no processo de desenvolvimento, a maioria das pessoas prefere que o aplicativo atualize automaticamente os arquivos à medida que são editados. Para isso, vamos configurar o servidor para usar o o nodemon para servir os arquivos.
Instalar o Nodemon
Se você ainda estiver no client diretório anterior, você pode voltar ao diretório principal do projeto subindo um nível com este comando, .. que indica um diretório pai.
Agora, instale o Nodemon como uma dependência de desenvolvimento. Para instalar uma dependência de desenvolvimento, basta adicionar --save-dev como uma opção do comando.
Após a instalação, você pode editar o package.json arquivo e modificar o start script conforme mostrado aqui.
+ "dev:server": "nodemon ./bin/www",
"start": "node ./bin/www"Quando você executar o aplicativo com npm run dev:server, ele utilizará o Nodemon. O Nodemon monitora os arquivos do aplicativo e reinicia o serviço automaticamente sempre que algum arquivo for alterado.
Observação: Isso também inclui metadados do arquivo, como permissões e data de modificação.
Execute o servidor e o cliente simultaneamente
À medida que avançarmos neste guia, você precisará executar o cliente e o Express.js simultaneamente. Há um pacote Concurrently para isso, que facilita muito a integração de Applications distintas entre si.
Instalar simultaneamente
Instale o Concurrently, também como dependência de desenvolvimento.
Inicie os dois ambientes de desenvolvimento
Modifique o package.json arquivo do servidor, conforme mostrado aqui. Na última seção, adicionamos um dev:server script que executava o servidor usando o Nodemon. Agora, vamos adicionar um dev:client script na raiz do projeto para executar o cliente a partir daqui também.
"dev:server": "nodemon ./bin/www",
+ "dev:client": "cd client && npm run serve",
"start": "node ./bin/www"
Agora, adicione esta linha para combinar os dois usando o Concurrently. Você notará a opção --kill-others-on-fail que significa que o Concurrently interromperá todos os serviços caso seja detectado um erro grave. Sem isso, se o Node ou o Webpack (que atende ao cliente) encontrasse um erro, você precisaria reiniciar o Concurrently para que tanto o cliente quanto o servidor voltassem a funcionar.
"dev:server": "nodemon ./bin/www",
"dev:client": "cd client && npm run serve",
+ "dev": "concurrently --kill-others-on-fail 'npm run dev:server' 'npm run dev:client'",
"start": "node ./bin/www"
Ao executar o aplicativo com npm run dev, ele iniciará o servidor e o cliente simultaneamente em localhost:3000 e localhost:8080 , respectivamente.

Solicitações de API por proxy ao servidor Express.js
Para enviar solicitações do cliente ao servidor no ambiente de desenvolvimento, você precisará configurar um proxy. É possível configurar o Vue.js para redirecionar por meio de um proxy quaisquer solicitações que comecem com uma rota específica.
Configurar o proxy
Para isso, crie um novo arquivo dentro do client diretório chamado vue.config.js. Portanto, acesse o diretório do cliente.
Crie um arquivo de configuração vazio.
Cole o código a seguir.
// vue.config.js
module.exports = {
devServer: {
proxy: {
"/api": {
target: "http://localhost:3000",
secure: false
}
}
}
};Esse código informa ao Vue.js que, ao ser executado, devServer que qualquer rota que corresponda a /api devem ser redirecionadas para http://localhost:3000. Essa é a URL do servidor ao executar o dev script ou o dev:server script diretamente.
Criar um serviço consumidor de API
Para enviar solicitações do Vue.js ao nosso servidor a partir do cliente, instale o Axios, que é uma cliente HTTP , para uso em código do lado do navegador.
Agora que você já instalou o Axios e pode encaminhar solicitações entre o servidor e o cliente, é hora de fazer essas solicitações. No diretório src/ , crie uma nova pasta chamada services para conter todos os arquivos do serviço de API.
Crie um serviço de API abstrato, que definirá o caminho para os serviços de API subsequentes. Lembre-se de que, no ambiente de desenvolvimento, /api vai atuar como proxy para o servidor.
Use o código a seguir para criar um serviço de API abstrato que retorne uma instância do Axios.
// src/services/Api.js
import axios from 'axios'
export default() => {
return axios.create({
baseURL: `/api`,
headers: {'Cache-Control': 'no-cache, no-store, no-transform'}
})
}
Você já criou um server/status ponto de extremidade no servidor, que, quando o servidor estava em execução, você podia acessar por meio de localhost:3000/api/server/status.
Para acessar esse endpoint a partir do aplicativo cliente, crie um arquivo para o serviço.
E adicione este código para criar um fetchStatus método no novo Server serviço.
// src/services/Server.js
import Api from '@/services/Api'
export default {
fetchStatus () {
return Api().get('server/status')
}
} Solicitar o status do servidor no cliente
Agora que você criou um serviço para fazer solicitações ao servidor, importe o serviço para o seu App.vue componente.
Abrir App.vue e adicione as linhas conforme mostrado aqui.
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
- <HelloWorld msg="Welcome to Your Vue.js App"/>
+ <HelloWorld v-if="!!server.status && server.status === 'ok'" msg="Welcome to Your Vue.js App"/>
+ <template v-else>
+ <HelloWorld msg="Connecting..."/>
+ </template>
</div>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
+ import ServerService from '@/services/Server'
export default {
name: 'App',
components: {
HelloWorld
+ },
+ data () {
+ return {
+ server: {},
+ }
+ },
+ mounted () {
+ this.getServerStatus()
+ },
+ methods: {
+ getServerStatus () {
+ ServerService.fetchStatus()
+ .then((response) => {
+ this.server = response.data
+ })
+ }
}
}
</script>
# ...
Aqui, ele reutiliza o HelloWorld para exibir o status da solicitação ao usuário.
Observação: Lembre-se de que, provavelmente, você ainda está no diretório do cliente nesta etapa. Para iniciar (ou reiniciar) o ambiente de desenvolvimento novamente, é necessário
npm run devdeve ser feito no diretório do servidor (cd ..para passar do cliente para o servidor).
Assim que estiver em execução, você poderá acessar o cliente em localhost:8080. Se você for rápido o suficiente, poderá ver a mensagem “Conectando...”.

Telas de carregamento com Tailwind e FontAwesome
Ao se conectar ao servidor na última seção, você terá reutilizado o HelloWorld . Agora, usando o Tailwind CSS e o FontAwesome, crie uma tela de carregamento para o cliente.
Se você quiser praticar isso fora deste aplicativo, escrevi sobre Como usar o Tailwind CSS com o Vue.js em um guia separado, feito especialmente para você.
Instalar o Tailwind CSS
Para usar o Tailwind CSS no cliente, precisamos instalá-lo como uma dependência e configurar o cliente para utilizá-lo.
Observação: Esta instalação é para o cliente, não para o servidor. Portanto, certifique-se de estar no
clientdiretório.
Configurar o cliente Vue.js para o Tailwind CSS
Quando o aplicativo cliente é compilado, ele procura por um postcss.config.js arquivo de configuração que o Vue.js usa para saber como processar o CSS. As instruções de instalação do Tailwind CSS indicam que você deve adicioná-lo como um plug-in na sua cadeia de compilação.
O aplicativo de demonstração gerado pelo Vue não cria um postcss.config.js arquivo. Faça isso agora.
E configure-o usando este código.
// postcss.config.js
const autoprefixer = require('autoprefixer');
const tailwindcss = require('tailwindcss');
module.exports = {
plugins: [
tailwindcss,
autoprefixer,
],
}; Adicionar o Tailwind como um recurso CSS
O aplicativo de demonstração também não cria nenhum recurso CSS. Em vez disso, ele usa CSS dentro dos componentes do Vue.js, como mostram muitos guias. Portanto, para incluir o Tailwind, crie um arquivo CSS básico dentro do diretório `assets` usando esses comandos ou seu editor.
Use este código para incluir a base, os componentes e os utilitários do Tailwind CSS na sua compilação de CSS. Copie e cole-o no seu novo index.css arquivo.
/* src/assets/styles/index.css */
@tailwind base;
@tailwind components;
@tailwind utilities; Incluir o Tailwind CSS
Agora edite seu main.js arquivo para importar index.css para o cliente.
// src/main.js
import Vue from 'vue';
import App from './App.vue';
+ import './assets/styles/index.css';
Vue.config.productionTip = false;
new Vue({
render: h => h(App),
}).$mount(`#app`);

Observação: O Tailwind CSS usa o preflight (baseado no normalize.css) para redefinir todos os estilos em diferentes navegadores para o mesmo padrão. Você vai perceber que isso alterou alguns estilos padrão. Não se preocupe; você vai substituir tudo isso em breve.
Instalar o FontAwesome
A criação de um indicador de carregamento será feita com um círculo com entalhes do Font Awesome. Instale-o no cliente com este comando.
Incluir o FontAwesome
Editar main.js novamente e adicione este código.
// src/main.js
import Vue from 'vue';
import App from './App.vue';
+ import { library } from '@fortawesome/fontawesome-svg-core'
+ import { fas } from '@fortawesome/free-solid-svg-icons'
+ import { far } from '@fortawesome/free-regular-svg-icons'
+ import { FontAwesomeIcon, FontAwesomeLayers } from '@fortawesome/vue-fontawesome'
import './assets/styles/index.css';
+ library.add(fas, far)
+ Vue.component('font-awesome-icon', FontAwesomeIcon)
+ Vue.component('font-awesome-layers', FontAwesomeLayers)
Vue.config.productionTip = false;
new Vue({
render: h => h(App),
}).$mount(`#app`);
Criar a tela de carregamento
Para criar um novo componente Vue.js para usar como tela de carregamento, adicione um novo arquivo de componente com este comando ou no seu editor.
Agora, usando esse código, adicione o spinner a uma sobreposição translúcida em tela cheia.
<template>
<div class="w-screen h-screen fixed block top-0 left-0 bg-white opacity-75 z-50 flex">
<span class="text-green-500 opacity-75 top-1/2 m-auto text-center">
<font-awesome-icon icon="circle-notch" class="fa-spin fa-5x mb-2"/>
<p class="text-base">
{{ message }}
</p>
</span>
</div>
</template>
<script>
export default {
name: 'Loading',
props: {
message: String
}
}
</script>
E, adicione a tela de carregamento editando App.vue e substituindo a reutilização de HelloWorld.vue pelo novo componente.
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
<HelloWorld v-if="!!server.status && server.status === 'ok'" msg="Welcome to Your Vue.js App"/>
<template v-else>
- <HelloWorld msg="Connecting..."/>
+ <Loading message="Connecting..." />
</template>
</div>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
+ import Loading from '@/components/Loading.vue'
import ServerService from '@/services/Server'
export default {
name: 'App',
components: {
- HelloWorld
+ HelloWorld,
+ Loading
},
data () {
return {
server: {},
}
},
mounted () {
this.getServerStatus()
},
methods: {
getServerStatus () {
ServerService.fetchStatus()
.then((response) => {
this.server = response.data
})
}
}
}
</script>
...

Observação: Para testar isso, você pode modificar a resposta
statusno diretóriocontrollers/server.jspara retornar algo diferente deok.
Como lidar com erros do servidor no cliente
É hora de adicionar o tratamento de erros ao cliente.
Erros na captura de solicitações
Editar App.vue e adicione o código a seguir.
...
<script>
import HelloWorld from './components/HelloWorld.vue'
import Loading from '@/components/Loading.vue'
import ServerService from '@/services/Server'
export default {
name: 'App',
components: {
HelloWorld,
Loading
},
data () {
return {
server: {},
+ error: null
}
},
mounted () {
this.getServerStatus()
},
methods: {
getServerStatus () {
ServerService.fetchStatus()
.then((response) => {
this.server = response.data
})
+ .catch((err) => {
+ this.error = { title: 'Couldn\'t connect to Server', message: 'There may be a problem with your connection. Please check and try again.', reason: err.reason }
+ })
}
}
}
</script>
...Agora, se houver um erro proveniente do servidor, ele será detectado pelo cliente e adicionado aos dados do componente.
Criar um componente de erro
Para exibir um erro, crie um componente Error.vue componente vazio usando este comando ou seu editor.
Adicione este código, que também utiliza ícones (e camadas) do FontAwesome para gerar um gráfico adequado.
<template>
<div class="flex h-screen">
<div class="m-auto text-center w-2/3">
<font-awesome-layers class="fa-10x mb-10">
<font-awesome-icon icon="globe-americas" transform="grow-4" class="text-gray-500"/>
<font-awesome-icon :icon="['far', 'circle']" transform="grow-5" class="outline text-white"/>
<font-awesome-icon icon="times" class="cross text-red-500" transform="shrink-8 right-5 up-5"/>
</font-awesome-layers>
<h1 class="text-3xl mb-3 text-gray-800">{{ error.title }}</h1>
<p class="text-base text-gray-800">{{ error.message }}</p>
<p class="invisible">{{ error.reason }}</p>
</div>
</div>
</template>
<script>
export default {
name: 'Error',
props: {
error: Object
}
}
</script>
<style scoped>
.outline path {
stroke: white;
stroke-width: 20px;
}
.cross path {
stroke: white;
stroke-width: 20px;
}
</style>
Exibir um erro do servidor no cliente
Mais uma vez, ao editar App.vue, adicione o código conforme mostrado aqui. Remova a imagem ao mesmo tempo.
<template>
<div id="app">
- <img alt="Vue logo" src="./assets/logo.png">
<HelloWorld v-if="!!server.status && server.status === 'ok'" msg="Welcome to Your Vue.js App"/>
<template v-else>
- <Loading message="Connecting..." />
+ <Loading v-if="!error" message="Connecting..." />
+ <Error v-else :error="error" />
</template>
</div>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
+ import Error from '@/components/Error.vue'
import Loading from '@/components/Loading.vue'
import ServerService from '@/services/Server'
export default {
name: 'App',
components: {
HelloWorld,
+ Error,
Loading
},
data () {
return {
server: {},
error: null
}
},
mounted () {
this.getServerStatus()
},
methods: {
getServerStatus () {
ServerService.fetchStatus()
.then((response) => {
this.server = response.data
})
.catch((err) => {
this.error = { title: 'Couldn\'t connect to Server', message: 'There may be a problem with your connection. Please check and try again.', reason: err.reason }
})
}
}
}
</script>
...
Agora, o cliente exibe os erros enviados pelo servidor.

Observação: Para ver isso em funcionamento, você pode modificar o aplicativo do seu servidor
controllers/server.js, substituindores.jsonporres.sendStatus(500)para fornecer ao cliente um código de erro 500.
Usar arquivos de ambiente Dotenv
Você não deve armazenar chaves e credenciais de forma estática no seu servidor, mas, principalmente, não no seu cliente.
Instalar o Dotenv
Instale dotenv para que você possa definir variáveis de ambiente e lê-las em seu aplicativo.
Observação: Você pode estar no diretório do cliente nesta etapa. Use
cd ..para passar do diretório do cliente para o do servidor.
Criar um arquivo de ambiente
Crie um arquivo de configuração vazio para o servidor usando este comando ou seu editor.
Configurar o ambiente
Agora, edite .env e adicione esta configuração de exemplo ao arquivo. O token e o ID não são reais.
Observação:
.envos arquivos são ignorados pelo Git devido ao.gitignoreadição de arquivos.envpor padrão. Fazer o commit do seu.envarquivo é tão seguro quanto codificar suas credenciais diretamente no código. Isso também ressalta que essas credenciais são específicas para este ambiente, que está sendo executado localmente. Se você for fazer a implantação, será necessário gerenciar o ambiente no servidor de uma maneira diferente. O Heroku, por exemplo, oferece um painel de controle para configurar o ambiente.
Carregar o ambiente
Agora, edite o arquivo principal do servidor para incluir o ambiente no momento em que o aplicativo for iniciado. Edite bin/www (ele não tem extensão de arquivo) conforme mostrado aqui.
#!/usr/bin/env node
+ require('dotenv').config();
/**
* Module dependencies.
*/
... Passar valores do ambiente do servidor para o cliente
A primeira variável de ambiente a ser compartilhada com o cliente é VONAGE_DEFAULT_CONVERSATION_ID, o ID padrão da “sala” para o chat! Você voltará aqui mais tarde para editar o valor dessa variável de ambiente.
Editar controllers/server.js e adicione o código mostrado aqui.
// controllers/server.js
exports.status = function(req, res, next) {
res.json({
+ defaultConversationId: process.env.VONAGE_DEFAULT_CONVERSATION_ID,
status: 'ok'
});
}; Terminais de usuário para autenticação de clientes
Nas próximas partes desta série, um provedor de identidade irá gerenciar os dados do usuário enviados pelo servidor. Enquanto isso, simule essas informações também e volte para editá-las quando tiver os dados reais.
Criar um endpoint de usuário
Crie um endpoint de usuário criando primeiro um user.js controlador usando seu editor ou este comando.
Inserindo esse código.
// controllers/user.js
exports.session = function(req, res, next) {
res.json({
user: process.env.VONAGE_USER,
token: process.env.VONAGE_USER_TOKEN
});
};Agora, crie uma rota para acessar os endpoints do controlador de usuário usando seu editor ou este comando.
E insira este código.
// routes/user.js
const express = require('express');
const router = express.Router();
const userController = require('../controllers/user');
router.get('/session', userController.session);
module.exports = router;Por fim, edite seu app.js arquivo e adicione a nova rota conforme mostrado aqui.
// app.js
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var app = express();
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
+ app.use('/api/user', require('./routes/user'));
app.use('/api/server', require('./routes/server'));
module.exports = app;Reinicie o aplicativo com npm start; em seguida, você poderá acessar a nova rota em localhost:3000/api/user/session.

Conectar-se à Conversation API da Vonage
Nesta seção, o que se segue são as etapas habituais, caso você já tenha lido algum dos meus tutoriais sobre o lado do cliente. Caso contrário, estes são comandos simples para criar nossa conversa no Vonage, à qual os usuários poderão participar.
Configure com nossa CLI
Para se conectar à Conversation API como usuário, primeiro é preciso criar um aplicativo, uma conversa e um usuário.
Criar um aplicativo
Crie uma aplicação com recursos de RTC (comunicação em tempo real). A URL de eventos recebe um registro em tempo real dos eventos que ocorrem no serviço, como usuários entrando ou saindo e enviando mensagens. Por enquanto, trata-se de uma URL de exemplo, mas você poderá capturar e reagir a eventos nas próximas partes da nossa série.
Criar uma conversa
Em segundo lugar, crie uma conversa, que funcione como uma sala de bate-papo. Ou seja, um espaço para mensagens e eventos.
Crie sua conta
Agora, crie uma conta para você mesmo.
Observação: Nesta demonstração, não haverá bate-papo entre dois usuários. Outros guias mostram como criar conversas entre vários usuários. Este guia se concentra em definir o estilo da interface de usuário das mensagens de maneira simples, mas atraente.
Adicionar o usuário a uma conversa
Em seguida, adicione seu novo usuário à conversa. Um usuário pode ser membro de uma Application, mas ainda assim precisa participar da conversa.
Gerar um token de usuário
Por fim, gere um token para o seu novo usuário. Esse token representa o usuário ao acessar o aplicativo. Esse token de acesso identifica o usuário; portanto, qualquer pessoa que o utilizar será considerada o usuário correto.
Na prática, você configurará o aplicativo com esse token. Em ambiente de produção, esses tokens devem ser protegidos, mantidos em sigilo e expostos com muito cuidado ao aplicativo cliente, se for o caso.
O token só pode ser usado por 24 horas. Depois disso, você precisará executar novamente este nexmo jwt:generate comando para conceder acesso ao seu usuário cliente novamente.
Armazene as credenciais no ambiente
Agora, edite .env e adicione as credenciais que você acabou de gerar.
Criar um serviço para a sessão do usuário
Crie um User.js serviço para acessar o endpoint da sessão do usuário a partir do aplicativo cliente.
Crie o arquivo usando este comando ou seu editor.
E adicione este código para criar um fetchSession método no novo User serviço.
// src/services/User.js
import Api from '@/services/Api'
export default {
fetchSession () {
return Api().get('user/session')
}
} Conecte o cliente à Conversation API
Para conectar o cliente à Conversation API, é necessário instalar a versão mais recente do nexmo-client.
Crie um novo Vonage.vue componente usando seu editor ou o comando abaixo, que terá a função de se conectar à Conversation API usando a nexmo-client biblioteca.
Semelhante ao App.vue componente, o Vonage.vue componente solicita informações da sessão do usuário ao servidor, utilizando o Loading.vue e Error.vue da mesma forma.
<template>
<div>
<HelloWorld v-if="!!app && !!conversation" msg="Welcome to Your Vue.js App"/>
<template v-else>
<Loading v-if="!error" message="Logging you in..." />
<Error v-else :error="error" />
</template>
</div>
</template>
<script>
import HelloWorld from '@/components/HelloWorld.vue'
import Loading from '@/components/Loading.vue'
import Error from '@/components/Error.vue'
import UserService from '@/services/User'
import Client from 'nexmo-client'
export default {
name: 'Vonage',
props: {
server: Object
},
components: {
ChatWindow,
Error,
Loading
},
data () {
return {
app: null,
conversation: null,
error: null
}
},
mounted () {
this.fetchSession()
},
methods: {
_errorHandler (err) {
this.error = { title: 'Chat Service Error', message: err.reason }
},
fetchSession () {
UserService.fetchSession()
.then((response) => {
const { token } = response.data
new Client()
.createSession(token)
.then(app => {
this.app = app
return app.getConversation(this.$props.server.defaultConversationId)
})
.then((conversation) => {
this.conversation = conversation
})
.catch(this._errorHandler)
})
.catch(this._errorHandler)
}
}
}
</script>
Agora, substitua o uso do HelloWorld.vue pelo novo Vonage.vue componente dentro App.vue fazendo essas alterações.
<template>
<div id="app">
- <HelloWorld v-if="!!server.status && server.status === 'ok'" msg="Welcome to Your Vue.js App"/>
+ <Vonage v-if="!!server.status && server.status === 'ok'" :server="server" />
<template v-else>
<Loading v-if="!error" message="Connecting..." />
<Error v-else :error="error" />
</template>
</div>
</template>
<script>
- import HelloWorld from './components/HelloWorld.vue'
+ import Vonage from '@/components/Vonage.vue'
import Error from '@/components/Error.vue'
import Loading from '@/components/Loading.vue'
import ServerService from '@/services/Server'
export default {
name: 'App',
components: {
- HelloWorld,
+ Vonage,
Error,
Loading
},
data () {
return {
server: {},
error: null
}
},
mounted () {
this.getServerStatus()
},
methods: {
getServerStatus () {
ServerService.fetchStatus()
.then((response) => {
this.server = response.data
})
.catch((err) => {
this.error = { title: 'Couldn\'t connect to Server', message: 'There may be a problem with your connection. Please check and try again.', reason: err.reason }
})
}
}
}
</script>
Agora, após a tela de carregamento “Conectando...”, você verá uma tela de carregamento “Fazendo seu login...” antes que o HelloWorld.vue componente.

Observação: Você só verá o “Hello World” se seu aplicativo tiver se conectado com sucesso ao servidor, recebido o status “OK”, solicitado a sessão do usuário e, em seguida, usado o token do usuário para se conectar à Conversation API por meio da
nexmo-clientbiblioteca.
Criar os componentes do chat
Agora que você está conectado à Conversation API, pode começar a criar sua interface de mensagens. Primeiro, comece pela estrutura básica do seu aplicativo: a janela de bate-papo.
Janela de bate-papo
Para isso, crie os componentes ChatWindow.vue, ChatWindowHeader.vue, ChatWindowEvents.vuee ChatWindowFooter.vue usando o comando ou seu editor.
Edição ChatWindow.vue, insira o seguinte código.
<template>
<div class="flex flex-col min-h-screen max-h-screen bg-white overflow-hidden">
<ChatWindowHeader :channelName="'#' + conversation.display_name"/>
<ChatWindowEvents :conversation="conversation" :user="user" :members="members" />
<ChatWindowFooter :conversation="conversation" />
</div>
</template>
<script>
import ChatWindowHeader from '@/components/ChatWindowHeader.vue'
import ChatWindowEvents from '@/components/ChatWindowEvents.vue'
import ChatWindowFooter from '@/components/ChatWindowFooter.vue'
export default {
name: 'ChatWindow',
props: {
app: Object,
conversation: Object
},
components: {
ChatWindowHeader,
ChatWindowEvents,
ChatWindowFooter
},
data () {
return {
user: {},
members: new Map(),
}
},
mounted () {
this.user = this.$props.app.me
this.fetchMembers()
},
methods: {
fetchMembers () {
this.members = this.$props.conversation.members
}
}
}
</script>
O ChatWindow.vue componente é responsável por estruturar o layout do chat. Cabeçalho na parte superior, mensagens no meio e rodapé na parte inferior. Ele passa o nome do canal, precedido por um hash, como o channelName prop para o cabeçalho. Ele também repassa a conversa, o usuário e os participantes para o componente de eventos. Em seguida, repassa a conversa para o rodapé.
Em seguida, edite ChatWindowHeader.vue e insira este código.
<template>
<div class="border-b flex px-6 py-2 items-center">
<div class="flex flex-col">
<h4 class="text-grey-darkest mb-1 font-extrabold">{{ channelName }}</h4>
</div>
</div>
</template>
<script>
export default {
name: 'ChatWindowHeader',
props: {
channelName: String,
members: Number
}
}
</script>O ChatWindowHeader.vue componente, por enquanto, apenas exibe o nome do canal.
Agora, edite ChatWindowEvents.vue e insira este código.
<template>
<div class="py-4 flex-auto overflow-y-auto" ref="chatWindow">
<template v-if="!!events.length">
<div class="px-6 hover:bg-gray-100" v-for="event in events" v-bind:key="'event' + event.id">
<div v-if="event.type === 'text'">
<strong>{{ members.get(event.from).display_name }}</strong> on <strong>{{ event.timestamp.split("T")[0] }}</strong> at <strong>{{ event.timestamp.split("T")[1].split(".")[0] }}</strong> says {{ event.body.text }}
</div>
<div v-else-if="event.type === 'member:joined'">
<strong>{{ event.body.user.display_name }}</strong> has joined <strong>#{{ event.conversation.display_name }}</strong>.
</div>
</div>
</template>
<Loading v-else message="Loading messages..." />
<Error v-else :error="error" />
</div>
</template>
<script>
import Loading from '@/components/Loading.vue'
import Error from '@/components/Error.vue'
export default {
name: 'ChatWindowEvents',
components: {
Loading,
Error
},
props: {
user: Object,
conversation: Object,
members: Map,
},
data () {
return {
events: [],
error: null
}
},
mounted () {
this.getEventHistory()
this.registerListeners()
},
methods: {
registerListeners () {
const { conversation } = this.$props
conversation.on('text', (user, event) => {
this.events.push(event)
})
conversation.on("member:joined", (user, event) => {
this.events.push(event)
})
},
getEventHistory () {
this.$props.conversation
.getEvents({ page_size: 40, order: 'desc' })
.then(eventsPage => {
eventsPage.items.forEach(event => {
this.events.unshift(event)
})
})
.catch(err => {
this.error = { title: 'Chat Service Error', message: err.message }
})
},
},
}
</script>
O ChatWindowEvents.vue componente é responsável por listar todos os eventos da conversa. Ele faz isso de cima para baixo, com os eventos mais antigos aparecendo na parte superior da janela. Role a tela para baixo para ver as mensagens mais recentes. Ele carrega um total de 40 mensagens. Mais adiante nesta série, você verá como carregar mensagens mais antigas.
Por fim, edite ChatWindowFooter.vue e insira este código.
<template>
<div class="px-4">
<textarea
v-bind:class="{
'disabled:opacity-75': isSending,
'bg-gray-300': isSending,
'border-gray-400': isSending,
'border-gray-400': !isSending
}"
v-bind:disabled="isSending"
v-bind:value="inputMessage"
v-on:input="inputMessage = $event.target.value"
v-on:keydown.enter.exact.prevent
v-on:keyup.enter.exact="sendMessage"
v-on:keyup="typingEvents"
type="text"
:placeholder="'Message ' + conversation.display_name"
class="w-full rounded border text-sm border-gray-700 overflow-hidden py-2 px-4 resize-none"
rows="1"
ref="inputBox"
>
</textarea>
<div class="grid grid-cols-10 h-6 text-xs">
</div>
</div>
</template>
<script>
export default {
name: 'ChatWindowFooter',
props: {
conversation: Object,
},
data () {
return {
inputMessage: '',
isSending: false
}
},
methods: {
typingEvents () {
this.resizeInput()
},
resizeInput () {
const inputRows = this.inputMessage.split(/\r?\n/).length
this.$refs.inputBox.rows = inputRows
},
sendMessage () {
if (this.inputMessage.replace(/\s/g,'').length > 0) {
this.isSending = true
this.$props.conversation
.sendText(this.inputMessage.trim())
.then(() => {
this.isSending = false
this.$nextTick(() => {
this.$refs.inputBox.focus()
this.inputMessage = ''
this.resizeInput()
});
})
.catch(err => {
console.error(err) // eslint-disable-line no-console
})
}
}
}
}
</script>
<style scoped>
textarea:focus{
outline: none;
}
</style>
Depois de criar seus componentes, edite Vonage.vue e substitua HelloWorld.vue pelo seu novo ChatWindow.vue componente.
<template>
<div>
- <HelloWorld v-if="!!app && !!conversation" msg="Welcome to Your Vue.js App" />
+ <ChatWindow v-if="!!app && !!conversation" :app="app" :conversation="conversation" />
<template v-else>
<Loading v-if="!error" message="Logging you in..." />
<Error v-else :error="error" />
</template>
</div>
</template>
<script>
- import HelloWorld from '@/components/HelloWorld.vue'
+ import ChatWindow from '@/components/ChatWindow.vue'
import Loading from '@/components/Loading.vue'
import Error from '@/components/Error.vue'
import UserService from '@/services/User'
import VonageClient from 'nexmo-client'
export default {
name: 'Vonage',
props: {
server: Object
},
components: {
- HelloWorld,
+ ChatWindow,
Error,
Loading
},
data () {
return {
app: null,
conversation: null,
error: null
}
},
mounted () {
this.fetchSession()
},
methods: {
...
}
}
</script>
Tem bastante coisa para copiar e colar aqui. Depois de executar, veja como fica.

Observe a margem, que ficou como resquício do aplicativo de demonstração! Por fim, remova esse estilo editando src/App.vue desta forma.
<template>
<div id="app">
<Vonage v-if="!!server.status && server.status === 'ok'" :server="server" />
<template v-else>
<Loading v-if="!error" message="Connecting..." />
<Error v-else :error="error" />
</template>
</div>
</template>
<script>
...
</script>
-
- <style>
- #app {
- font-family: Avenir, Helvetica, Arial, sans-serif;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
- text-align: center;
- color: #2c3e50;
- margin-top: 60px;
- }
- </style>
Já que está nisso, apague HelloWorld.vue. Finalmente.

O chat já está funcionando!
Parte 1, concluída! Você criou um cliente de bate-papo que está começando a se parecer com o Slack. Aqui está uma lista do que você fez até agora:
Criei um aplicativo em Express.js para usar como API
Criei um aplicativo em Vue.js para usar como cliente
Criei pontos de extremidade da API no Express.js
Endpoints da API utilizados no Vue.js
Adicionada a recarga dinâmica de arquivos do Express.js
Adicionado simultaneamente ao Express.js e ao Vue.js com um único comando
Solicitações de API encaminhadas pelo Vue.js para o Express.js
Estilizando o Vue.js com o Tailwind CSS
Ícones animados com o FontAwesome
Criei um componente de carregamento em tela cheia
Conectado à Conversation API da Vonage
Criei uma interface de usuário para mensagens
Se você estiver interessado no aplicativo de demonstração completo, confira o repositório no GitHub do meu clone do Slack em Vue.js até o momento.
Fique ligado na parte 2, onde abordaremos os seguintes aspectos essenciais da experiência do usuário.
Histórico de rolagem infinita
Posições fixas ao percorrer o histórico
Ping para a parte inferior ao enviar mensagens
Notificações de mensagens não lidas
Botão “Marcar como lido”
Número de membros do canal
Exclusão de mensagens
Notificação de eventos de digitação do usuário (várias pessoas estão digitando)
Mensagens com várias linhas
Markdown no estilo do Slack
Ao final da Parte 2, você terá algo mais parecido com isto!

Leitura complementar
Aqui estão mais alguns artigos que podem ser úteis na sua jornada para criar um aplicativo de bate-papo na web.
Como adicionar funcionalidade de Voice a um aplicativo de bate-papo já existente
Crie uma interface de usuário simples para mensagens com o Bootstrap
E não se esqueça: se tiver alguma dúvida, sugestão ou ideia que gostaria de compartilhar com a comunidade, sinta-se à vontade para participar do nosso espaço de trabalho da Comunidade no Slack 👇
Compartilhar:
Educador de tecnologia com um jeito amigável, pai de família, defensor da diversidade, provavelmente discuto um pouco demais. Ex-engenheiro de backend. Fale comigo sobre JavaScript (front-end ou back-end), o incrível Vue.js, DevOps, DevSecOps e tudo relacionado ao JamStack. Escritor no DEV.to