https://d226lax1qjow5r.cloudfront.net/blog/blogposts/build-an-appointment-scheduler-using-node-firebase-and-vonage/appointment-scheduler.png

Erstellen eines Terminplaners mit Node, Firebase und Vonage

Zuletzt aktualisiert am December 16, 2021

Lesedauer: 12 Minuten

Einführung

In diesem Tutorial werden wir eine Terminplanungs-Webanwendung mit Node.js, Express, Firebase und der Vonage Messages API. Das GitHub-Repository für dieses Projekt ist ebenfalls verfügbar, Sie können es hier klonen.

Firebase einrichten

Um zu beginnen, erstellen wir ein neues Projekt in der Firebase-Konsole.

  • Klicken Sie auf add a new project

  • Geben Sie Ihrem Projekt einen aussagekräftigen Namen, zum Beispiel vonage appointment scheduler

  • Prüfen Sie, ob Ihnen die eindeutige Kennung für Ihr Projekt gefällt (sie wird in Ihrer Echtzeit-Datenbank-URL, den Firebase Hosting-Subdomains und mehr verwendet. Sie kann nach der Projekterstellung nicht mehr geändert werden)

  • Klicken Sie auf die Schaltfläche, um fortzufahren

Console view with a text field to enter project and name and edit the project idConsole view with a text field to enter project and name and edit the project id

  • Wählen Sie aus, ob Sie die Analysefunktion aktivieren möchten. In diesem Lernprogramm werden wir das nicht tun

  • Klicken Sie auf die Schaltfläche, um das Projekt zu erstellen

  • Warten, bis das Projekt erstellt ist

Project being createdProject being created

  • Sobald das Projekt fertig ist, klicken Sie auf "Weiter". Sie werden zur Konsolenansicht Ihres Projekts weitergeleitet

  • Legen Sie die Abrechnungsart fest, indem Sie auf das Zahnradsymbol klicken, gefolgt von Nutzung und Abrechnung, dann auf die Registerkarte Details & Einstellungen und ändern Sie den Plan zur Verwendung von Blaze. Dieser Abrechnungstarif ist erforderlich, wenn Sie eine Drittanbieter-API verwenden.

Firebase Tools CLI installieren

Installieren Sie in Ihrem Terminal die Firebase-Tools mit NPM, falls Sie sie nicht bereits haben, indem Sie Folgendes eingeben: npm install -g firebase-tools. Geben Sie anschließend firebase login ein. Daraufhin öffnet sich ein Fenster in Ihrem Browser, das Sie entweder automatisch authentifiziert (wenn Sie bereits angemeldet sind) oder Sie nach Ihren Anmeldedaten fragt. Sobald dies abgeschlossen ist, haben Sie die Firebase CLI installiert.

RealTime-Datenbank erstellen und einrichten

Nun ist es an der Zeit, die NoSQL-Datenbankinstanz zu erstellen, in der die Informationen zu den Terminen gespeichert werden sollen. Unsere App wird eine Ansicht enthalten, in der ein Benutzer Termine vereinbaren oder absagen kann. Wenn die Person, die mit der Ansicht interagiert, ein Termindatum und eine Uhrzeit auswählt, wird dieser Slot der Firebase RealTime Database hinzugefügt oder entfernt.

  • Klicken Sie im Menü der Firebase-Konsole unter Build auf "Realtime Database".

Button to create the databaseButton to create the database

  • Klicken Sie auf "Datenbank erstellen".

  • Wählen Sie den Speicherort der Echtzeitdatenbank, in der Ihre Daten gespeichert werden sollen, und klicken Sie auf next

  • Wählen Sie, ob Sie die Datenbank im gesperrt oder im Testmodus. In diesem Beispiel verwende ich den Testmodus

  • Klicken Sie auf enable

    Database createdDatabase created

Importieren der Datenbank JSON-Datei

Lassen Sie uns eine Beispieldatenbank importieren, die bereits einige zugewiesene Slots enthält und aus der Sie in Zukunft Slots hinzufügen und entfernen können. Sie können eine Datei namens myAppointments.json erstellen, die das JSON aus dem folgenden Ausschnitt enthält, und diese Datei dann über die Konsole importieren.

myAppointments.json
{
  "myAppointments": {
    "0": {
      "date": "2021-06-01T09:00",
      "userId": "1234abcd"
    },
    "new_activity_7kh3a3a3z": {
      "date": "2023-06-01T08:50",
      "userId": "_7kh3a3a3z"
    },
    "new_activity_etxen95x3": {
      "date": "2021-06-01T08:40",
      "userId": "_etxen95x3"
    }
  }
}

