https://d226lax1qjow5r.cloudfront.net/blog/blogposts/office-hours-reservation-using-node-js-and-the-vonage-messages-api/office-hours-reservation.png

Reserva de horas de oficina con Node.js y la API Messages API de Vonage

Publicado el August 16, 2022

Tiempo de lectura: 8 minutos

Introducción

En este tutorial, crearemos una aplicación web que permita a los estudiantes reservar horarios de oficina con sus profesores. Una vez que se reserva una hora de oficina, el estudiante recibirá un mensaje de texto confirmando la reserva a través de Vonage Messages API.

Lo construiremos utilizando Node.js, Express, SQLite y la API Messages API de Vonage. El repositorio repositorio de GitHub para este proyecto también está disponible.

Creación de una aplicación web

Página de inicio

Necesitaremos crear tres plantillas HTML para que sirvan como formulario de entrada (la página de inicio), página de error y página de confirmación. Después de crear estas plantillas, pasaremos al "motor" que impulsa esta aplicación web en Node.js. Para empezar, vamos a empezar con la creación de una página de inicio donde un usuario puede introducir información como su nombre, su número de teléfono, la fecha de su elección, su preferencia de tiempo, y cualquier nota o comentario que tienen que utilizar un formulario web básico. Este formulario se enviará a nuestra aplicación web para su procesamiento.

Empecemos creando un archivo index.html que incluya el título de nuestra página, importe nuestro estilo CSS y un formulario web básico para recopilar la información de los estudiantes. Debería tener este aspecto:

<html>
  <head>
    <title>Office Hours Reservation</title>
    <link type="text/css" rel="stylesheet" href="/assets/indexstyles.css" />
    <link
      rel="stylesheet"
      href="https://code.jquery.com/ui/1.13.2/themes/base/jquery-ui.css"
    />
    <script src="https://code.jquery.com/jquery-3.6.0.js"></script>
    <script src="https://code.jquery.com/ui/1.13.2/jquery-ui.js"></script>
  </head>

  <body>
    <h1>Office Hours Reservation Form</h1>
    <div id="statusdiv">Current Status</div>
    <form
      id="scheduleform"
      name="scheduleform"
      method="post"
      action="/schedule"
    >
      <div id="FormInfo">
        <div class="FormTextBox">
          <div id="alignRight">Select Date for Reservation:</div>
          <div>
            <script>
              $(function () {
                $("#datepicker").datepicker();
              });
            </script>
            <input
              type="text"
              name="AppointmentDate"
              value="7/15/2022"
              id="datepicker"
            />
          </div>
        </div>
        <!--FormTextBox-->
        <div class="FormTextBox">
          <div id="alignRight">Select Professor:</div>
          <div>
            <select name="ProfessorName">
              <option value="Professor Nimoy">Professor Nimoy</option>
              <option value="Dr. Rory">Dr. Rory</option>
              <option value="Professor Amanda">Professor Amanda</option>
              <option value="Professor Dwane">Professor Dwane</option>
              <option value="Dr. Zach">Dr. Zach</option>
            </select>
          </div>
        </div>
        <!--FormTextBox-->
        <div class="FormTextAreaBox">
          <div>Choose Appointment Time:</div>
          <div>
            <label>
              <input type="radio" name="AppointmentTime" value="8" /> 8:00 AM
            </label>
            <label>
              <input type="radio" name="AppointmentTime" value="9" /> 9:00 AM
            </label>
            <label>
              <input type="radio" name="AppointmentTime" value="10" /> 10:00 AM
            </label>
            <label>
              <input type="radio" name="AppointmentTime" value="11" /> 11:00 AM
            </label>
          </div>
        </div>
        <!--FormTextAreaBox-->
        <div class="FormTextBox">
          <div id="alignRight">Student First Name:</div>
          <div>
            <input
              type="text"
              name="studentfirstname"
              id="studentfirstname_id"
            />
          </div>
        </div>
        <!--FormTextBox-->
        <div class="FormTextBox">
          <div id="alignRight">Student Last Name:</div>
          <div>
            <input type="text" name="studentlastname" id="studentlastname_id" />
          </div>
        </div>
        <!--FormTextBox-->
        <div class="FormTextBox">
          <div id="alignRight">Student Phone Number:</div>
          <div>
            <input
              type="text"
              name="studentphonenumber"
              id="studentphonenumber_id"
            />
          </div>
        </div>
        <!--FormTextBox-->
        <div class="FormTextAreaBox">
          <div>Comments, Questions, Notes...</div>
          <div>
            <textarea
              name="studentnotes"
              id="studentnotes_id"
              style="width: 300px; height: 100px"
            >
            </textarea>
          </div>
        </div>
        <!--FormTextAreaBox-->
        <div style="text-align: center">
          <button type="submit" name="booknowbutton" class="BigButton">
            Book Now
          </button>
        </div>
      </div>
      <!--FormInfo-->
    </form>
  </body>
