
Compartir:
Alex Lakatos es JavaScript Developer Advocate para Nexmo. En su tiempo libre es voluntario en Mozilla como Tech Speaker y Reps Mentor. Desarrollador de JavaScript en la web abierta, ha estado empujando sus límites todos los días. Cuando no está programando en Londres, le gusta viajar por el mundo, así que es probable que te lo encuentres en la sala de espera de un aeropuerto.
Cómo enviar y recibir mensajes SMS con Nuxt.js y la SMS API de Nexmo
Tiempo de lectura: 12 minutos
Vue.js es uno de los nuevos frameworks progresivos de JavaScript que está haciendo las rondas del mundo front-end. Es uno de los frameworks más accesible, versátil y de alto rendimiento por ahí, y aquí en Nexmo, hemos cambiado recientemente nuestro Portal de Desarrolladores Nexmo a utilizar Vue.js.
En 2018 hemos (em)potenciado el Campamento CEM utilizando aplicaciones Vue.js junto con los SDK para clientes de Nexmo.
Quería explorar el ecosistema Vue.js un poco más, y me he topado con Nuxt.js. Es un framework modular de Vue.js que facilita la configuración de una aplicación de producción. Con Vue.js normalmente se necesita un componente back-end para manejar el modo historia en las URLs, y Nuxt.js se encarga de eso. También añade una capa de abstracciones en tu URL proporcionando Middleware desde el primer momento. Middleware son métodos que se ejecutan antes de tu código de gestión de rutas, y funcionan tanto en tu código Vue.js como en el servidor.
Así que pensé que sería un excelente sustituto para manejar las llamadas a la API en el servidor, en lugar de tener que configurar una segunda aplicación back-end. En esta entrada de blog, voy a utilizar el middleware de servidor Nuxt.js para enviar y recibir mensajes SMS.
Para los SMS, voy a utilizar la Nexmo SMS APIque te permite enviar y recibir un gran volumen de SMS en cualquier parte del mundo. Una vez que obtenga su número de teléfono virtual, puede utilizar la API para gestionar los mensajes salientes ("envío") y los mensajes entrantes ("recepción").
He aquí un vistazo a lo que estamos construyendo:

El código de este tutorial se encuentra en GitHub.
Requisitos previos
Antes de empezar, asegúrate de que tienes:
Node.js instalado en su máquina
ngrok para que el código de nuestra máquina local sea accesible al mundo exterior
La versión beta del Nexmo CLI:
npm install -g nexmo-cli@beta
Generar una nueva aplicación Nuxt.js
Para facilitar los primeros pasos, el equipo de Nuxt.js ha creado una herramienta CLI llamada create-nuxt-appque genera un nuevo proyecto y te permite seleccionar todos los módulos que puedes tener en una aplicación Nuxt.js. He utilizado esa herramienta para generar un nuevo proyecto, llamado nexmo-nuxt-sms.
$ npx create-nuxt-app nexmo-nuxt-smsHe elegido npm como mi gestor de paquetes. He encontrado un buen Tailwind CSS de Tailwind con el que quería construir, así que he elegido Tailwind como mi framework de interfaz de usuario. Para un framework de servidor personalizado, he elegido usar none, la recomendación de Nuxt.js. Para los módulos, he elegido axios para las peticiones HTTP, y dotenv para poder usar un archivo .env para mis variables de compilación. Soy fan de ESlintasí que lo he elegido como mi herramienta de linting. No voy a escribir ninguna prueba para esta entrada del blog, así que he optado por no añadir un marco de pruebas. He elegido Universal como modo de renderizado porque me ofrece Server Side Rendering desde el principio. Como mi editor preferido para Vue.js es VS Code, he elegido jsconfig.json como herramienta de desarrollo extra para el último paso del proceso de andamiaje.

