
Compartir:
Soy desarrollador de JavaScript y educador de desarrolladores en Vonage. A lo largo de los años me han entusiasmado las plantillas, Node.js, las aplicaciones web progresivas y las estrategias offline-first, pero lo que siempre me ha encantado es una API útil y bien documentada. Mi objetivo es hacer que tu experiencia usando nuestras APIs sea la mejor posible.
Creación de una Aplicación de Chat de Voz con Vue.js y Express
Tiempo de lectura: 11 minutos
Añadir un servicio a una aplicación web compleja puede ser complicado. Esto es aún más cierto cuando el servicio tiene un componente de interfaz de usuario. Con la API de Nexmo puedes crear un chat de voz en el navegador que se convierta en la base de una gran variedad de aplicaciones de comunicación. Pero incluso organizar las piezas de esa interfaz de usuario básica puede resultar difícil. Componentes como los utilizados en Vue.js facilitan esta tarea proporcionando un patrón para las plantillas, el estilo y la secuencia de comandos de interfaz de usuario que puede requerir un componente de interfaz de usuario individual. Un servidor Express que se conecta a las herramientas de Nexmo le proporciona una solución ligera de pila completa que puede adaptarse a cualquier arquitectura del mundo real con la que termine, gracias a la separación de preocupaciones.
Hay muchas maneras de estructurar una aplicación con Vue. Para este tutorial voy a remezclar un proyecto proyecto Glitch que proporciona relativamente poco andamiaje, pero puedes elegir un proyecto de inicio proporcionado por la Vue CLI o una biblioteca de terceros que ofrezca características específicas como Server-Side Rendering. Dado que tu código dependerá tanto de Vue como de Express, el único requisito es que tu configuración incluya ambos.
Vonage API Account
To complete this tutorial, you will need a Vonage API account. If you don’t have one already, you can sign up today and start building with free credit. Once you have an account, you can find your API Key and API Secret at the top of the Vonage API Dashboard.
Añadir Nexmo a su proyecto
Para crear una conversación desde el navegador, deberá instalar el cliente Nexmo cliente y el servidor servidor Nexmo. Como el usuario enviará datos desde el cliente también necesitarás body-parser en Express. En el directorio raíz de tu proyecto, instala esos paquetes con npm, o desde la consola en Glitch usa pnpm:
pnpm install nexmo@beta nexmo-client body-parser -sPara utilizar las herramientas de Nexmo, también tendrá que proporcionar sus credenciales de API en el archivo .env archivo. El archivo debería tener este aspecto:
Dependiendo de su entorno, puede que también necesite instalar el paquete
dotenvdesde npm. Para importar sus variables de entorno desde.envbasta con añadir una línea al principio del archivoserver.jsarchivo:require('dotenv').config();
Puede encontrar su clave y secreto API en la página Primeros pasos de su panel de Nexmo. En el menú Voice, vaya a Crear una aplicación y haga clic en "Generar par de claves pública/privada" para descargar el archivo private.key archivo. A continuación, rellene los campos y haga clic en "Crear aplicación" para obtener su ID de aplicación.
Asegúrese de copiar el archivo private.key archivo en su proyecto y actualizar la ruta en .env a la ubicación en la que lo has guardado. Es posible pegar el contenido directamente en .envpero el formato puede causar problemas. Generalmente es más robusto guardarlo en un archivo separado.
Un servidor para llamadas API
El papel de Express.js en tu proyecto será proporcionar un servidor simple que llame a la API de Nexmo para realizar algunas tareas administrativas. Esto requerirá alguna configuración del propio servidor, una instancia de Nexmo y definiciones de ruta para los puntos finales del servidor.
En server.jscree el servidor y ordénele que analice JSON en los cuerpos de las peticiones y sirva páginas estáticas desde el directorio public directorio. A continuación, crea un objeto Nexmo, pasándole los valores de .env. Por último, crea marcadores de posición para tus rutas e indica al servidor que empiece a escuchar eventos:
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
app.use(bodyParser.json());
app.use(express.static('public'));
// create a Nexmo client
const Nexmo = require('nexmo');
const nexmo = new Nexmo({
apiKey: process.env.API_KEY,
apiSecret: process.env.API_SECRET,
applicationId: process.env.APP_ID,
privateKey: __dirname + process.env.PRIVATE_KEY
}, {debug: true});
// the client calls this endpoint to request a JWT, passing it a username
app.post('/getJWT', function(req, res) {});
// the client calls this endpoint to get a list of all users in the Nexmo application
app.get('/getUsers', function(req, res) {});
// the client calls this endpoint to create a new user in the Nexmo application,
// passing it a username and optional display name
app.post('/createUser', function(req, res) {});
app.listen(process.env.PORT); Rutas del servidor
Las tres rutas definidas en el servidor permitirán a la aplicación listar y crear usuarios que puedan unirse a una conversación, y autenticarlos. En una aplicación para uso en el mundo real, probablemente conectarías esto a tu propia gestión de usuarios en lugar de a una interfaz web.
La ruta /getJWT proporciona un token que el cliente puede utilizar para autenticar al usuario actual. La producción del JWT se realiza con una única función, pero requiere varios datos. Tienes que proporcionar de nuevo el ID de tu aplicación, así como subque es el nombre de usuario que quieres autenticar. También establecerás la caducidad y las rutas permitidas para el token. Puedes enviar el token recién creado al cliente:
// the client calls this endpoint to request a JWT, passing it a username
app.post('/getJWT', function(req, res) {
const jwt = nexmo.generateJwt({
application_id: process.env.APP_ID,
sub: req.body.name,
exp: Math.round(new Date().getTime()/1000)+3600,
acl: {
"paths": {
"/v1/users/**":{},
"/v1/conversations/**":{},
"/v1/sessions/**":{},
"/v1/devices/**":{},
"/v1/image/**":{},
"/v3/media/**":{},
"/v1/push/**":{},
"/v1/knocking/**":{}
}
}
});
res.send({jwt: jwt});
});La ruta /getUsers también realiza una única llamada y devuelve su resultado, pero vamos a ordenarlo un poco para utilizarlo en una interfaz web. Antes de devolver la lista de todos los usuarios de esta aplicación, puede filtrar los usuarios del sistema cuyos ID empiecen por el prefijo NAM-. En una aplicación del mundo real en la que los ID de usuario estuvieran asignados a cuentas dentro de su aplicación más grande, probablemente no se molestaría en realizar este paso y podría devolver la lista tal cual:
// the client calls this endpoint to get a list of all users in the Nexmo application
app.get('/getUsers', function(req, res) {
const users = nexmo.users.get({}, (err, response) => {
if (err) {
res.sendStatus(500);
} else {
let realUsers = response.filter(user => user.name.substring(0,4) !== 'NAM-');
res.send({users: realUsers});
}
});
});
La última ruta, /createUsertomará alguna entrada del usuario y añadirá un usuario a la aplicación. Dado que la función create toma tanto un nombre de usuario como un nombre para mostrar como entrada, existe la opción en este código de establecer un nombre para mostrar por separado, sin embargo no lo incluiremos en la interfaz de usuario. Por lo tanto, el endpoint sólo busca un name del cliente, y una vez que crea un usuario con él, devuelve su ID:
// the client calls this endpoint to create a new user in the Nexmo application,
// passing it a username and optional display name
app.post('/createUser', function(req, res) {
nexmo.users.create({
name: req.body.name,
display_name: req.body.display_name || req.body.name
},(err, response) => {
if (err) {
res.sendStatus(500);
} else {
res.send({id: response.id});
}
});
});
El componente Vue App
Todos los componentes Vue para este proyecto vivirán en el directorio src directorio. El proyecto que estoy remezclando ya incluye un archivo main.js que crea una instancia de Vue, así como un componente contenedor en el directorio app.vue. main.js no hace nada más que renderizar el componente App:
var Vue = require('vue');
var App = require('./app.vue');
var vm = new Vue({
el: '#app',
render: createElement => {
return createElement(App)
}
});
Esto funciona junto con public/index.htmldonde un div con el ID app es el único elemento de la página:
<!DOCTYPE html>
<html>
<head>
<title>VueJS + Express Template</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
</head>
<body>
<div id="app"></div>
<script src="build.js"></script>
</body>
</html>
En caso de que queramos añadir más cosas más adelante, dejaremos el componente App y cargaremos un componente Nexmo dentro de él, en lugar de sustituir App por Nexmo. Si ya tiene un archivo app.vue puede sustituir su contenido por una plantilla sencilla y un script que cargue el componente Nexmo componente:
template>
<div class="app">
<Nexmo/>
</div>
</template>
<script>
import Nexmo from './nexmo.vue';
export default {
name: 'App',
components: {
Nexmo
}
}
</script>
El componente Nexmo
El componente Nexmo es donde las cosas empiezan a ponerse interesantes. Puedes crearlo en nexmo.vue y añadir una plantilla al principio del archivo que renderizará User y Conversation componentes. Para User, un gancho de actualización llamará a una función getJWT en el script que añadirás a continuación. También puedes añadir una referencia al componente para acceder a él más tarde:
<template>
<div class="nexmo">
<User @hook:updated="userUpdated" ref="user" />
<Conversation/>
</div>
</template>
Debajo de la plantilla añadirás una etiqueta script que contenga la lógica del componente. Después de importar los dos subcomponentes y el Nexmo Client SDK, exportarás un componente Vue llamado Nexmo. Contendrá algunas propiedades data que formarán parte de su estado, así como sus subcomponentes y algunos métodos que definirás a continuación:
<script>
import User from './user.vue';
import Conversation from './conversation.vue';
import nexmoClient from 'nexmo-client';
export default {
name: 'Nexmo',
data: () => ({
app: null,
token: null,
invites: [],
loggedIn: false
}),
components: {
User,
Conversation
},
methods: {}
};
</script>La propiedad methods definirá dos funciones, una para obtener un JWT del servidor y otra para gestionar el inicio de sesión. La función getJWT es llamada por el gancho de actualización de su componente User por lo que primero debe comprobar si ese componente contiene una propiedad username propiedad. Si lo hace, puede llamar al endpoint del lado del servidor /getJWT del servidor utilizando fetch. Pasa el valor username y, si todo funciona correctamente, obtiene un JWT a cambio. Almacena el JWT como una propiedad de la instancia y llama a la función login función .
La función login es donde instanciarás un cliente Nexmo real. Usted iniciará sesión en su usuario con su JWT, a continuación, establecer una bandera si tiene éxito y guardar una referencia a la aplicación Nexmo. Una vez que tengas la aplicación puedes obtener las conversaciones a las que el usuario actual está invitado:
methods: {
getJWT: function() {
var username = this.$refs.user.username;
if (!username) {
return;
}
var vm = this;
fetch('/getJWT', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: username
})
})
.then(results => results.json())
.then(data => {
vm.token = data.jwt;
vm.login();
});
},
login: function() {
let nexmo = new nexmoClient();
nexmo.login(this.token).then(app => {
this.loggedIn = true;
this.app = app;
app.getConversations().then(convos => {
this.invites = Array.from(convos.entries());
});
});
}
}
El componente usuario
El User en src/user.vue es el primer lugar donde tendrás una plantilla que hace algo más que renderizar un subcomponente. Esta es otra parte que podrías omitir en una aplicación de producción, pero en este caso la interfaz de inicio de sesión de usuario es parte de tu aplicación Nexmo. La plantilla mostrará el usuario conectado si existe. Si no, muestra un formulario con dos rutas. El primero permite seleccionar un usuario existente de un menú desplegable. Si se realiza una selección, el usuario es actualizado inmediatamente por la función setExistingUser función.
El usuario también puede introducir un nuevo nombre de usuario y pulsar el botón "Enviar". Esto llama a la función createUser función:
<template>
<div v-if="userId" class="userinfo userconnected">
Connected as <span class="username">{{username}}</span>
</div>
<div v-else class="userinfo">
<label>User name:
<select v-on:change="setExistingUser">
<option value=""></option>
<option v-for="item in currentUsers" v-bind:value="item.id">
{{item.name}}
</option>
</select>
</label>
<input type="text" v-on:change="setUsername" />
<button v-on:click="createUser">Create user</button>
</div>
</template>
Componente de usuario Script
El sitio script para el componente tiene algunas cosas diferentes, pero ninguna lógica compleja. La mayor parte de lo que hace es cargar y guardar propiedades. Las cosas complejas ocurren dentro del propio framework de Vue, en funcionalidades como el gancho de actualización en tu componente Nexmo componente.
No hay nada que importar, por lo que puede exportar inmediatamente un User componente. Lo único data que necesitará son propiedades para el ID y el nombre del usuario, y una lista de los usuarios actuales de la aplicación.
El componente tiene cuatro métodos para apoyar el formulario en la plantilla. La función getUsers función llama /getUsers al servidor para obtener la lista de usuarios. Recordarás que has manejado cualquier lógica de filtrado necesaria del lado del servidor, así que si no hay ningún error puedes simplemente establecer esa propiedad en el componente.
setExistingUser es llamado por un evento onchange en el desplegable de usuarios. Guarda el nombre de usuario y el ID de usuario de la selección realizada. Para los nuevos usuarios, setUsername también es llamado por un evento onchangeesta vez en el campo de texto. Actualizar el nuevo nombre de usuario en el componente cada vez que cambia ahorra tener que obtener una referencia al elemento del campo de texto. Si un usuario pulsa el botón "Crear usuario", createUser es llamado, enviando el nombre de usuario en estado al servidor y guardando el ID de usuario que se devuelve.
Después de methodseste componente también llama a beforeMount para asegurarse de que la lista de usuarios se carga cuando se inicializa por primera vez:
<template>
...
</template>
<script>
export default {
name: 'User',
data: () => ({
userId: undefined,
username: null,
currentUsers: []
}),
methods: {
getUsers: function() {
var vm = this;
fetch('/getUsers', {
method: 'GET'
}).then(results => results.json())
.then(data => {
vm.currentUsers = data.users;
});
},
setExistingUser: function(evt) {
this.username = evt.target[evt.target.selectedIndex].text;
this.userId = evt.target.value;
},
setUsername: function(evt) {
this.username = evt.target.value;
},
createUser: function() {
var vm = this;
fetch('/createUser', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: vm.username
})
}).then(results => results.json())
.then(data => {
vm.userId = data.id;
});
}
},
beforeMount() {
this.getUsers()
}
};
</script> El componente de conversación
Hasta ahora, el código que has escrito ha sido para crear tu aplicación Nexmo, establecer un usuario e iniciar sesión. Debería ser lo suficientemente independiente como para que puedas hacer cambios para satisfacer las necesidades de tu proyecto individual, sin dejar de exponer las piezas esenciales de información a tu aplicación Vue más grande. Ahora puedes utilizar esas piezas para unirte a una conversación. La conversación es el punto de partida para una variedad de tipos de comunicación que puede que desee hacer uso de la API de Nexmo en el cliente.
El componente sería más manejable con algunos subcomponentes divididos (por ejemplo, los controles de audio). Sin embargo, para hacer las cosas más obvias por ahora puedes poner todo el código junto en conversation.vue.
La plantilla
En el nivel superior de la plantilla hay una condicional para determinar si existe una conversación en curso. En caso afirmativo, mostrará un elemento de audio para proporcionar el sonido, y dos botones para activar y desactivar el audio. Al pulsarlos, llamarán a enableAudio y disableAudiorespectivamente.
Si no hay ninguna conversación en curso, el usuario tendrá que unirse o iniciar una. Si el usuario ha sido invitado a una conversación o ha iniciado una previamente, aparecerá en el invites en el componente Nexmo padre. Los valores de invites rellenarán un desplegable de conversaciones, y al seleccionar una se llamará a la función joinConversation función Tanto si el usuario tiene invitesverá un botón para iniciar una nueva conversación:
<template>
<div v-if="current_conv" class="conversation">
<audio ref="audio">
<source/>
</audio>
<button v-on:click="enableAudio" v-bind:disabled="audioOn">
Enable audio
</button>
<button v-on:click="disableAudio" v-bind:disabled="!audioOn">
Disable audio
</button>
</div>
<div v-else class="conversation">
<label v-if="$parent.invites.length">Choose an active conversation:
<select v-on:change="joinConversation">
<option value="0">-</option>
<option v-for="invite in $parent.invites" v-bind:value="invite[0]">
{{invite[1].name}}
</option>
</select> or
</label>
<button v-on:click="createConversation" :disabled="!$parent.loggedIn">
Start conversation
</button>
</div>
</template>
El guión
De nuevo en este componente, lo único que ocurre en el nivel superior de la etiqueta script es exportar un componente Conversation componente. Sus únicos data son la conversación actual y una bandera que indica si el audio está activado.
Las páginas methods que contiene el componente son bastante sencillas. createConversation llama a la función newConversation de la aplicación almacenada en el componente Nexmo y almacena la conversación creada. joinConversation hace lo mismo, salvo que llama a la función de la aplicación getConversation de la aplicación, pasándole el ID de la conversación seleccionada en el desplegable.
En enableAudioprimero tienes que activar los medios de comunicación en la conversación actual. Esto te dará un flujo que puedes configurar como srcObject o src del elemento audio en su plantilla. Una vez que se hayan cargado los metadatos, puedes reproducir el flujo y establecer el indicador audioOn del componente a true. La función disableAudio cuando se pulsa el botón "Desactivar audio" es más sencilla. Sólo tiene que desactivar los medios de comunicación en current_conv y luego volver a poner el indicador audioOn a false:
<template>
...
</template>
<script>
export default {
name: 'Conversation',
data: () => ({
current_conv: undefined,
audioOn: false
}),
methods: {
createConversation: function() {
var vm = this;
this.$parent.app.newConversation().then(conv => {
conv.join();
vm.current_conv = conv;
});
},
joinConversation: function(evt) {
var vm = this;
this.$parent.app.getConversation(evt.target.value).then(conv => {
conv.join();
vm.current_conv = conv;
});
},
enableAudio: function() {
var vm = this;
this.current_conv.media.enable().then(stream => {
// Older browsers may not have srcObject
if ('srcObject' in vm.$refs.audio) {
vm.$refs.audio.srcObject = stream;
} else {
// Avoid using this in new browsers, as it is going away.
vm.$refs.audio.src = window.URL.createObjectURL(stream);
}
vm.$refs.audio.onloadedmetadata = () => {
vm.$refs.audio.play();
vm.audioOn = true;
}
});
},
disableAudio: function() {
var vm = this;
this.current_conv.media.disable().then(() => {
vm.audioOn = false;
});
}
}
};
</script> El resto
Hay algunas cosas que no hemos cubierto, que con suerte son suministradas por el Vue boilerplate que has elegido o no necesariamente tienen que afectar a la lógica de tu aplicación. Por ejemplo, en mi propio código confío en Browserify y Vueifyasí como un poco de CSS que formaba parte del proyecto que he remezclado. El paso de compilación que hace que el lado Vue de la aplicación funcione se define en "scripts" en package.json:
"compile": "browserify -t vueify -e src/main.js -o public/build.js" Próximos pasos
El código que has escrito es en gran medida un punto de partida para tu trabajo en el mundo real. Como se ha mencionado, probablemente querrás sustituir el sistema de gestión de usuarios de prueba por algo que vincule a los miembros de la conversación con tus propios usuarios autenticados. Con tu conversación creada podrás enviar y recibir mensajes, recibir llamadas y establecer audioconferencias.
Obtenga más información sobre el Client SDK de Nexmo para saber qué puede hacer a continuación:
Compartir:
Soy desarrollador de JavaScript y educador de desarrolladores en Vonage. A lo largo de los años me han entusiasmado las plantillas, Node.js, las aplicaciones web progresivas y las estrategias offline-first, pero lo que siempre me ha encantado es una API útil y bien documentada. Mi objetivo es hacer que tu experiencia usando nuestras APIs sea la mejor posible.