</html>

En nuestro ejemplo, incluimos un campo de entrada de fecha de reserva, un grupo de radio de hora de reserva, un cuadro desplegable para elegir opciones de profesor, junto con el nombre del alumno, el teléfono móvil del alumno y un campo de comentarios.

Por último, cree un botón de envío llamado "Reservar ahora" para enviar el contenido del formulario. Estamos publicando este formulario en nuestro servidor web en esta URL: /postschedule, de la que hablaremos más adelante en nuestro tutorial.

Página de error

En el caso de que el usuario introduzca datos no válidos, o se produzca un error al procesar la petición, mostraremos una página HTML de error informando al usuario de lo que ha ido mal. Esta es una simple página HTML que será procesada por Node.js y mostrará variables específicas del lado del servidor (a través de Nunjucks) que establezcamos dependiendo del problema que se haya producido. Este es un ejemplo del aspecto de nuestra página error.html página:

<html>
  <head>
    <title>There is an Error processing your request</title>

    <link type="text/css" rel="stylesheet" href="/assets/errorstyles.css" />
    <link
      rel="stylesheet"
      href="https://code.jquery.com/ui/1.13.2/themes/base/jquery-ui.css"
    />
    <script src="https://code.jquery.com/jquery-3.6.0.js"></script>
    <script src="https://code.jquery.com/ui/1.13.2/jquery-ui.js"></script>
  </head>

  <body>
    <h1>There was an Error processing your request</h1>
    <div
      id="statusdiv"
    >
      Please correct the following:
      <ul>
        {{ErrorMessage}}
      </ul>
    </div>
    <form
      id="scheduleform"
      name="scheduleform"
      method="post"
      action="/schedule"
    >
      <div id="FormInfo">
        <div class="FormTextBox">
          <div id="alignRight">Date for Reservation:</div>
          <div class="readonlydata">{{AppointmentDate}}</div>
        </div>
        <!--FormTextBox-->
        <div class="FormTextBox">
          <div id="alignRight">Appointment Time:</div>
          <div class="readonlydata">{{AppointmentTime}}</div>
        </div>
        <!--FormTextAreaBox-->
        <div class="FormTextBox">
          <div id="alignRight">Professor:</div>
          <div class="readonlydata">{{ProfessorName}}</div>
        </div>
        <!--FormTextBox-->
        <div class="FormTextBox">
          <div id="alignRight">Student Name:</div>
          <div class="readonlydata">{{StudentName}}</div>
        </div>
        <!--FormTextBox-->
        <div class="FormTextBox">
          <div id="alignRight">Student Phone Number:</div>
          <div class="readonlydata">{{StudentPhoneNumber}}</div>
        </div>
        <!--FormTextBox-->
        <div class="FormTextAreaBox">
          <div>Comments, Questions, Notes...</div>
          <div class="readonlydata">{{StudentNotes}}</div>
        </div>
        <!--FormTextAreaBox-->
        <div id="returnButton">
          <a href="/" class="BigButton"> Return to Reservation Form </a>
        </div>
      </div>
      <!--FormInfo-->
    </form>
  </body>