Import DatabaseImport Database

Hinzufügen der Datenbankregeln

Die Firebase Realtime Database Rules bestimmen, wer auf Ihre Datenbank zugreifen darf, wie Ihre Indizes aufgebaut sind und wie Ihre Daten strukturiert sind.

  • In der Firebase-Konsole sehen Sie in der Ansicht der Echtzeit-Datenbank "Regeln", klicken Sie auf diese Registerkarte. Sie werden zu einem Bildschirm weitergeleitet, auf dem Sie Ihre Regeln bearbeiten können

  • Kopieren Sie die Regeln aus dem folgenden Codeschnipsel und fügen Sie sie in Ihre Konsole ein, um die myAppointments Sammlung zu indizieren durch das date Feld zu indizieren.

  • Klicken Sie auf Publish

{
  "rules": {
    ".read": "now < 1643842800000",  // 2022-2-3
    ".write": "now < 1643842800000",  // 2022-2-3
    "myAppointments": {
      ".indexOn": ["date"]
    }
  }
}

Edit Firebase Database RulesEdit Firebase Database Rules

Erstellen Sie die Projektstruktur

Am Ende dieses Tutorials wird Ihre Projektstruktur ungefähr so aussehen. In den folgenden Schritten werden wir die Dateien erstellen, die den Inhalt, das Erscheinungsbild und die Funktionen aufbauen und die von uns verwendeten Dienste verwalten.

Project Structure also displayed as a code block below for accessibilityProject Structure

appointment-scheduler | public | |- styles | | | L styles.css | |- favicon.ico | L index.html |- script | |- server.js |- .env |- .firebaserc |- README.md |- firebase.json |- package-lock.json |- package.json |- serviceAccountKey.json

Einrichtung

  • Erstellen Sie den Projektordner und cd hinein: mkdir appointment-scheduler && cd appointment-scheduler

  • NPM initialisieren : npm init. Mit diesem Befehl werden Sie aufgefordert, Informationen über das Projekt hinzuzufügen

  • Installieren Sie die Abhängigkeiten: npm install @vonage/server-sdk dotenv uuid express firebase-admin firebase-functions

  • Typ firebase init. Da wir bereits ein Projekt im Dashboard erstellt haben, können Sie Use an existing project wählen, woraufhin Sie aufgefordert werden, das gewünschte Projekt auszuwählen. Sie können mein Beispiel mit meiner Projekt-ID sehen vonage-appointment-scheduler unten. Ich habe mich auch für die Realtime Database Funktion

? Which Firebase features do you want to set up for this directory? Press Space to select features, then Enter to confirm your choices. Realtime Database: Configure a security rules file for Realtime Database and (optionally) provision default instance === Project Setup First, let's associate this project directory with a Firebase project. You can create multiple project aliases by running firebase use --add, but for now, we'll just set up a default project. ? Please select an option: Use an existing project ? Select a default Firebase project for this directory: vonage-appointment-scheduler (vonage appointment scheduler) i Using project vonage-appointment-scheduler (vonage appointment scheduler)

Erstellen Sie den HTML-Inhalt

Wussten Sie, dass das HTML-Eingabeelement viele Optionen für die Auswahl von Datum und Uhrzeit bietet? Wir haben zum Beispiel: date, datetime-local, time. In diesem Lernprogramm werden wir <input type="datetime-local">. Dieser Ansatz ist vielleicht nicht so stabil wie die Verwendung der Datums- und Zeitbibliothek, da es zu einigen Unstimmigkeiten kommen kann, aber für den Zweck dieses Tutorials funktioniert er. Der Benutzer kann alle 5 Minuten Zeitfenster buchen, die auf 0 oder 5 enden, z.B. 18:00 ist buchbar, 18:01 nicht.

  • Erstellen Sie die public/index.html die den Inhalt für die Ansicht zum Auswählen eines neuen Termins oder zum Stornieren von Terminen enthält, indem Sie den folgenden Codeschnipsel hinzufügen

  <!-- index.html -->
