https://d226lax1qjow5r.cloudfront.net/blog/blogposts/build-a-basic-video-call-platform-with-netlify-functions/Blog_Netlify_Video-Call_1200x600.png

Construir una Plataforma Básica de Video Llamadas con Funciones Netlify

Publicado el May 5, 2021

Tiempo de lectura: 7 minutos

Si bien puedes comenzar a usar la Video API de Vonage con muy poco código, necesitarás un servidor para manejar la generación de sesiones y tokens. Si tienes un servidor para alojar una aplicación Express.js básica, tenemos una encantadora entrada de blog sobre cómo crear un videochat básico, pero no todos lo tienen.

Las funciones sin servidor se alojan en la web y sólo se ejecutan cuando se necesitan. Atrás quedaron los días en los que era necesario gestionar un servidor entero para una pequeña aplicación, y Netlify tiene una maravillosa barrera baja para construir y alojar funciones sin servidor.

Para realizar un seguimiento de las sesiones que existen en su aplicación, también necesitará una base de datos alojada. FaunaDB tiene una barrera igualmente baja para empezar y es lo que se utilizará en este tutorial. Empecemos...

El código completo está disponible en GitHub en https://github.com/nexmo-community/netlify-functions-video-conf

Requisitos previos

Cree un nuevo directorio y navegue hasta él utilizando su terminal, luego cree un archivo package.json escribiendo npm init -y. Una vez hecho esto, instale las dependencias del proyecto ejecutando npm install encoding faunadb netlify-lambda opentok.

Crear un proyecto de Video API de Vonage

Abre tu panel de Video API de Vonage y crea un nuevo proyecto de API. Puedes llamarlo como quieras y dejar el códec como VP8.

Cree un archivo en el directorio de su proyecto llamado .env y rellénalo con tu clave y secreto de la API de la siguiente manera:

VONAGE_KEY=YOUR_KEY
VONAGE_SECRET=YOUR_SECRET

Configurar una función Netlify localmente

El paquete netlify-lambda le permite ejecutar Funciones Netlify en su propia máquina. Para empezar, añada dos scripts a package.json:

"netlify:serve": "netlify-lambda serve functions/src",
"netlify:build": "netlify-lambda build functions/src"

Cree un functions directorio y una src dentro de éste. En la src cree un archivo hello.js archivo. Utilizarás esta función básica para comprobar que todo está configurado correctamente.

Cree un netlify.toml archivo y pon la siguiente configuración en él:

[build]
  functions = "./functions/build"

Esta configuración le dice a Netlify que sus funciones construidas serán accesibles en el directorio ./functions/build que aún no existe. Ejecute npm run netlify:serve en su terminal, y aparecerá un directorio build directorio aparecerá. Los archivos en este directorio se generan y actualizan automáticamente, así que mantenga sus cambios en srco podrían ser anulados.

Ponga lo siguiente en su hello.js archivo:

const headers = {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Headers': 'Content-Type'
}
 exports.handler = async (event, context) => {
  try {
    return { headers, statusCode: 200, body: 'ok' }
  } catch(e) {
    console.error('Error', e)
    return { headers, statusCode: 500, body: 'Error: ' + e }
  }
}

Abra un nuevo terminal y pruebe su endpoint ejecutando curl http://localhost:9000/hello. Debería obtener una respuesta de ok.

Crear una base de datos de fauna

Cada sesión de Vonage Video tiene un ID de sesión, que debes poder almacenar y recuperar posteriormente. En esta aplicación, también tendrán un nombre que los usuarios podrán usar para referirse a las sesiones.

Inicie sesión en su Consola Fauna DB y cree una nueva base de datos. Una vez en esta base de datos, cree una nueva colección llamada sessionsque almacenará el ID de sesión y los nombres.

Crear un índice de base de datos

Los índices se utilizan para definir las búsquedas habituales que tendrán lugar en nuestra base de datos. Haga clic en índices en la barra lateral y cree un nuevo índice con la siguiente configuración:

  • Fuente Colección: sesiones

  • Nombre índice: sessions_by_name

  • Términos: data.name

  • Único: marcado

Este índice existe ahora para buscar sesiones por su nombre amigable, que almacenará en una propiedad llamada name.

Obtener una clave de acceso a FaunaDB

Necesitará una clave de acceso para escribir y leer datos en esta base de datos. Haga clic en seguridad en la barra lateral y crea una nueva clave con un rol de administrador. Copie el secreto de la clave y añada la siguiente línea al final de su archivo .env archivo:

FAUNA_SECRET=YOUR_SECRET

Crear y guardar una Video Sesión

Copie y pegue todo el contenido de su hello.js en un nuevo archivo session.js en el mismo directorio. En la parte superior del archivo, añada lo siguiente:

import dotenv from 'dotenv'
dotenv.config()
 import faunadb, { query as q } from 'faunadb'
const client = new faunadb.Client({ secret: process.env.FAUNA_SECRET })
 const OpenTok = require("opentok");
const OT = new OpenTok(process.env.VONAGE_KEY, process.env.VONAGE_SECRET);