</html>

Nuestra página de error renderizada tendrá este aspecto:

Error pageError page

Las variables encerradas entre corchetes dobles, por ejemplo: {{AppointmentDate}}son las variables del lado del servidor que se envían a través de Node.js. Discutiremos esta configuración más adelante en este tutorial. Finalmente, crearemos un botón de retorno. Este botón devolvería al usuario a la página de inicio si se produjera un error al procesar su solicitud de reserva.

Página de confirmación

Ahora que tenemos una respuesta de error, también debemos configurar una respuesta de confirmación. Esta página se mostrará sólo si la solicitud tiene éxito. Del mismo modo, esta página mostrará toda la información introducida por el usuario. Para empezar, crearemos un archivo confirmation.html y lo configuraremos de forma similar a nuestro archivo error.html con variables del lado del servidor (Nunjucks). He aquí un ejemplo de lo que nuestra confirmation.html página:

<html>
  <head>
    <title>Your Reservation has been confirmed</title>
    <link
      type="text/css"
      rel="stylesheet"
      href="/assets/confirmationstyles.css"
    />
    <link
      rel="stylesheet"
      href="https://code.jquery.com/ui/1.13.2/themes/base/jquery-ui.css"
    />
    <script src="https://code.jquery.com/jquery-3.6.0.js"></script>
    <script src="https://code.jquery.com/ui/1.13.2/jquery-ui.js"></script>
  </head>
  <body>
    <h1>Your Reservation has been confirmed</h1>
    <form
      id="scheduleform"
      name="scheduleform"
      method="post"
      action="/schedule"
    >
      <div id="FormInfo">
        <div class="FormTextBox">
          <div id="alignRight">Date for Reservation:</div>
          <div class="readonlydata">{{AppointmentDate}}</div>
        </div>
        <!--FormTextBox-->
        <div class="FormTextBox">
          <div id="alignRight">Appointment Time:</div>
          <div class="readonlydata">{{AppointmentTime}}</div>
        </div>
        <!--FormTextAreaBox-->
        <div class="FormTextBox">
          <div id="alignRight">Professor:</div>
          <div class="readonlydata">{{ProfessorName}}</div>
        </div>
        <!--FormTextBox-->
        <div class="FormTextBox">
          <div id="alignRight">Student Name:</div>
          <div class="readonlydata">{{StudentName}}</div>
        </div>
        <!--FormTextBox-->
        <div class="FormTextBox">
          <div id="alignRight">Student Phone Number:</div>
          <div class="readonlydata">{{StudentPhoneNumber}}</div>
        </div>
        <!--FormTextBox-->
        <div class="FormTextAreaBox">
          <div>Comments, Questions, Notes...</div>
          <div class="readonlydata">{{StudentNotes}}</div>
        </div>
        <!--FormTextAreaBox-->
        <div id="returnButton">
          <a href="/" class="BigButton"> Return to Reservation Form </a>
        </div>
      </div>
      <!--FormInfo-->
    </form>
  </body>
</html>

Nuestra página de Confirmación renderizada tendrá este aspecto:

Confirmation pageConfirmation page

Tenga en cuenta que nuestras variables del lado del servidor se derivarán del formulario enviado por el estudiante, y son las siguientes: AppointmentDate, AppointmentTime, ProfessorName, StudentName, StudentPhoneNumbery StudentNotes. Por último, crearemos un botón de retorno. Este botón devolverá al usuario a la página de inicio si desea crear otra solicitud de reserva.

Backend

Opción Mecánica: Dotenv