Una vez terminado el andamiaje, he cambiado de directorio a mi nuevo proyecto, y he ejecutado el proyecto usando npm run dev. Eso iniciará los procesos del cliente y del servidor y los hará disponibles en http://localhost:3000. También los recargará en caliente cada vez que haga un cambio, para que pueda verlo en vivo sin tener que reiniciar los procesos.
$ cd nexmo-nuxt-sms
$ npm run devEl comando generó toda una estructura de directorios, que es la piedra angular de Nuxt.js. En la carpeta raíz, hay nuxt.config.jsque es el archivo de configuración de Nuxt.js. Lo actualizaremos para añadir serverMiddleware. El middleware del servidor funciona especificando rutas y archivos JavaScript asociados que se ejecutan cuando se accede a esas rutas. Crearemos dos rutas, /api/send y /api/receivepara gestionar el envío y la recepción de mensajes SMS a través de ellas. En la parte inferior, añade una propiedad para serverMiddleware:
export default {
...
},
serverMiddleware: [
{ path: '/api/send', handler: '~/api/send-sms.js' },
{ path: '/api/receive', handler: '~/api/receive-sms.js' }
]
} Envío de un mensaje SMS
Le hemos dicho a Nuxt.js que utilice el método ~/api/send-sms.js cuando se haga una petición en /api/send pero aún no hemos creado el archivo. Seguiremos adelante y crearemos una carpeta api y un archivo send-sms.js dentro de ella.
$ mkdir api
$ touch send-sms.jsPara enviar mensajes SMS con la SMS API de Nexmo, utilizaremos el SDK de nexmo SDK de Node.js. Primero necesitamos instalarlo:
$ npm install nexmoVamos a usarlo dentro del archivo, y necesitamos requerirlo, y luego instanciarlo con tu clave y secreto de la API de Nexmo. Puedes encontrarlos en tu Panel Nexmo. Actualizar send-sms.js para que se vea así:
require('dotenv').config()
const Nexmo = require('nexmo')
const nexmo = new Nexmo({
apiKey: process.env.NEXMO_API_KEY,
apiSecret: process.env.NEXMO_API_SECRET
})
export default function (req, res) {
console.log(req.method, req.url)
}Estamos utilizando dotenv para tomar la clave y el secreto de la API del archivo .env en lugar de añadirlas directamente en el código. Así que tendremos que actualizar el archivo .env en la raíz del proyecto generado con los valores de NEXMO_API_KEY y NEXMO_API_SECRET.
NEXMO_API_KEY=aabbcc0
NEXMO_API_SECRET=s3cRet$tuffEl archivo exporta una función por defecto que tiene los objetos Node.js de petición y respuesta por defecto. Como están ahí, y no quería añadir la dependencia extra de expresslos usaremos para crear un servidor HTTP Node.js clásico. Actualicemos el export en el archivo send-sms.js para que quede así:
export default function (req, res, next) {
console.log(req.method, req.url)
if (req.method === 'GET') {
const url = new URL(req.url, `http://${req.headers.host}`)
nexmo.message.sendSms(
process.env.FROM_NUMBER,
url.searchParams.get('number'),
url.searchParams.get('text'),
(err, responseData) => {
let message
if (err) {
message = JSON.stringify(err)
} else if (responseData.messages[0].status === '0') {
message = 'Message sent successfully.'
} else {
message = `Message failed with error: ${responseData.messages[0]['error-text']}`
}
res
.writeHead(200, {
'Content-Length': Buffer.byteLength(message),
'Content-Type': 'text/plain'
})
.end(message)
}
)
} else {
res.statusCode = 200
res.end()
}
}
Estoy comprobando si la solicitud es una GET solicitud aquí y luego usando el botón "Enviar un SMS" para enviar un SMS. El método nexmo.message.sendSms recibe un método from, to y text para determinar el destinatario, el remitente y el contenido del mensaje SMS. También toma un método callback que se ejecutará una vez finalizada la llamada a la API. Tomo el parámetro from del archivo .env que será un número de teléfono Nexmo. El to y text provienen de los parámetros de consulta de la solicitud HTTP entrante.
Mi función callback es una función anónima, y primero compruebo si ha habido un error en la petición. Si hubo un error, transformo el objeto error a String y lo paso al mensaje de respuesta. Si no hubo error, voy a mirar el estado del mensaje en los datos de respuesta. Un estado de 0 significa que el SMS se ha enviado correctamente, así que actualizo el mensaje de respuesta. Si el estado no era 0significa que se ha producido un error al enviar el mensaje SMS desde la API Nexmo, a través de las redes de las operadoras de telecomunicaciones, a un teléfono. Actualizaré el mensaje con el texto de error apropiado.
Como se trata de un servidor Node.js, tengo que escribir explícitamente el encabezado de la solicitud con un campo 200 status, el Content-Length y Content-Type del mensaje, antes de que pueda enviar el mensaje en la petición.
También existe una opción alternativa para todas las solicitudes que no sean GET, que devuelve una respuesta vacía 200 OK vacía.
Comprar un número Nexmo
Te habrás dado cuenta de que he usado process.env.FROM_NUMBER como ID de remitente, lo que significa que Nuxt.js lo buscará en el archivo .env archivo. Antes de que podamos añadirlo allí, tendremos que comprar un número de teléfono habilitado para SMS en el Panel Nexmo.
También podemos comprar un número a través de la CLI Nexmo, y voy a hacer precisamente eso. En caso de que no haya utilizado la CLI Nexmo antes, es necesario configurarlo con su clave de API Nexmo y secreto antes de que podamos usarlo.
$ nexmo setup NEXMO_API_KEY NEXMO_API_SECRETUtilizaremos el comando number:search para buscar un número disponible antes de comprarlo. El comando acepta un código de país de dos letras como entrada (yo he usado GB para los números del Reino Unido), y podemos especificar algunos indicadores para reducir la lista de números de teléfono disponibles. Yo utilizo --sms para marcar los números habilitados para SMS, --size=5 para limitar el tamaño de la lista devuelta, y --verbose para devolver una tabla bien formateada con información adicional sobre los números de teléfono disponibles.
$ nexmo number:search GB --sms --size=5 --verboseLa respuesta que recibí fue más o menos la siguiente:
Item 1-5 of 7633
msisdn | country | cost | type | features
------------------------------------------------------
447451272708 | GB | 1.25 | mobile-lvn | VOICE,SMS
447451272710 | GB | 1.25 | mobile-lvn | VOICE,SMS
447451272713 | GB | 1.25 | mobile-lvn | VOICE,SMS
447451272714 | GB | 1.25 | mobile-lvn | VOICE,SMS
447451272719 | GB | 1.25 | mobile-lvn | VOICE,SMSHe elegido el primer número de la respuesta, así que vamos a comprar ese número en la plataforma Nexmo.
$ nexmo number:buy 447451272708 --confirmAhora que ya tienes ese número de teléfono, vamos a añadirlo al archivo .env archivo.
NEXMO_API_KEY=aabbcc0
NEXMO_API_SECRET=s3cRet$tuff
FROM_NUMBER=447451272708Podemos probar el endpoint que hemos creado, asegurarnos de que funciona. Debido a que es una GET request, no necesitamos una herramienta adicional como Postman, podemos usar la URL directamente en el navegador. Si cargas una URL con una consulta como http://localhost:3000/api/send?text=hello&number=YOUR_PHONE_NUMBERsustituyendo YOUR_PHONE_NUMBER por tu número de móvil, deberías recibir un SMS con el texto hello en tu teléfono.