<!DOCTYPE html>
<html>
  <head>
    <title>Appointment Scheduler</title>

    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link rel="stylesheet" href="styles/styles.css" />
  </head>
  <body>
    <main>
      <h1>Appointment Scheduler</h1>
      <!-- datepicker from html. Source: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/datetime-local -->
      <form action="/appointment" method="POST">
        <div>
          <label for="slot">Choose your slot: </label>
          <input
            id="slot"
            type="datetime-local"
            name="slotdate"
            min="2021-06-01T08:30"
            max="2023-10-30T16:30"
            step="300"
            required
          />
          <span class="validity"></span>
        </div>
        <div>
          <label for="phonenumber">Your phone number:</label>
          <input type="tel" id="phonenumber" name="phonenumber" required />
          <span class="validity"></span>
        </div>
        <div>
          <input type="submit" value="Book slot!" />
        </div>
      </form>
      <form action="/cancelAppointment" method="POST">
        <div>
          <input type="text" name="code" placeholder="code" />
          <input type="submit" value="Remove slot!" />
        </div>
      </form>
    </main>
  </body>
</html>

CSS-Styling hinzufügen

Für diese Demo-Webanwendung fügen wir einige Formatierungen hinzu, um den Inhalt auf der Seite zu zentrieren und ein rotes ✖ anzuzeigen, wenn die Eingabe ungültig ist, und ein ✓, wenn sie gültig ist.

  • Erstellen Sie die public/styles.css Datei

  • Fügen Sie den folgenden CSS-Code ein

body {
    margin: auto;
    width: 50%;
    padding: 10px;
}

div {
    margin-bottom: 10px;
    display: flex;
    align-items: center;
}

label {
  display: inline-block;
  width: 300px;
}

input:invalid+span:after {
    content: '✖';
    color: red;
    padding-left: 5px;
}

input:valid+span:after {
    content: '✓';
    color: green;
    padding-left: 5px;
}

Erstellen Sie die Umgebungsvariablendatei

  • Erstellen Sie das .env und füllen Sie sie mit den folgenden Informationen

FIREBASE_DATABASE_URL= VONAGE_API_KEY= VONAGE_API_SECRET= VONAGE_FROM_NUMBER= VONAGE_TO_NUMBER=
  • Die FIREBASE_DATABASE_URL kann in der Firebase-Konsole gefunden werden

  • Die VONAGE_API_KEYund die VONAGE_API_SECRET finden Sie im Vonage Dashboard

  • Die VONAGE_FROM_NUMBER enthält die Nummer, den Namen oder die Marke, die als Absender der Nachricht erscheinen wird

  • Die VONAGE_TO_NUMBER ist die Nummer, die die SMS-Nachrichten erhalten wird

Erstellen Sie die JavaScript-Dateiserver.js

Wir erstellen die server.js um Express mitzuteilen, wie die von der Benutzeroberfläche gestellten Anfragen zu behandeln sind. Ich werde Ihnen Schritt für Schritt zeigen, wie wir ihn erstellen. Die komplette Serverdatei finden Sie hier.

Unsere Webanwendung wird express verwenden und die statischen Dateien lesen, die wir zuvor im Ordner public Ordner.

  • Um die Abhängigkeiten und Importdateien hinzuzufügen, fügen Sie den folgenden Codeschnipsel in Ihr script/server.js