Antes de que podamos comenzar la implementación de nuestro backend, debemos crear un archivo llamado .env. Este archivo es extremadamente importante ya que almacena nuestro API Secret y API Key privados. Estas variables nos permiten enviar nuestro texto de confirmación usando la API de Messages API de Vonage. Comience por crear un nuevo archivo llamado .env y colócalo en el directorio raíz de tu proyecto. Ahora, agrega las siguientes variables con la información específica de tu Account de Vonage: VONAGE_API_KEY, VONAGE_API_SECRETy FROM_PHONE_NUMBER.

VONAGE_API_KEY= VONAGE_API_SECRET= FROM_PHONE_NUMBER=

Configuración de Node.js como servidor de aplicaciones web

Para completar nuestra aplicación web, añadiremos funcionalidad a nuestros archivos HTML utilizando Node.js, Express y SQLite. Para simplificar, colocaremos toda la funcionalidad de nuestra aplicación web en un único archivo llamado index.js. Una vez que tengamos Node.js instalado correctamente en nuestro sistema, necesitaremos añadir algunos paquetes más a la configuración: Express, Nunjucks, Fetch y SQLite.

npm install express 

npm install nunjucks 

npm install node-fetch 

npm install sqlite3

npm install dotenv 

npm install @vonage/server-sdk

Configuramos nuestro archivo index.js para utilizar las librerías recién instaladas y dar a nuestra aplicación la capacidad de servir páginas web en el puerto 3000. El inicio de nuestro archivo index.js tiene el siguiente aspecto:

require("dotenv").config();
const express = require("express");
const nunjucks = require("nunjucks");
const webserver = express();
const Vonage = require("@vonage/server-sdk");
const SMS = require("@vonage/server-sdk/lib/Messages/SMS");

const fetch = (...args) =>
  import("node-fetch").then(({ default: fetch }) => fetch(...args));

webserver.use("/assets", express.static("assets"));

nunjucks.configure("html", { autoescape: false, express: webserver });

webserver.use(express.json());

webserver.use(express.urlencoded({ extended: true }));

// Global database
const sqlite3 = require("sqlite3").verbose();
const db = new sqlite3.Database("reservations.db");

const vonage = new Vonage({
  apiKey: process.env.VONAGE_API_KEY,
  apiSecret: process.env.VONAGE_API_SECRET,
});

webserver.listen(3000);

Algunas notas sobre el código:

  • Estamos llamando al servidor web de la instancia Express

  • Colocamos nuestros tres archivos HTML (index.html, error.html, confirmation.html) en un subdirectorio llamado HTML que Nunjucks está configurado para mirar.

  • Hemos configurado SQLite para utilizar una base de datos llamada reservations.db que estará en el directorio raíz de esta aplicación web.

  • Estamos ocultando las variables Clave de API y Secreto de API que nos proporcionó la API de Messages API de Vonage en un archivo .env pero deberás sustituir estas variables por tus propias credenciales seguras.

Estamos configurando el servidor web para que escuche las peticiones web en el puerto 3000. Esto significa que para ejecutar esta aplicación, tendrá que especificar este puerto, por ejemplo, http://localhost:3000.

Uso de Node.js como servidor de aplicaciones web

Nuestro código index.js código para proporcionar tres funciones:

  • Configurar la base de datos para almacenar las solicitudes de reserva (reservations.db)

  • Mostrar una página de inicio en la que un estudiante pueda enviar una solicitud de cita (index.html)

  • Procese la solicitud de reserva del estudiante enviada a /schedule y, una vez revisado el envío, responder con éxito (confirmation.html) o error (error.html)

SetUpDatabase();

DisplayHomePage();

PostSchedule();

Configuración de la base de datos

En este proyecto, estamos utilizando SQLite como base de datos backend para almacenar nuestra información. Sin embargo, puedes utilizar cualquier base de datos que funcione con Node.js.