Recibir un mensaje SMS
Cuando un número de teléfono Nexmo recibe un mensaje SMS, Nexmo pasará ese mensaje a un Webhook que hayas especificado en el Panel de Nexmo. Tendremos que crear el /api/receive endpoint, exponerlo públicamente a Nexmo a través de ngrok, y luego vincularlo en el Panel de Nexmo.
Ya hemos registrado el endpoint /api/receive con el middleware del servidor Nuxt.js, vamos a crear el archivo para manejarlo. Dentro del directorio api cree un archivo receive-sms.js archivo.
$ cd api
$ touch receive-sms.jsEl archivo funciona de forma similar al archivo send-sms.js que creamos anteriormente, tiene la misma sintaxis export default function recibiendo una petición Node.js y un objeto de respuesta. Vamos a completar el archivo receive-sms.js con un manejador de peticiones POST, que construye el cuerpo de la petición a partir de trozos, y luego lo registra en la consola.
export default function (req, res) {
console.log(req.method, req.url)
if (req.method === 'POST') {
const body = []
req.on('data', (chunk) => {
body.push(chunk)
})
req.on('end', () => {
const sms = JSON.parse(body)
console.log(sms)
})
}
res.statusCode = 200
res.end()
}
Compruebo si la petición entrante es una POST solicitud, y luego escuchar en los trozos de datos de solicitud, añadiéndolos a un body array. Cuando la solicitud termina, estoy analizando el body en JSON, y lo registro en la consola. Eso va a ser efectivamente los datos de SMS procedentes de Nexmo. Nexmo espera un 200 OK estado en la solicitud, por lo que estoy respondiendo con eso.
Nuestro punto final Webhook está en vivo en localhost ahora, pero tenemos que exponerlo a Internet, por lo que Nexmo puede acceder a él. Vamos a utilizar ngrok para hacer eso.
Ejecutar ngrok
Si usted no ha utilizado ngrok antes, hay un entrada de blog que explica cómo utilizarlo. Si estás familiarizado con ngrok, ejecútalo con http en el puerto 3000.
$ ngrok http 3000Después de ngrok se ejecuta, se le dará una URL de aspecto aleatorio, que vamos a utilizar como base para nuestro Webhook más adelante. La mía tiene este aspecto: http://3dea3250.ngrok.io.
Vincular el Webhook a Nexmo
Para configurar la URL webhook, vaya al pequeño icono de engranaje junto a sus Numbers en el Panel Nexmo y rellene el campo "Inbound Webhook URL" con YOUR_NGROK_URL/api/receive.
O podemos utilizar el Nexmo CLI vincular el número de teléfono Nexmo que compró anteriormente con la URL Webhook de ngrok:
nexmo link:sms 447451272708 http://YOUR_NGROK_URL.ngrok.io/api/receiveAhora puedes enviar un mensaje SMS desde tu teléfono a tu número de teléfono Nexmo, y deberías verlo registrado en el terminal donde se ejecuta tu aplicación Nuxt.js.

