Cómo crear una aplicación de chat grupal con Vanilla JS, Twilio y Node.js

En este tutorial, aprenderá cómo crear una aplicación de chat grupal. Creará una aplicación de backend utilizando Node.js que realizará chats grupales, autorizará a los participantes del chat y mantendrá sesiones para ellos. El front-end se construirá utilizando Vanilla JS, HTML y CSS. Permitirá a los usuarios iniciar sesión, crear nuevos grupos, invitar amigos e intercambiar mensajes. Al final, debería tener una aplicación funcional donde varios participantes puedan enviar mensajes en un grupo.
El chat se está convirtiendo en un medio de comunicación cada vez más popular tanto en contextos empresariales como sociales. Las empresas utilizan el chat para la comunicación dentro de la empresa con clientes y empleados, como con Slack , Microsoft Teams , Chanty , HubSpot Live Chat , Help Scout , etc. La mayoría de las redes sociales y aplicaciones de comunicación también ofrecen chat como una opción predeterminada, como en Instagram, Facebook, Reddit y Twitter. Otras aplicaciones como Discord, Whatsapp y Telegram se basan principalmente en chats, siendo los chats grupales una de sus principales funcionalidades.
Si bien existen numerosos productos para facilitar el chat, es posible que necesite una solución personalizada para su sitio que se ajuste a sus necesidades de comunicación particulares. Por ejemplo, muchos de estos productos son aplicaciones independientes y es posible que no puedan integrarse en su propio sitio. Hacer que sus usuarios abandonen su sitio web para chatear puede no ser la mejor opción, ya que puede afectar la experiencia del usuario y la conversión. Por otro lado, crear una aplicación de chat desde cero puede ser una tarea desalentadora y, a veces, abrumadora. Sin embargo, al utilizar API como Twilio Conversations, puedes simplificar el proceso de creación. Estas API de comunicación manejan la creación de grupos, agregar participantes, enviar mensajes, notificaciones, entre otras funciones importantes de chat. Las aplicaciones de backend que utilizan estas API solo tienen que manejar la autenticación y realizar llamadas a estas API. Luego, las aplicaciones de front-end muestran conversaciones, grupos y mensajes desde el backend.
En este tutorial, aprenderá cómo crear una aplicación de chat grupal utilizando la API de conversaciones de Twilio . La interfaz de esta aplicación se creará utilizando HTML, CSS y Vanilla JavaScript. Permitirá a los usuarios crear chats grupales, enviar invitaciones, iniciar sesión y enviar y recibir mensajes. El backend será una aplicación Node.js. Proporcionará tokens de autenticación para los invitados al chat y gestionará la creación del chat.
Requisitos previos
Antes de poder comenzar este tutorial, debe tener lo siguiente:
- Node.js instalado. Lo usará principalmente para la aplicación de backend y para instalar dependencias en la aplicación de frontend.
Puede obtenerlo utilizando un instalador prediseñado disponible en la página de descargas de Node.js. - Una cuenta de Twilio .
Puede crear uno en el sitio web de Twilio en este enlace . http-server
para servir la aplicación front-end.
Puedes instalarlo ejecutandonpm i -g http-server
. También puedes ejecutarlonpx http-server
para ejecuciones únicas.- MongoDB para almacenamiento de sesiones en la aplicación backend.
Su página de instalación tiene una guía detallada sobre cómo hacerlo funcionar.
La aplicación de fondo
Para enviar mensajes de chat usando la API de Twilio, necesita una conversación . Los mensajes de chat se envían y reciben dentro de una conversación. Las personas que envían los mensajes se denominan participantes . Un participante solo puede enviar un mensaje dentro de una conversación si está agregado a ella. Tanto las conversaciones como los participantes se crean utilizando la API de Twilio. La aplicación backend realizará esta función.
Un participante necesita un token de acceso para enviar un mensaje y recibir sus conversaciones suscritas. La parte inicial de este proyecto utilizará este token de acceso. La aplicación backend crea el token y lo envía al frontend. Allí se utilizará para cargar conversaciones y mensajes.
Iniciador del proyecto
Llamarás a la aplicación backend twilio-chat-server
. Un iniciador de proyecto estructurado está disponible en Github . Para clonar el proyecto y obtener el iniciador, ejecute:
git clone https://github.com/zaracooper/twilio-chat-server.gitcd twilio-chat-servergit checkout starter
La aplicación backend toma esta estructura:
.├── app.js├── config/├── controllers/├── package.json├── routes/└── utils/
Para ejecutar la aplicación, usará el node index.js
comando.
Dependencias
La aplicación backend necesita 8 dependencias. Puedes instalarlos ejecutando:
npm i
Aquí hay una lista de cada una de las dependencias:
connect-mongo
se conecta a MongoDB, que utilizará como almacén de sesiones;cors
maneja CORS ;dotenv
carga variables de entorno desde el.env
archivo que creará en un paso posterior;express
es el marco web que usarás para el backend;express-session
proporciona middleware para manejar datos de sesión;http-errors
ayuda a crear errores en el servidor;morgan
maneja el registro;twilio
crea el cliente Twilio, genera tokens, crea conversaciones y agrega participantes.
Configuración
La config
carpeta es responsable de cargar la configuración desde las variables de entorno. La configuración se agrupa en tres categorías: configuración para CORS, Twilio y la base de datos de sesión MongoDB. Cuando el entorno sea development
, cargará config
desde el .env
archivo usando dotenv
.
Comience creando el .env
archivo en la terminal. Este archivo ya está agregado al .gitignore
archivo para evitar que los valores confidenciales que contiene se registren en el repositorio.
touch .env
Así es como .env
debería verse:
# Session DB ConfigSESSION_DB_HOST=XXXXSESSION_DB_USER=XXXXSESSION_DB_PASS=XXXXSESSION_DB_PORT=XXXXSESSION_DB_NAME=XXXXSESSION_DB_SECRET=XXXX# Twilio ConfigTWILIO_ACCOUNT_SID=XXXXTWILIO_AUTH_TOKEN=XXXXTWILIO_API_KEY=XXXXTWILIO_API_SECRET=XXXX# CORS Client ConfigCORS_CLIENT_DOMAIN=XXXX
Puede aprender cómo crear un usuario para su base de datos de sesión en esta entrada del manual de MongoDB . Una vez que cree una base de datos de sesión y un usuario que pueda escribir en ella, puede completar los valores SESSION_DB_USER
, SESSION_DB_PASS
y SESSION_DB_NAME
. Si está ejecutando una instancia local de MongoDB, SESSION_DB_HOST
sería localhost
, y SESSION_DB_PORT
normalmente es 27017
. express-session SESSION_DB_SECRET
utiliza el para firmar la cookie de ID de sesión y puede ser cualquier cadena secreta que establezca.
En el siguiente paso, obtendrá las credenciales de Twilio Console . Las credenciales deben asignarse a las variables con el TWILIO_
prefijo. Durante el desarrollo local, el cliente front-end se ejecutará en http://localhost:3000 . Entonces, puede usar este valor para la CORS_CLIENT_DOMAIN
variable de entorno.
Agregue el siguiente código para config/index.js
cargar variables de entorno.
import dotenv from 'dotenv';if (process.env.NODE_ENV == 'development') { dotenv.config();}const corsClient = { domain: process.env.CORS_CLIENT_DOMAIN};const sessionDB = { host: process.env.SESSION_DB_HOST, user: process.env.SESSION_DB_USER, pass: process.env.SESSION_DB_PASS, port: process.env.SESSION_DB_PORT, name: process.env.SESSION_DB_NAME, secret: process.env.SESSION_DB_SECRET};const twilioConfig = { accountSid: process.env.TWILIO_ACCOUNT_SID, authToken: process.env.TWILIO_AUTH_TOKEN, apiKey: process.env.TWILIO_API_KEY, apiSecret: process.env.TWILIO_API_SECRET};const port = process.env.PORT || '8000';export { corsClient, port, sessionDB, twilioConfig };
Las variables de entorno se agrupan en categorías según lo que hacen. Cada una de las categorías de configuración tiene su propia variable de objeto y todas se exportan para usarlas en otras partes de la aplicación.
Obtener credenciales de Twilio desde la consola
Para construir este proyecto, necesitará cuatro credenciales de Twilio diferentes: un SID de cuenta , un token de autenticación , una clave API y un secreto API . En la consola, en la página Configuración general , desplácese hacia abajo hasta la sección Credenciales API . Aquí es donde encontrará el SID de su cuenta y el token de autenticación .
Para obtener una clave API y un secreto , vaya a la página Claves API . Puedes verlo en la captura de pantalla a continuación. Haga clic en el +botón para ir a la página Nueva clave API .
API Key
Botón Crear en la página de lista de claves API. ( Vista previa grande )
En esta página, agregue un nombre de clave y déjelo KEY TYPE
como Standard
y luego haga clic en Create API Key. Copie la clave API y el secreto. Agregará todas estas credenciales en un .env
archivo como verá en los pasos siguientes.
Utilidades
La aplicación backend necesita dos funciones de utilidad. Uno creará un token y el otro empaquetará los controladores asíncronos y manejará los errores por ellos.
En utils/token.js
, agregue el siguiente código para crear una función llamada createToken
que generará tokens de acceso de Twilio:
import { twilioConfig } from '../config/index.js';import twilio from 'twilio';function createToken(username, serviceSid) { const AccessToken = twilio.jwt.AccessToken; const ChatGrant = AccessToken.ChatGrant; const token = new AccessToken( twilioConfig.accountSid, twilioConfig.apiKey, twilioConfig.apiSecret, { identity: username } ); const chatGrant = new ChatGrant({ serviceSid: serviceSid, }); token.addGrant(chatGrant); return token.toJwt();}
En esta función, genera tokens de acceso utilizando el SID de su cuenta , la clave API y el secreto API . Opcionalmente, puede proporcionar una identidad única que podría ser un nombre de usuario, correo electrónico, etc. Después de crear un token, debe agregarle una concesión de chat . La concesión de chat puede tomar un ID de servicio de conversación, entre otros valores opcionales. Por último, convertirá el token en un JWT y lo devolverá.
El utils/controller.js
archivo contiene una asyncWrapper
función que envuelve las funciones del controlador asíncrono y detecta cualquier error que arrojen. Pegue el siguiente código en este archivo:
function asyncWrapper(controller) { return (req, res, next) = Promise.resolve(controller(req, res, next)).catch(next);}export { asyncWrapper, createToken };
Controladores
La aplicación backend tiene cuatro controladores: dos para autenticación y dos para manejar conversaciones. El primer controlador de autenticación crea un token y el segundo lo elimina. Uno de los controladores de conversaciones crea nuevas conversaciones , mientras que el otro agrega participantes a las conversaciones existentes.
Controladores de conversación
En el controllers/conversations.js
archivo, agregue estas importaciones y código para el StartConversation
controlador:
import { twilioConfig } from '../config/index.js';import { createToken } from '../utils/token.js';import twilio from 'twilio';async function StartConversation(req, res, next) { const client = twilio(twilioConfig.accountSid, twilioConfig.authToken); const { conversationTitle, username } = req.body; try { if (conversationTitle username) { const conversation = await client.conversations.conversations .create({ friendlyName: conversationTitle }); req.session.token = createToken(username, conversation.chatServiceSid); req.session.username = username; const participant = await client.conversations.conversations(conversation.sid) .participants.create({ identity: username }) res.send({ conversation, participant }); } else { next({ message: 'Missing conversation title or username' }); } } catch (error) { next({ error, message: 'There was a problem creating your conversation' }); }}
El StartConversation
controlador primero crea un Twilio client
usando tu twilioConfig.accountSid
y twilioConfig.authToken
del que obtienes config/index.js
.
A continuación, crea una conversación. Para ello necesita un título de conversación, que obtiene del cuerpo de la solicitud. Es necesario agregar un usuario a una conversación antes de que pueda participar en ella. Un participante no puede enviar un mensaje sin un token de acceso. Entonces, genera un token de acceso usando el nombre de usuario proporcionado en el cuerpo de la solicitud y el archivo conversation.chatServiceSid
. Luego se agrega a la conversación el usuario identificado por el nombre de usuario. El controlador completa respondiendo con la conversación y el participante recién creados.
A continuación, debe crear el AddParticipant
controlador. Para hacer esto, agregue el siguiente código debajo de lo que acaba de agregar en el controllers/conversations.js
archivo anterior:
async function AddParticipant(req, res, next) { const client = twilio(twilioConfig.accountSid, twilioConfig.authToken); const { username } = req.body; const conversationSid = req.params.id; try { const conversation = await client.conversations.conversations .get(conversationSid).fetch(); if (username conversationSid) { req.session.token = createToken(username, conversation.chatServiceSid); req.session.username = username; const participant = await client.conversations.conversations(conversationSid) .participants.create({ identity: username }) res.send({ conversation, participant }); } else { next({ message: 'Missing username or conversation Sid' }); } } catch (error) { next({ error, message: 'There was a problem adding a participant' }); }}export { AddParticipant, StartConversation };
El AddParticipant
controlador agrega nuevos participantes a conversaciones ya existentes. Utilizando lo conversationSid
proporcionado como parámetro de ruta, recupera la conversación. Luego crea un token para el usuario y lo agrega a la conversación usando su nombre de usuario del cuerpo de la solicitud. Por último, envía la conversación y el participante como respuesta.
Controladores de autenticación
Los dos controladores controllers/auth.js
se llaman GetToken
y DeleteToken
. Agréguelos al archivo copiando y pegando este código:
function GetToken(req, res, next) { if (req.session.token) { res.send({ token: req.session.token, username: req.session.username }); } else { next({ status: 404, message: 'Token not set' }); }}function DeleteToken(req, res, _next) { delete req.session.token; delete req.session.username; res.send({ message: 'Session destroyed' });}export { DeleteToken, GetToken };
El GetToken
controlador recupera el token y el nombre de usuario de la sesión, si existen, y los devuelve como respuesta. DeleteToken
elimina la sesión.
Rutas
La routes
carpeta tiene tres archivos: index.js
, conversations.js
y auth.js
.
Agregue estas rutas de autenticación al routes/auth.js
archivo agregando este código:
import { Router } from 'express';import { DeleteToken, GetToken } from '../controllers/auth.js';var router = Router();router.get('/', GetToken);router.delete('/', DeleteToken);export default router;
La GET
ruta en la /
ruta devuelve un token mientras que la DELETE
ruta elimina un token.
A continuación, copie y pegue el siguiente código en el routes/conversations.js
archivo:
import { Router } from 'express';import { AddParticipant, StartConversation } from '../controllers/conversations.js';import { asyncWrapper } from '../utils/controller.js';var router = Router();router.post('/', asyncWrapper(StartConversation));router.post('/:id/participants', asyncWrapper(AddParticipant));export default router;
En este archivo se crea el enrutador de conversaciones. Se agrega al enrutador una POST
ruta para crear conversaciones con la ruta /
y otra POST
ruta para agregar participantes con la ruta ./:id/participants
Por último, agregue el siguiente código a su nuevo routes/index.js
archivo.
import { Router } from 'express';import authRouter from './auth.js';import conversationRouter from './conversations.js';var router = Router();router.use('/auth/token', authRouter);router.use('/api/conversations', conversationRouter);export default router;
Al agregar los enrutadores conversation
y auth
aquí, los hará disponibles en /api/conversations
y /auth/token
para el enrutador principal respectivamente. Luego se exporta el enrutador.
La aplicación de fondo
Ahora es el momento de juntar las piezas del backend. Abra el index.js
archivo en su editor de texto y pegue el siguiente código:
import cors from 'cors';import createError from 'http-errors';import express, { json, urlencoded } from 'express';import logger from 'morgan';import session from 'express-session';import store from 'connect-mongo';import { corsClient, port, sessionDB } from './config/index.js';import router from './routes/index.js';var app = express();app.use(logger('dev'));app.use(json());app.use(urlencoded({ extended: false }));app.use(cors({ origin: corsClient.domain, credentials: true, methods: ['GET', 'POST', 'DELETE'], maxAge: 3600 * 1000, allowedHeaders: ['Content-Type', 'Range'], exposedHeaders: ['Accept-Ranges', 'Content-Encoding', 'Content-Length', 'Content-Range']}));app.options('*', cors());app.use(session({ store: store.create({ mongoUrl: `mongodb://${sessionDB.user}:${sessionDB.pass}@${sessionDB.host}:${sessionDB.port}/${sessionDB.name}`, mongoOptions: { useUnifiedTopology: true }, collectionName: 'sessions' }), secret: sessionDB.secret, cookie: { maxAge: 3600 * 1000, sameSite: 'strict' }, name: 'twilio.sid', resave: false, saveUninitialized: true}));app.use('/', router);app.use(function (_req, _res, next) { next(createError(404, 'Route does not exist.'));});app.use(function (err, _req, res, _next) { res.status(err.status || 500).send(err);});app.listen(port);
Este archivo comienza creando la aplicación express. Luego configura el análisis de carga útil codificada en URL y JSON y agrega el middleware de registro. A continuación, configura CORS y el manejo de la sesión. Como se mencionó anteriormente, MongoDB se utiliza como almacén de sesiones.
Después de configurar todo, agrega el enrutador creado en el paso anterior antes de configurar el manejo de errores. Por último, hace que la aplicación escuche y acepte conexiones en el puerto especificado en el .env
archivo. Si no ha configurado el puerto, la aplicación escuchará en el puerto 8000
.
Una vez que haya terminado de crear la aplicación backend, asegúrese de que MongoDB se esté ejecutando e inícielo ejecutando este comando en la terminal:
NODE_ENV=development npm start
Pasa la NODE_ENV=development
variable para que la configuración se cargue desde el .env
archivo local.
El front-end
La parte inicial de este proyecto cumple un par de funciones. Permite a los usuarios crear conversaciones, ver la lista de conversaciones de las que forman parte, invitar a otros a las conversaciones que crearon y enviar mensajes dentro de las conversaciones. Estos roles se logran mediante cuatro páginas:
- una página de conversaciones ,
- una página de chat ,
- una página de error ,
- una página de inicio de sesión .
Llamarás a la aplicación front-end twilio-chat-app
. Existe un iniciador andamiado para ello en Github . Para clonar el proyecto y obtener el iniciador, ejecute:
git clone https://github.com/zaracooper/twilio-vanilla-js-chat-app.gitcd twilio-vanilla-js-chat-appgit checkout starter
La aplicación toma esta estructura:
.├── index.html├── pages│ ├── chat.html│ ├── conversation.html│ ├── error.html│ └── login.html├── scripts│ ├── chat.js│ ├── conversation.js│ └── login.js└── styles ├── chat.css ├── main.css └── simple-page.css
El estilo y el marcado HTML ya se han agregado para cada una de las páginas del inicio. Esta sección solo cubrirá los scripts que debe agregar.
Dependencias
La aplicación tiene dos dependencias: axios
y @twilio/conversations
. Lo utilizará axios
para realizar solicitudes a la aplicación backend y @twilio/conversations
para enviar y recuperar mensajes y conversaciones en scripts. Puedes instalarlos en la terminal ejecutando:
npm i
La página de índice
Esta página sirve como página de inicio para la aplicación. Puede encontrar el marcado de esta página ( index.html
) aquí . Utiliza dos hojas de estilo CSS: styles/main.css
la que utilizan todas las páginas y styles/simple-page.css
la que utilizan las páginas más pequeñas y menos complicadas.
Puede encontrar el contenido de estas hojas de estilo vinculado en el párrafo anterior. Aquí hay una captura de pantalla de cómo se verá esta página:
La página de errores
Esta página se muestra cuando ocurre un error. El contenido de pages/error.html
se puede encontrar aquí. Si se produce un error, un usuario puede hacer clic en el botón para ir a la página de inicio. Allí podrán volver a intentar lo que estaban intentando.
La página de conversaciones
En esta página, un usuario proporciona el título de una conversación que se creará y su nombre de usuario en un formulario.
El contenido de pages/conversation.html
se puede encontrar aquí. Agregue el siguiente código al scripts/conversation.js
archivo:
window.twilioChat = window.twilioChat || {};function createConversation() { let convoForm = document.getElementById('convoForm'); let formData = new FormData(convoForm); let body = Object.fromEntries(formData.entries()) || {}; let submitBtn = document.getElementById('submitConvo'); submitBtn.innerText = "Creating..." submitBtn.disabled = true; submitBtn.style.cursor = 'wait'; axios.request({ url: '/api/conversations', baseURL: 'http://localhost:8000', method: 'post', withCredentials: true, data: body }) .then(() = { window.twilioChat.username = body.username; location.href = '/pages/chat.html'; }) .catch(() = { location.href = '/pages/error.html'; });}
Cuando un usuario hace clic en el Submitbotón, createConversation
se llama a la función. En él, el contenido del formulario se recopila y se utiliza en el cuerpo de una POST
solicitud realizada http://localhost:8000/api/conversations/
en el backend.
Utilizarás axios
para realizar la solicitud. Si la solicitud tiene éxito, se crea una conversación y se agrega al usuario. Luego, el usuario será redirigido a la página de chat donde podrá enviar mensajes en la conversación.
A continuación se muestra una captura de pantalla de la página de conversaciones:
La página de chat
En esta página, un usuario verá una lista de conversaciones de las que forma parte y les enviará mensajes. Puede encontrar el marcado pages/chat.html
aquí y el estilo styles/chat.css
aquí .
El scripts/chat.js
archivo comienza definiendo un espacio de nombres twilioDemo
.
window.twilioChat = window.twilioChat || {};
Agregue la initClient
función a continuación. Es responsable de inicializar el cliente Twilio y cargar conversaciones.
async function initClient() { try { const response = await axios.request({ url: '/auth/token', baseURL: 'http://localhost:8000', method: 'GETget', withCredentials: true }); window.twilioChat.username = response.data.username; window.twilioChat.client = await Twilio.Conversations.Client.create(response.data.token); let conversations = await window.twilioChat.client.getSubscribedConversations(); let conversationCont, conversationName; const sideNav = document.getElementById('side-nav'); sideNav.removeChild(document.getElementById('loading-msg')); for (let conv of conversations.items) { conversationCont = document.createElement('button'); conversationCont.classList.add('conversation'); conversationCont.id = conv.sid; conversationCont.value = conv.sid; conversationCont.onclick = async () = { await setConversation(conv.sid, conv.channelState.friendlyName); }; conversationName = document.createElement('h3'); conversationName.innerText = ` ${conv.channelState.friendlyName}`; conversationCont.appendChild(conversationName); sideNav.appendChild(conversationCont); } } catch { location.href = '/pages/error.html'; }};
Cuando se carga la página, initClient
recupera el token de acceso del usuario del backend y luego lo usa para inicializar el cliente. Una vez que se inicializa el cliente, se utiliza para recuperar todas las conversaciones a las que está suscrito el usuario. Después de eso, las conversaciones se cargan en el archivo side-nav
. En caso de que ocurra algún error, el usuario es enviado a la página de error.
La setConversion
función carga una sola conversación. Copie y pegue el siguiente código en el archivo para agregarlo:
async function setConversation(sid, name) { try { window.twilioChat.selectedConvSid = sid; document.getElementById('chat-title').innerText = '+ ' + name; document.getElementById('loading-chat').style.display = 'flex'; document.getElementById('messages').style.display = 'none'; let submitButton = document.getElementById('submitMessage') submitButton.disabled = true; let inviteButton = document.getElementById('invite-button') inviteButton.disabled = true; window.twilioChat.selectedConversation = await window.twilioChat.client.getConversationBySid(window.twilioChat.selectedConvSid); const messages = await window.twilioChat.selectedConversation.getMessages(); addMessagesToChatArea(messages.items, true); window.twilioChat.selectedConversation.on('messag
Deja un comentario