function SetUpDatabase() {
  db.serialize(() => {
    db.run(
      "CREATE TABLE if not exists Appointments (appointmentdate text,    appointmenttime text, professorname text, studentfirstname text, studentlastname text, studentphonenumber text, studentnotes text)"
    );
  });
}

Algunas notas sobre esta función:

  • Se comprobará si existe una tabla de citas en la base de datos. reservations.db base de datos; si la tabla no existe, la creará

  • Dependiendo del sistema de base de datos que decidas utilizar con Node.js, querrás cambiar la fecha y la hora de la cita para que funcionen con las funciones datetime de ese motor

Configuración de la página de inicio

Nuestra función DisplayHomePage() simplemente escucha la petición en la raíz del directorio ('/') y luego renderiza nuestro archivo index.html que se encuentra en el directorio HTML. Recordará que ya hemos configurado el archivo index.html para que sea el formulario de reserva de horas de oficina.

function DisplayHomePage() {
  // Displaying Homepage after reading an HTML file locally

  webserver.get("/", function (req, res) {
    res.render("index.html");
  });
}

Configuración de la función de planificación de contabilización

PostSchedule() es el caballo de batalla de nuestra aplicación web. Este proceso escuchará el /schedule y realizará una comprobación básica de errores de la entrada del usuario. Si los datos de envío pasan nuestro rudimentario proceso de comprobación de errores, guardaremos esta información en nuestra tabla de citas en la base de datos reservations.db base de datos. Luego, encapsularemos esta información en un objeto JSON que enviaremos a la API de Messages API de Vonage para enviar un mensaje de confirmación.