// script/server.js
require('dotenv').config();
const express = require('express');
const app = require('express')();
const port = 3000; //setting the port to listen to as 3000
const admin = require('firebase-admin');
const Vonage = require('@vonage/server-sdk');
const SMS = require('@vonage/server-sdk/lib/Messages/SMS';
const { v4: uuidv4 } = require('uuid');

app.use(express.static('public'));

app.use(express.json());

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

Hinzufügen des Service Accounts

Ein Firebase Service Account kann verwendet werden, um verschiedene Firebase Features zu authentifizieren; für unser Projekt werden wir das Firebase Admin SDK verwenden, um auf unsere Datenbank URL zuzugreifen.

  • Klicken Sie in der Firebase-Konsole auf das Zahnrad und wählen Sie die Registerkarte Service Account

  • Klicken Sie auf die Schaltfläche, um generate key

  • Fügen Sie die erzeugte Datei in das Stammverzeichnis Ihres Projekts ein und benennen Sie sie um in serviceAccountKey.json

  • Kopieren Sie das Admin SDK-Konfigurations-Snippet und fügen Sie es in Ihr Projekt ein, wie Sie im folgenden Schritt dieses Tutorials sehen können, um Firebase zu initialisieren. Wir verwenden ${process.env.FIREBASE_DATABASE_URL zum Lesen der URL aus der .env Datei zu lesen, aber es ist die gleiche Datenbank-URL wie in der Firebase Admin SDK-Konfiguration.

Admin SDK configurationAdmin SDK configuration

Firebase initialisieren

Wir verwenden initializeApp um eine Firebase-App-Instanz zu erstellen und zu initialisieren, die die /myAppointments Firebase-Datenbankinstanz verwendet, die wir zuvor über die Firebase-Konsole erstellt und befüllt haben.

  • Fügen Sie den folgenden Codeschnipsel zu Ihrem server.js um Firebase zu initialisieren.

const serviceAccount = require('../serviceAccountKey.json');

// Initializes firebase
admin.initializeApp({
  credential: admin.credential.cert(serviceAccount),
  databaseURL: `${process.env.FIREBASE_DATABASE_URL}`,
});

// A Reference represents a specific location in your Database and can be 
// used for reading or writing data to that Database location.
ref = admin.database().ref('/myAppointments');

Initialisieren des Vonage API-Objekts

Wir erstellen die Instanz der Vonage-Client-Klasse und initialisieren sie mit dem Vonage-API-Schlüssel und dem Geheimnis, die Sie zuvor in Ihre .env Datei hinzugefügt haben.

  • Fügen Sie den folgenden Codeschnipsel zu Ihrem server.js um Vonage hinzuzufügen.

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

Erstellen Sie die Funktion getDateTime()

Der HTML-Eingabetyp datetime-local ist formatiert als JJJJ-MM-TThh:mm. Wir werden also eine Funktion schreiben, die das Datum von der Stunde trennt, indem sie es an dem Zeichen T. Zum Beispiel im Beispiel 2018-06-12T19:30hätten wir dann 2018-06-12 für das Datum und 19:30 für die Stunde.

  • Fügen Sie den folgenden Codeschnipsel zu Ihrem server.js hinzu, um die getDateTime() Funktion

  const getDateTime = (slot) => {
    return slot.split('T');
  };

Erstellen Sie die/appointment Endpunkt

Es ist an der Zeit, den /appointment Endpunkt zu erstellen, der die POST-Anfragen zur Erstellung eines Termins bearbeitet. Dieser Endpunkt prüft, ob der Termin verfügbar ist, fügt den Termin zur Firebase-Datenbank hinzu und sendet schließlich eine SMS-Bestätigung über die Vonage Messages API an das Telefon des Benutzers zurück.

  • Fügen Sie den folgenden Codeschnipsel in Ihr server.js um den /appointment Endpunkt zu erstellen.

app.post('/appointment', async (request, response) => {
  let phonenumber = request.body.phonenumber;
  let slot = request.body.slotdate;
  let [date, time] = getDateTime(slot);

  // Checks if a slot is available
  checkIfAvailable = async (slot) => {};
  
  // Adds to Database
  addToDatabase = () =>  {};
  
  // Sends an SMS back to the user's phone using the Vonage Messages API
  sendSMStoUser = async (code) => {};
});

Sie haben vielleicht bemerkt, dass ein Großteil der Funktionalität innerhalb des Request Handlers noch nicht implementiert wurde. Daher wollen wir nun die Stubs für die erforderlichen Funktionalitäten erweitern.

Slot-Verfügbarkeit prüfen

Diese Funktion überprüft, ob ein Slot verfügbar ist, indem sie prüft, ob der Slot bereits in der Datenbank existiert. Wir fragen ab ref.orderByChild('date'). Die Abfragen dürfen jeweils nur einen Schlüssel anfordern. Wir haben zuvor unseren Index über die .indexOn in den Firebase Rules definiert, um die Leistung zu verbessern. Und dann verwenden wir .once('value') um auf genau ein Ereignis des Wertes zu warten, und dann hört es auf zu warten.

  • Fügen Sie den folgenden Codeschnipsel in Ihr server.js hinzu, um die checkIfAvailable() Funktion

  // Checks if a slot is available
  checkIfAvailable = async (slot) => {
    let snapshot = await ref.orderByChild('date').once('value');

    let available = true;
    
    snapshot.forEach((data) => {
      let dataval = data.val();
      for (let key in dataval) {
        let datapoint = dataval[key];
        if (slot === datapoint) {
          available = false;
        }
      }
    });
    return available;
  };

Den Slot zur Datenbank hinzufügen

Die folgende Funktion addToDatabase() fügt den Termin und einen Code in die Firebase-Datenbank ein. Dieser Code wird benötigt, um den Termin zu stornieren.

  // Adds the slot to the database
  addToDatabase = () => {
    let code = uuidv4();

    ref.child(code).set({
      date: slot,
      userId: code,
    });

    return code;
  };

Senden Sie eine SMS mit den Termindaten

Sobald der Platz reserviert ist, wird eine SMS-Bestätigung an den Benutzer zurückgeschickt mit der Nachricht Meeting booked at ${time} on date: ${date}. Please save this code: ${code} in case you'd like to cancel your appointment. wie in der Funktion zu sehen ist sendSMStoUser().

  • Fügen Sie den folgenden Codeschnipsel in Ihr server.js hinzu, um die sendSMStoUser() Funktion

// Sends an SMS back to the user's phone using the Vonage Messages API
sendSMStoUser = async (code) => {
  const to = phonenumber;
  const text = `Meeting booked at ${time} on date: ${date}. Please save this code: ${code} in case you'd like to cancel your appointment.`;
  const result = await new Promise((resolve, reject) => {
    vonage.messages.send(
      new SMS(text, process.env.VONAGE_TO_NUMBER, "Vonage"),
      (err, data) => {
        if (err) {
          console.error(err);
        } else {
          console.log(data.message_uuid);
        }
      }
    );
  });
};

Finalisieren Sie die Geschäftslogik

Der folgende Code ist für den Aufruf der zuvor erstellten Hilfsfunktionen verantwortlich. Wenn der Termin verfügbar ist, wird er in die Datenbank aufgenommen und die SMS an den Nutzer zurückgeschickt. Andernfalls wird er aufgefordert, einen anderen Termin zu wählen.

let available = await checkIfAvailable(slot);

if (available) {
	let code = addToDatabase();
	await sendSMStoUser(code);
	response.send(`This slot is available, booking it for you now: ${slot}`);
} else {
	// Sends user error
	response.send(
		`Sorry, you'll need to choose a different slot.${slot} is already busy.`
	);
}

Den Termin absagen/cancelAppointment

Erstellen wir den /cancelAppointment Endpunkt, der die POST-Anfragen zur Stornierung eines Termins aus der Datenbank bearbeitet, indem er einen vom Benutzer bereitgestellten Code verwendet, den er bei der Planung seines Termins erhalten hat.

app.post('/cancelAppointment', async (request, response) => {
  let code = request.body.code;

  // Removes slot from the database
  removeSlotFromDB = (code) => {
    ref.child(code).remove();
  };
  removeSlotFromDB(code);

  response.send(`This slot has been removed.`);
});

Den Hafen anhören

Schließlich wird die Anwendung auf dem angegebenen Port lauschen; wenn sie lokal ausgeführt wird, ist sie erreichbar unter https://localhost:${port}. Unter dieser URL können Sie mit der Benutzeroberfläche dieser Demoanwendung interagieren und die hinzugefügten/entfernten Slots auf der Firebase-Konsolen-Webseite überprüfen.

app.listen(port, () => {
  console.log(`I run on port ${port}`);
});

Testen Sie es aus

  • In Ihrer package.json Datei fügen Sie das Startskript "start": "node script/server.js" direkt unter "test": "echo \"Error: no test specified\" && exit 1",. Es sollte wie folgt aussehen:

"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "node script/server.js" },
  • Installieren Sie alle Abhängigkeiten npm install

  • Führen Sie den NPM-Befehl aus, um das Projekt auszuführen npm run start

  • Navigieren Sie zu http://localhost:3000

  • Hinzufügen und Entfernen von Terminen und Anzeige, wie sie in der Firebase Realtime Database hinzugefügt und entfernt werden

Example adding a slot and it is shown on the Firebase Realtime databaseExample adding a slot and it being shown on the Firebase Realtime database

Schlussfolgerung und nächste Schritte

Heute haben Sie gesehen, wie man eine Demo-Webanwendung für einen Terminplaner erstellt. Jetzt können Sie weitermachen und ein ausgefalleneres Styling und andere Funktionen hinzufügen. Mit dem, was Sie hier gelernt haben, können Sie viele Terminplaner erstellen, sei es für ein Fitnessstudio oder für einen Impftermin - lassen Sie Ihrer Kreativität freien Lauf!

Erreichen Sie uns auf Twitter und treten Sie unserer Gemeinschaft auf Slack bei.

Teilen Sie:

https://a.storyblok.com/f/270183/400x400/3f6b0c045f/amanda-cavallaro.png
Amanda CavallaroAdvokat für Entwickler