Creación de una interfaz de usuario Vue.js
Hemos creado la funcionalidad de servidor para enviar y recibir mensajes SMS, es hora de crear una interfaz de usuario para interactuar con esa funcionalidad desde el navegador.
En primer lugar, vamos a limpiar la interfaz de usuario Nuxt.js existente creado para nosotros. Sustituye el contenido del archivo /layouts/default.vue por:
<template>
<div>
<nuxt />
</div>
</template>
<style>
html {
background-color: #4299e1;
}
</style>
Estoy usando una plantilla de Terminal Mac de tailwindcomponents.comasí que vamos a reemplazar el contenido de la etiqueta <template> en el archivo /pages/index.vue con la nueva interfaz de usuario:
<template>
<div class="w-1/2 mx-auto py-20">
<div class="w-full shadow-2xl subpixel-antialiased rounded h-64 bg-black border-black mx-auto">
<div
id="headerTerminal"
class="flex items-center h-6 rounded-t bg-gray-100 border-b border-gray-500 text-center text-black"
>
<div
id="closebtn"
class="flex ml-2 items-center text-center border-red-900 bg-red-500 shadow-inner rounded-full w-3 h-3"
/>
<div
id="minbtn"
class="ml-2 border-yellow-900 bg-yellow-500 shadow-inner rounded-full w-3 h-3"
/>
<div
id="maxbtn"
class="ml-2 border-green-900 bg-green-500 shadow-inner rounded-full w-3 h-3"
/>
<div id="terminaltitle" class="mx-auto pr-16">
<p class="text-center text-sm">
<logo />Terminal
<logo />
</p>
</div>
</div>
<div id="console" class="pl-1 pt-1 h-auto text-green-500 font-mono text-xs bg-black">
<p class="pb-1">
Last login: {{ new Date().toUTCString() }} on ttys002
</p>
<p v-for="counter in counters" :key="counter.id" class="pb-1">
<span class="text-red-600">@lakatos88</span>
<span class="text-yellow-600 mx-1">></span>
<span class="text-blue-600">~/nexmo/nexmo-nuxt-sms</span>
<span class="text-red-600 mx-1">$</span>
<span v-if="!counter.message" class="blink" contenteditable="true" @click.once="stopBlinking" @keydown.enter.once="runCommand">_</span>
<span v-if="counter.message">{{ counter.message }}</span>
</p>
</div>
</div>
</div>
</template>He modificado la plantilla ligeramente para que coincida con los colores a la configuración de mi terminal y actualizar la información de usuario para que coincida con mi terminal también.
Los cambios que hice ocurren en el console div, así que vamos a echar un vistazo a eso. Estoy usando {{ new Date().toUTCString() }} para obtener la fecha actual y mostrarla en pantalla.
A continuación, utilizo la directiva Vue.js v-for para recorrer un array counters y mostrar un guión bajo parpadeante o un mensaje en la ventana de terminal, por cada entrada del array de contadores. El guión bajo parpadeante tiene una bandera contenteditable en él, lo que significa que puede editar el contenido de la misma en el navegador. Estoy usando la directiva @click para ejecutar una función JavaScript stopBlinking la primera vez que un usuario haga clic en él, y evitar que parpadee. La misma etiqueta HTML tiene una directiva @keydown.enter para ejecutar una función runCommand la primera vez que el usuario pulse la tecla Intro, enviando el comando al terminal.
Necesitaremos crear el array inicial counters en la estructura de datos de Vue.js, y crear los métodos para stopBlinking y runCommand. Reemplacemos la etiqueta <script> en el mismo archivo por:
<script>
import Logo from '~/components/Logo.vue'
export default {
components: {
Logo
},
data () {
return {
counters: [{ id: 0 }]
}
},
mounted () {
},
methods: {
stopBlinking (event) {
event.target.classList.remove('blink')
event.target.textContent = '\u00A0'
},
async runCommand (event) {
const splitCommand = event.target.textContent.trim().split(' ')
event.target.contentEditable = false
if (splitCommand.length > 3 && splitCommand[0] === 'nexmo' && splitCommand[1] === 'sms') {
const sms = await this.$axios.$get(`/api/send?text=${splitCommand.slice(3).join(' ')}&number=${splitCommand[2]}`)
this.counters.push({ id: this.counters.length, message: sms })
} else {
this.counters.push({ id: this.counters.length, message: `Unrecognized command "${splitCommand[0]}".` })
}
this.counters.push({ id: this.counters.length })
}
}
}
</script>El método runCommand es asíncrono e impide que el elemento HTML sea contentEditable. También divide el comando del terminal en 4 partes, el nombre del comando, el argumento, el número de teléfono y el mensaje de texto. El método comprueba si hay más de 3 partes en el comando y que la primera es nexmo y la segunda es sms. Si es así, realiza una petición HTTP GET utilizando axios al endpoint /api/send que creamos anteriormente, pasando el texto y el número del comando. A continuación, utiliza el mensaje que recibe para mostrarlo en la interfaz de usuario.
Si el comando no es nexmo sms number textmuestra un error genérico en la interfaz de usuario. Una vez hecho esto, añade una nueva línea con un guión bajo parpadeante en la IU, a la espera del siguiente comando.
También he reemplazado el contenido de la etiqueta <style> para colocar los logotipos de Nuxt.js en la parte superior de la ventana del terminal, y crear la animación parpadeante para el guión bajo.
<style>
.NuxtLogo {
width: 10px;
height: 10px;
position: relative;
margin: 0 10px;
bottom: 2px;
display: inline-block;
}
.blink {
animation-duration: 1s;
animation-name: blink;
animation-iteration-count: infinite;
}
@keyframes blink {
from {
opacity: 1;
}
50% {
opacity: 0;
}
to {
opacity: 1;
}
}
</style>Esto le dará la capacidad de enviar mensajes SMS desde la interfaz de usuario Vue.js, pero no permite la recepción de mensajes SMS todavía. Debido a que el Webhook de recepción de SMS es activado por Nexmo, no podemos saber desde el código de la UI cuando hay un nuevo SMS para solicitarlo. Tendremos que añadirle algún tipo de mecanismo de sondeo.
Añadir WebSockets
No soy un fan de los largos sondeos, así que en su lugar, decidí construir un par cliente/servidor WebSocket para ello. Para el servidor, estoy usando el paquete ws paquete npmasí que tendremos que instalarlo:
$ npm install wsPara construir el servidor WebSocket, vamos a editar el archivo /api/receive-sms.js para crear un servidor WebSocket en la parte superior del mismo. También voy a reemplazar la parte que registra el SMS en la consola, para enviarlo en el WebSocket en su lugar.
const WebSocket = require('ws')
let websocket = {}
const wss = new WebSocket.Server({ port: 3001 })
wss.on('connection', (ws) => {
websocket = ws
})
export default function (req, res, next) {
console.log(req.method, req.url)
if (req.method === 'POST') {
const body = []
req.on('data', (chunk) => {
body.push(chunk)
})
req.on('end', () => {
const sms = JSON.parse(body)
websocket.send(`Message from ${sms.msisdn}: ${sms.text}`)
})
}
res.statusCode = 200
res.end()
}
El servidor se inicia en el puerto 3001y envía el mensaje SMS en cuanto termina de construirse a partir de la petición. También tendremos que añadir un cliente WebSocket a la interfaz de usuario, para recibir el mensaje y mostrarlo en la interfaz de usuario. Actualicemos el archivo /pages/index.vue específicamente el método mounted() para crear un cliente WebSocket tan pronto como el componente Vue.js termine de montarse.
mounted () {
console.log(process.env.WS_URL)
const ws = new WebSocket(process.env.WS_URL)
ws.onmessage = (event) => {
this.counters[this.counters.length - 1].message = event.data
this.counters.push({ id: this.counters.length })
}
},
El cliente WebSocket se conecta al servidor process.env.WS_URLy establece una escucha de mensajes. Cuando hay un nuevo mensaje en el WebSocket, actualiza el último comando de la pantalla con los datos del evento recibidos del servidor, es decir, el mensaje SMS. También añade una nueva línea en la interfaz de usuario, con un guión bajo parpadeante.
Habrás notado que estamos usando el process.env.WS_URLasí que tenemos que añadirlo a nuestro archivo .env archivo.
WS_URL=ws://localhost:3001Debido a que la interfaz de usuario Vue.js necesita saber sobre el archivo de entorno, tenemos que añadir una entrada al respecto en el archivo de configuración Nuxt.js, nuxt.config.js.
env: {
wsUrl: process.env.WS_URL || 'ws://localhost:3001'
}, Pruébelo
Puede cargar http://localhost:3000/ en su navegador, haga clic en el guión bajo intermitente y escriba nexmo sms YOUR_PHONE_NUMBER hello. Después de pulsar Intro en el teclado, el SMS debería llegar a tu teléfono. Si respondes a ese SMS, también podrás verlo aparecer en tu navegador.

Espero que haya funcionado y acabes de aprender a enviar y recibir mensajes SMS con las APIs de Nexmo y Nuxt.js.
Compartir:
Alex Lakatos es JavaScript Developer Advocate para Nexmo. En su tiempo libre es voluntario en Mozilla como Tech Speaker y Reps Mentor. Desarrollador de JavaScript en la web abierta, ha estado empujando sus límites todos los días. Cuando no está programando en Londres, le gusta viajar por el mundo, así que es probable que te lo encuentres en la sala de espera de un aeropuerto.