function PostSchedule() {
  // POST /new Route Handler
  webserver.post("/schedule", function (req, res) {
    // Student info/variables collected from HTML form

    let professorname = req.body.ProfessorName;
    let appointmentdate = req.body.AppointmentDate;
    let appointmenttime = req.body.AppointmentTime;
    let studentfirstname = req.body.studentfirstname;
    let studentlastname = req.body.studentlastname;
    let studentphonenumber = req.body.studentphonenumber;
    let studentnotes = req.body.studentnotes;

    // Text message info sent to Student
    let message =
      "Dear " +
      studentfirstname +
      " " +
      studentlastname +
      "," +
      " you have successfully booked your appointment! Here are your additional notes: " +
      studentnotes;

    let ValidationCheck = true; // Assume success, prove otherwise
    let ErrorMessage = "";

    if (appointmentdate == "") {
      ValidationCheck = false;
      ErrorMessage = ErrorMessage + "<li>Enter an appointment date</li>";
    } else if (appointmenttime == "" || !appointmenttime) {
      ValidationCheck = false;
      ErrorMessage = ErrorMessage + "<li>Enter an appointment time</li>";
    } else if (professorname == "") {
      ValidationCheck = false;
      ErrorMessage = ErrorMessage + "<li>Select a Professor</li>";
    } else if (studentfirstname == "") {
      ValidationCheck = false;
      ErrorMessage = ErrorMessage + "<li>Enter a Student first name</li>";
    } else if (studentlastname == "") {
      ValidationCheck = false;
      ErrorMessage = ErrorMessage + "<li>Enter a Student last name</li>";
    } else if (studentphonenumber == "") {
      ValidationCheck = false;
      ErrorMessage = ErrorMessage + "<li>Enter a mobile phone number</li>";
    }

    if (ValidationCheck == false) {
      res.render("error.html", {
        ProfessorName: professorname,
        StudentName: studentfirstname + " " + studentlastname,
        AppointmentDate: appointmentdate,
        AppointmentTime: appointmenttime,
        StudentNotes: studentnotes,
        StudentPhoneNumber: studentphonenumber,
        ErrorMessage: ErrorMessage,
      });
    } else {

Algunas notas sobre este código:

  • Nuestra instancia Express llamada servidor web está a la escucha de un mensaje enviado a /schedule y sólo se ejecuta si se da esta condición

  • Hemos creado las siguientes variables Node.js a los correspondientes pares nombre/valor del formulario de solicitud HTML:

professorname = req.body.ProfessorName
  appointmentdate = req.body.AppointmentDate
  appointmenttime = req.body.AppointmentTime
  studentfirstname = req.body.studentfirstname
  studentlastname = req.body.studentlastname
  studentphonenumber = req.body.studentphonenumber
  studentnotes = req.body.studentnotes
  • Nuestra variable booleana ValidationCheck comprueba si hay datos de cadena vacíos y se establece en false en caso de fallo. Deberá realizar una comprobación de errores adicional para otros escenarios de datos erróneos, como fechas y horas de citas no válidas

  • Si se produce un error, generamos la página error.html que incluye la configuración de la variable Nunjucks {{ErrorMessage}} con una descripción del problema

  • En ValidationCheck éxito (devolviendo: true), continuaremos agregando los datos de la cita a la tabla de citas y enviaremos la confirmación de texto mediante la API de mensajería de Vonage.

Luego de la validación exitosa de la entrada, ahora encapsulamos el mensaje en un objeto JSON que enviaremos a Vonage. Creamos la instancia de la clase cliente de Vonage, la inicializamos con la clave y el secreto de la API de Vonage que agregaste previamente a tu archivo .env y las variables necesarias son el número de teléfono, el número de teléfono del destinatario, el texto del mensaje y nuestra clave y secreto de la API.

else {

    vonage.messages.send(
      new SMS(message, studentphonenumber, process.env.FROM_PHONE_NUMBER),
      (err, data) => {
        if (err) {
          console.error(err);
        } else {
          console.log(data.message_uuid);
        }
      }
    );

    // Build SQL string --> insert string to add record to appointments table
    let sqlstring =
      "INSERT INTO Appointments (appointmentdate, appointmenttime, professorname, studentfirstname, studentlastname, studentphonenumber, studentnotes) " +
      "VALUES (?, ?, ?, ?, ?, ?, ?)";

    // Execute SQL string into database (Using paramaterized queries to prevent SQL injection)
    db.run(
      sqlstring,
      appointmentdate,
      appointmenttime,
      professorname,
      studentfirstname,
      studentlastname,
      studentphonenumber,
      studentnotes
    );
    
    // For testing purposes query all records in appointments table and display to console
    // The last record shown should be the record we just inserted
    db.each(
      "SELECT appointmentdate, appointmenttime, professorname, studentfirstname, studentlastname info FROM Appointments",
      (err, row) => {
        console.log(
          row.appointmentdate +
            ": " +
            row.appointmenttime +
            ":" +
            row.professorname
        );
      }
    );

    res.render("confirmation.html", {
      ProfessorName: professorname,
      StudentName: studentfirstname + " " + studentlastname,
      AppointmentDate: appointmentdate,
      AppointmentTime: appointmenttime + ":00 AM",
      StudentNotes: studentnotes,
      StudentPhoneNumber: studentphonenumber,
    });
}

A continuación, construimos nuestra sentencia de inserción SQL utilizando las variables que establecimos desde el formulario de envío HTML, y ejecutamos esa sentencia utilizando la función db.run . Por último, mostramos la confirmation.html para que se muestren las variables Nunjucks.

Próximos pasos

La participación de la comunidad es siempre bienvenida. No dude en unirse a nosotros en GitHub y en Slack de la comunidad de Vonage o envíanos un mensaje en Twitter.

Compartir:

https://a.storyblok.com/f/270183/400x520/992948a3b3/zoe-mithaug.png
Zoe MithaugBecario de Relaciones con los Promotores

Zoe Mithaug es becaria de Relaciones con Desarrolladores en Vonage. Es estudiante de cuarto año de licenciatura en la Universidad de Florida.

Le apasionan la programación, el desarrollo de software, el desarrollo de videojuegos, la animación y el diseño digital.