Esto carga tu archivo .env en la aplicación, importa e inicializa los clientes FaunaDB y OpenTok (ahora Vonage Video API).

En la parte inferior de su archivo, añada una función createSession función:

const createSession = (name) => {
  return new Promise((resolve, reject) => {
    OT.createSession(async (error, session) => {
      try {
        if(error) { throw error }
         // Send document to FaunaDB
        const document = await client.query(
          q.Create(
            q.Collection('sessions'), 
            { data: { name, id: session.sessionId }}
          )
        )
        
        resolve(document)
      } catch(e) {
        reject(e)
      }
    })
  })
}

Esta función toma un nombre de sesión amigable, crea un nuevo ID de sesión a través del cliente de la Video API de Vonage y lo almacena en nuestra base de datos Fauna.

Por último, actualice el contenido del bloque exports.handler por lo siguiente:

if (event.httpMethod == 'OPTIONS') {
  return { 
    headers: { ...headers, 'Allow': 'POST' }, 
    statusCode: 204 
  }
}
 const { name } = JSON.parse(event.body)
const document = await createSession(name)
 return { 
  headers, 
  statusCode: 200, 
  body: JSON.stringify(document) 
}

Cuando se lanza una petición desde una aplicación, ésta puede enviar una petición HTTP OPTIONS para preguntar qué métodos están permitidos. Si este es el caso, devuelve una cabecera adicional Allow adicional. Al devolver, se omite el resto del código.

Una vez enviada nuestra petición principal, se obtiene el valor del nombre enviado por el cliente, y se crea una nueva sesión. Como el cuerpo en la respuesta necesita ser una cadena, el documento devuelto es stringificado y devuelto al usuario.

¡Pruébalo! Reinicia tu aplicación netlify-lambda, abre tu terminal y ejecuta este comando:

curl --header "Content-Type: application/json" --request POST --data '{"name": "kevins-call"}' http://localhost:9000/session

La respuesta debería ser un documento completo de FaunaDB. En la consola de FaunaDB debería ver el documento almacenado.

Showing a new entry in the FaunaDB Console

Intente ejecutar el comando curl de nuevo, y observe un error ya que no puede haber dos elementos con el mismo nombre (basado en nuestra restricción de unicidad colocada en la colección a través del índice).

Buscar una sesión existente

Cuando un usuario introduce el nombre de una sesión existente, la aplicación debería devolver el documento en lugar de intentar crear una nueva. Si se crean nuevas sesiones con cada conexión, cada usuario acabará en una sesión unipersonal, lo que no es muy social.

Sustituye esta línea:

const document = await createSession(name)

Con este fragmento:

const doesSessionExist = await client.query(
  q.Exists(q.Match(q.Index('sessions_by_name'), name))
)
 let document
if(doesSessionExist) {
  document = await client.query(
    q.Get(q.Match(q.Index('sessions_by_name'), name))
  )
} else {
  document = await createSession(name)
}

El primer paso es comprobar si existe un documento con el nombre actual. Si se utiliza el método q.Exists() de FaunaDB con el índice creado anteriormente da como resultado un documento true o false almacenado en doesSessionExist.

Si la sesión existe, obtenga el documento. Si no existe, crea uno. Al final de este fragmento, la variable document contendrá un documento.

Generar un token

Para que los usuarios se autentiquen al conectarse a una sesión de video de Vonage, se les debe proporcionar un token desde nuestro punto final.

Debajo del código que acabas de escribir, y sobre la declaración de retorno, genera un token usando el cliente de la Video API de Vonage:

const token = OT.generateToken(document.data.id, {
  role: 'publisher',
  data: `roomname=${document.data.name}`
})

El primer parámetro es un identificador de sesión, y el segundo contiene opciones para este token. Hay tres roles admitidos: suscriptor (sólo puede suscribirse a los flujos de otras personas), editor (puede publicar y suscribirse) y moderador (editor, y puede obligar a otros clientes a desconectarse).

Todos serán editores de esta aplicación. Sin embargo, más adelante podrá añadir una forma de distinguir los roles de los usuarios. La segunda opción contiene el nombre de la sala, que se almacena en la base de datos con el ID de sesión asociado.

Devolver datos al cliente

El punto final debe devolver tres elementos de información al frontend: un ID de sesión, un token y nuestra clave de Video API de Vonage. Reemplaza tu declaración de retorno para incluir todos estos elementos:

return { 
  headers, 
  statusCode: 200, 
  body: JSON.stringify({
    token: token,
    sessionId: document.data.id,
    apiKey: process.env.VONAGE_KEY
  }) 
}

Vuelva a probar el endpoint con un nombre de sesión nuevo y otro existente. La respuesta debería incluir las tres propiedades. Esto significa que el endpoint está funcionando correctamente, y puede pasar a construir un frontend básico para utilizarlo.

Construir Frontend

Cree un archivo index.html en el directorio principal de tu proyecto. Como el cliente frontend será mínimo, todo tu código vivirá en este archivo. Escribe el HTML necesario:

<html>
  <head></head>
  <body>
    <form id="registration">
      <input type="text" name="name" placeholder="Enter room name" required />
      <button>Enter</button>
    </form>

     <div id="call">
      <div id="subscriber" class="subscriber"></div>
      <div id="publisher" class="publisher"></div>
    </div>

     <script src="https://static.opentok.com/v2/js/opentok.min.js"></script>

     <script>
      // Other logic will go here
    </script>
  </body>
</html>

Cuando se pulse el botón, envíe una petición al endpoint y compruebe que los datos se devuelven correctamente. Dentro de la etiqueta <script> vacía:

const form = document.getElementById("registration")
 form.addEventListener("submit", event => {
  event.preventDefault()
  form.style.display = "none"
   fetch("http://localhost:9000/session", { 
    method: "POST",
    body: JSON.stringify({ name: form.elements.name.value })
  }).then(res => {
    return res.json();
  }).then(res => {
    console.log(res);
  }).catch(handleCallback);
})
 function handleCallback(error) {
  if (error) {
    console.log("error: " + error.message);
  } else {
    console.log("callback success");
  }
}

La consola de tu navegador debería tener este aspecto:

Showing the correct data being returned on the frontend

Ahora que el frontend tiene todos los datos que necesita, puede inicializar la sesión en su navegador, crear un nuevo editor y publicarlo en la sesión. Sustituya console.log(res); por lo siguiente:

const { apiKey, sessionId, token } = res;
 const session = OT.initSession(apiKey, sessionId);
 const publisher = OT.initPublisher(
  "publisher",
  { insertMode: "append", width: "100%", height: "100%" },
  handleCallback
);
 session.connect(token, error => {
  if (error) {
    handleCallback(error);
  } else {
    session.publish(publisher, handleCallback);
  }
});
 session.on("streamCreated", event => {
  session.subscribe(
    event.stream,
    "subscriber",
    { insertMode: "append", width: "100%", height: "100%" },
    handleCallback
  );
});

En "publisher" y "subscriber" se refieren a los valores HTML id de los elementos que contendrán las secuencias de vídeo.

Abre index.html en tu navegador, escribe el nombre de una sala y pulsa el botón. Después de dar permiso a la página para acceder a tu micrófono y cámara, deberías ver aparecer un stream de Video (editor).

Anfitrión en Netlify

Cree un archivo .gitignore en el directorio de su proyecto e incluya en él el siguiente código para evitar que estos archivos y directorios pasen a un espacio público.

node_modules
functions/build
.env

Crear un nuevo repositorio GitHub y sigue los pasos para 'crear un nuevo repositorio en la línea de comandos'.

Una vez completado, vaya a su cuenta de Netlify y haga clic en Nuevo sitio desde Git. Elija su repositorio GitHub y realice el comando Build npm run netlify:build.

Vaya a la sección Variables de entorno de la configuración de compilación y despliegue de este proyecto. Introduzca las tres variables de entorno de su archivo .env local.

El paso final es hacer referencia a su función Netlify alojada en su cliente frontend cuando se aloja en línea, y su URL localhost cuando está fuera de línea.

Localice esta línea en index.html:

fetch("http://localhost:9000/session", {

Sustitúyalo por lo siguiente:

let url;
  if(location.hostname == 'localhost' || location.hostname == "127.0.0.1") {
    url = "http://localhost:9000/session"
  }  else {
    url = "YOUR_NETLIFY_URL/.netlify/functions/session"
  }
   fetch(url, {

Crea y envía un nuevo commit a GitHub, y tu función Netlify debería volver a desplegarse. Una vez hecho esto, visite su URL de Netlify y ¡déle una vuelta!

¿Y ahora qué?

Esta aplicación funciona muy bien, pero no tiene un aspecto muy agradable. Puedes considerar tomar algunas pistas de estilo de nuestro tutorial Express.js tutorial. Otras entradas de la serie cubren la adición de chat de texto, compartir tu pantalla o usar los otros roles provistos como parte de la Video API de Vonage. Recuerda: para cada punto final que existe en esos tutoriales, debes crear una nueva función sin servidor en el directorio functions/src directorio.

El código completo de este proyecto también está en GitHub.

Puedes obtener más información sobre la API de Video de Vonage a través de nuestra documentacióny si necesitas ayuda adicional, no dudes en comunicarte con nuestro equipo a través de nuestra cuenta de cuenta de Twitter para desarrolladores de Vonage o en Slack de la comunidad de Vonage.

Compartir:

https://a.storyblok.com/f/270183/400x400/c822f15b89/kevinlewis.png
Kevin LewisAntiguos alumnos de Vonage

Antiguo defensor de los desarrolladores de Vonage, donde su función era apoyar a la comunidad tecnológica local de Londres. Es un experimentado organizador de eventos, jugador de mesa y padre de un precioso perrito llamado Moo. También es el principal organizador de You Got This, una red de eventos sobre las habilidades básicas necesarias para una vida laboral feliz y saludable.