
Teilen Sie:
Chris ist Developer Relations Tooling Manager und leitet das Team, das Ihre Lieblingstools entwickelt. Er programmiert seit mehr als 15 Jahren in verschiedenen Sprachen und für verschiedene Projekttypen, von der Kundenarbeit bis hin zu Big-Data-Großsystemen. Er lebt in Ohio, verbringt seine Zeit mit seiner Familie und spielt Video- und TTRPG-Spiele.
Sprechen Sie mit Ihrer Datenbank mit Directus
Lesedauer: 10 Minuten
Sprechen Sie mit Ihrer Datenbank mit Directus
"Spiele deine Stärken aus" ist ein wichtiger Bestandteil der Softwareentwicklung. Viele Probleme und Fehler werden verursacht, wenn jemand etwas entwickelt, das nicht in seinem Fachgebiet liegt. Authentifizierung und Datenbanken sind zwei dieser Dinge, die unglaublich schwer herauszufinden sein können, und wenn Sie eine App entwickeln, warum sollten Sie einen Haufen Zeit mit Dingen verbringen, die nicht direkt mit Ihrer App zu tun haben?
Directus ermöglicht es uns, die Datenbankverwaltung auf ein externes System zu übertragen und die Arbeit mit unseren Daten zu erleichtern. Es kann nicht nur zum Speichern von Daten verwendet werden, sondern auch, um unsere Daten auf verschiedene Weise außerhalb unserer Anwendung zugänglich zu machen. Sie können sich ihre 100+ Mehr Dinge zum Bauen Artikel, um ein Gefühl für alles zu bekommen, was sie tun.
Werfen wir einen Blick auf ihr Datenbankangebot. Wir werden eine Beispielanwendung durchgehen, die es den Benutzern ermöglicht, Kinokarten zu kaufen und zu stornieren, und wir werden Directus für die Datenspeicherung verwenden. Wir können dann die Vonage Voice API verwenden, um den Benutzern die Möglichkeit zu geben, anzurufen, ihre Tickets zu verwalten und Directus oder andere Dienste mit der Anzeige im Web zu beauftragen.
Voraussetzungen
Ein Vonage Entwickler-Konto. Klicken Sie auf Sign Up um eines zu erstellen, wenn Sie noch keines haben.
A Directus Konto.
Ein Tunnelbau-System wie ngrok.
Directus einrichten
Falls Sie es noch nicht getan haben, besuchen Sie den Directus-Schnellstart-Anleitung und folgen Sie den Anweisungen, um entweder ein Directus-Cloud-Konto einzurichten (das wir für diese Demo verwenden) oder um Directus lokal einzurichten. Sie benötigen ein GitHub-Konto, um sich für ein Cloud-Konto anzumelden.
Wir müssen die Datenquellen einrichten, auf die wir von Directus aus zugreifen werden. Wir werden eine Sammlung "Filme" einrichten, die eine Liste von Filmen enthält, die derzeit gezeigt werden. Wir werden auch eine Sammlung "Showtimes" haben, die alle individuellen Vorstellungszeiten eines Films enthält. Dann gibt es noch eine Sammlung "Tickets", die die einzelnen gekauften Tickets enthält.
Filme werden mit Vorführzeiten verknüpft, und Tickets werden mit Vorführzeiten verknüpft.
Um eine neue Sammlung in Directus zu erstellen, klicken Sie auf das Zahnrad-Symbol in Ihrer Seitenleiste und wählen Sie "Datenmodell".
Directus Sidebar - Settings
Dadurch erhalten Sie eine Liste der Modelle, die Sie derzeit haben. Klicken Sie auf die große Schaltfläche "+" (Plus) in der oberen rechten Ecke des Bildschirms, um ein neues Modell zu erstellen.
Creating a new collection in Directus
Geben Sie dem Modell einen Namen, in diesem Fall "Filme". Dies wird der Name sein, mit dem wir die Sammlung beim Aufruf über das Directus SDK referenzieren werden. Klicken Sie auf die rechte Pfeiltaste, um fortzufahren.
Sie können weitere Felder hinzufügen, aber wir lassen diese vorerst unausgewählt. Klicken Sie auf das Häkchensymbol in der oberen rechten Ecke, um das Modell zu erstellen.
Dies führt Sie zum Bearbeitungsbildschirm des Modells.
Movie Collection Edit Screen
Hier können wir neue Felder erstellen und sie dem Modell hinzufügen. Die Sammlung "Filme" ist recht einfach, also klicken Sie auf "Feld erstellen" und wir fügen die folgenden Felder hinzu:
Titel - Wählen Sie "Eingabe", den Typ "String" und "Erforderlich".
Beschreibung - Wählen Sie "Datetime", den Typ "Datetime" und die Option "Erforderlich".
start_time - Wählen Sie "Eingabe", den Typ "String" und wählen Sie "Erforderlich".
Außerdem müssen wir zwei weitere Modelle und die dazugehörigen Felder erstellen:
Vorstellungszeiten
film_id - Wählen Sie unter "Relational" die Option "Many to One", und geben Sie unter "Related Collection" "movies" ein.
start_time - Wählen Sie "Datetime", der Typ ist "Datetime", und wählen Sie "Erforderlich".
Eintrittskarten
vorstellungszeit_id - Wählen Sie unter Relational "Many to One" und geben Sie unter "Related Collection" "showtimes" ein.
Benutzer - Wählen Sie unter Relational "Many to One" und geben Sie unter "Related Collection" "directus_users" ein.
Sobald Sie diese Sammlungen erstellt haben, können Sie einige Filme in die Filmsammlung und die dazugehörigen Vorführzeiten eingeben.
Für unsere Demo wollen wir auch einen Benutzer erstellen, der im Namen des Benutzers mit unseren Sammlungen interagiert. Wir tun dies, weil jemand, der uns anruft, nicht in der Lage sein wird, einen Benutzernamen und ein Passwort einzugeben, also werden wir die Vorteile der setToken() Composable auf dem Directus-Client nutzen.
Klicken Sie auf das Symbol "Benutzer" in der Seitenleiste, um die Seite "Benutzer" aufzurufen. Klicken Sie auf das "+"-Symbol (Plus) in der oberen rechten Ecke, um einen neuen Benutzer anzulegen. Achten Sie darauf, dass Sie nicht auf die Schaltfläche "Einladen" direkt daneben klicken.
Füllen Sie das Benutzerformular aus, und geben Sie dem Benutzer unten unter "Admin-Optionen" die Rolle "Anwendungsbenutzer", und klicken Sie dann auf "+" im Feld "Token". Bewahren Sie dieses Token auf, da wir es für die Konfiguration unserer Anwendung benötigen.
Klicken Sie auf die Schaltfläche mit dem Häkchen in der oberen rechten Ecke, um den Benutzer zu speichern.
Start ngrok
Wir brauchen eine Möglichkeit, die Vonage-Server mit unserer Anwendung in Kontakt zu bringen, und der einfachste Weg, dies zu tun, ist die Verwendung eines Tunneling-Systems wie ngrok. Gehen Sie rüber zu ngrok, melden Sie sich für ein Konto an und folgen Sie der Schnellstart-Anleitung um es auf Ihrem PC zu installieren.
Sobald es installiert ist, können wir es mit starten:
ngrok http 3000Dadurch wird ein Tunnel zur Außenwelt über die ngrok-Server geöffnet, einschließlich einer URL, die SSL aktiviert hat. Wenn ngrok läuft, erhalten Sie eine nette kleine Statusausgabe, mit der Sie die Ereignisse, die durch das System fließen, überprüfen können. Wir müssen die "Forwarding"-Adresse auf der linken Seite des ->die unsere öffentliche URL ist. Wir werden diese URL gleich benötigen.
Vonage einrichten
Wir verwenden die Vonage Sprach-API verwenden, um einem Benutzer zu ermöglichen, unser "Movie Phone System" anzurufen. Vonage-Anwendung. Vonage-Anwendungen sind im Wesentlichen Gruppen von Konfigurationen, die mit Anwendungen interagieren. Bei den meisten unserer Dienste handelt es sich um "Callbacks" oder URLs, die wir verwenden, um Informationen an Ihre Anwendung zu senden. Bei Voice werden diese Rückrufe z. B. dazu verwendet, Sie über ein Anrufereignis zu informieren oder eine URL für den Start eines Anrufs zu verwenden.
Melden Sie sich bei Ihrem Entwickler-Dashboardund gehen Sie dann zu "Anwendungen"Wir werden eine neue Anwendung erstellen, also klicken Sie auf "+ Eine neue Anwendung erstellen."
Geben wir unserer Anwendung einen Namen wie "Directus IVR", damit wir sie später wiederfinden können. Da wir eine Vonage-Anwendung verwenden, müssen wir einen privaten Schlüssel erzeugen, um unser JWT zu signieren. Weitere Informationen darüber, wie die Vonage JWT-Authentifizierung funktioniert, finden Sie in unserer Dokumentation zur Authentifizierung. Speichern Sie diese private Schlüsseldatei, da wir sie hier in Kürze benötigen werden.
Scrollen Sie ein wenig nach unten und klicken Sie auf die Schaltfläche neben "Voice", um die Sprachkonfiguration zu öffnen. Verwenden Sie die ngrok-URL, die wir soeben kopiert haben,
Antwort-URL - Auf "HTTP POST" setzen und den Wert auf
<ngrok URL>/Ereignis-URL - Auf "HTTP POST" setzen und den Wert auf
<ngrok URL>/eventsFallback-URL - Lassen Sie die Option "HTTP GET" und den Wert leer.
Blättern Sie dann weiter nach unten und klicken Sie auf "Neue Bewerbung erstellen".
Sobald die Anwendung erstellt ist, werden Sie auf die Informationsseite für die neue Anwendung weitergeleitet. Oben befindet sich eine Anwendungs-ID, die wir für die Konfiguration unseres Codes benötigen, also kopieren Sie sie nach unten. Außerdem benötigen wir eine Telefonnummer. Verknüpfen Sie daher unter dem Abschnitt "Verknüpfte Nummern" eine Ihrer Nummern, indem Sie auf die Schaltfläche "Verknüpft" für eine Nummer klicken, die Sie besitzen. Wenn Sie noch keine Nummer haben, gehen Sie auf die Seite "Nummern kaufen"Beachten Sie, dass es in einigen Ländern Beschränkungen für den Kauf von Nummern geben kann.
Unser Kodex
Wenn Sie mitmachen möchten, können Sie den Code abrufen.
Konfigurieren Sie unsere Anwendung
Wir müssen einige Informationen, die Sie betreffen, in unsere Anwendung aufnehmen. Kopieren Sie die .env.dist Datei in eine neue Datei mit dem Namen .envund öffnen Sie sie, um sie zu bearbeiten. Es gibt vier Variablen, die wir in dieser Datei zuweisen müssen, die unsere Anwendung benötigt:
DIRECTUS_INSTANZ - Dies ist die URL für Ihre Directus-Instanz. Sie wird etwa so aussehen: "https://my-instance.directus.app/".
DIRECTUS_TOKEN - Das statische Token, das wir zuvor für unseren Directus-Benutzer erstellt haben
API_ANWENDUNG_ID - Die von uns generierte Vonage-Anwendungs-ID
PRIVAT_SCHLÜSSEL - Eine base64-kodierte Version des privaten Schlüssels, den wir für unsere Anwendung heruntergeladen haben. Lesen Sie unseren Artikel über private Schlüssel in Umgebungsvariablen um zu erfahren, wie man die
private.keyDatei in eine base64-Zeichenkette umwandelt
Speichern Sie diese Datei.
Der Directus-Kunde
Directus bietet ein JavaScript SDK an, das wir für den Zugriff auf unsere Directus-Datenbankinstanz verwenden werden. Ausführliche Informationen finden Sie in der der Directus JavaScript SDK-Dokumentationaber wir werden eine Instanz des Clients erstellen. Da wir einen Systembenutzer für den Zugriff auf unsere Directus-Datenbank verwenden werden, können wir das Problem der Authentifizierung pro Benutzer umgehen, so dass wir einen wiederverwendbaren Directus-Client für die gesamte Anwendung erstellen.
# src/Directus.js
export function getDirectusClient() {
return createDirectus(process.env.DIRECTUS_INSTANCE)
.with(staticToken(process.env.DIRECTUS_TOKEN))
.with(rest());
}Das Directus SDK ist komponierbar, d.h. Sie beginnen mit einem Client, der für Ihre Zwecke konfiguriert werden muss. In unserem Fall werden wir einen staticToken() für die Authentifizierung und verwenden die rest() Methode, um den Zugriff über die REST-API statt über die GraphQL-API zu konfigurieren.
In unserer gesamten Anwendung werden wir diese getDirectusClient() Methode aufrufen, um diesen zusammengesetzten Client in unserem Code zu verwenden.
Da wir die REST-API verwenden, wird unser Client eine request() Methode, die es uns ermöglicht, mit ihrer API zu interagieren. Dies geschieht durch eine Kombination von Methoden, die wir an die request() Methode übergeben, wie readItems() oder deleteItem().
const directus = getDirectusClient();
const tickets = await directus.request(readItems(
// Collection we are accessing
'tickets',
// Options for what we return and search for
{
'fields': [
'id',
'showtime_id.*.*'
],
'sort': [
'showtime_id.start_time'
],
'filter': {
'user': {
'_eq': user.id
}
}
}
)); Wie die Anwendung funktioniert
Wenn der Benutzer die unserer Anwendung zugewiesene Vonage-Nummer wählt, greifen die Vonage-Server auf die Antwort-URL für unsere Anwendung zu und tätigen einen Anruf an unseren Server. Für unsere Anwendung ist dies die src/routes/CallStart.js Datei. Die Antwort-URL liefert ein NCCO oder ein Call Control Object. Ein NCCO ist einfach ein JSON-Blob mit Anweisungen, die die Vonage-Server befolgen sollen. Wenn wir einen ersten Anruf starten, geben wir ein NCCO zurück, das eine Talk Aktion und eine Input Aktion hat. Wir fragen den Benutzer, was er tun möchte, und warten dann darauf, dass er es uns mitteilt.
Außerdem prüfen wir über die Directus-API, ob sie bereits ein Ticket haben. Ist dies der Fall, erinnern wir sie an den kommenden Film.
// src/routes/CallStart.js
router.post('/', async (req, res) => {
const builder = new NCCOBuilder();
const directus = getDirectusClient();
const user = await getUserFromPhone(req.body.from);
const tickets = await directus.request(readItems(
'tickets',
{
'fields': [
'showtime_id.*.*'
],
'sort': [
'showtime_id.start_time'
],
'filter': {
'user': {
'_eq': user.id
}
}
}
));
let openingMessage = "Welcome! What can we help you with?";
if (tickets.length > 0) {
const startTime = new Date(tickets[0].showtime_id.start_time);
const timeString = `${startTime.getFullYear()} ${startTime.getMonth() + 1} ${startTime.getDay()} at ${startTime.getHours()} ${startTime.getMinutes()}`;
openingMessage = `Welcome back! We look forward to seeing you on ${timeString} for ${tickets[0].showtime_id.movie_id.title}. What can we help you with?`;
}
const domain = getDomain(req);
return res.send(
builder
.addAction(new Talk(openingMessage))
.addAction(new Input(
null,
{
'context': ['cancel ticket']
},
`${domain}/determine_action`))
.build()
);
});Zusätzlich zum Aufruf der Directus-API verwenden wir die NCCOBuilder die mit dem Vonage Node SDK ausgeliefert wird, um uns bei der Erstellung des JSON zu helfen, das wir für das NCCO benötigen. Wir fügen eine Talk Aktion, um die Begrüßungsnachricht zu übermitteln, und dann eine Input Aktion, die auf die Stimme des Benutzers hört und sie in eine Zeichenkette umwandelt. Wir könnten auch DTMF oder die Eingabe über die Wähltastatur verwenden, aber im Moment werden wir Voice-to-Text verwenden.
Wenn der Benutzer uns sagt, was er tun möchte (und da es sich um eine Demo handelt, unterstützen wir nur das Stornieren eines Tickets), nehmen die Vonage-Server diese Spracheingabe und wandeln sie in eine JSON-Anfrage um, die sie an die URL sendet, die durch unsere Input Aktion bestimmt wird, die lautet <ngrok URL>/determine_action. Die eingehende Anfrage wird etwa so aussehen:
{
"speech": {
"timeout_reason": "end_on_silence_timeout",
"results": [
{
"confidence": "0.7276162",
"text": "cancel ticket"
}
]
},
"dtmf": {
"digits": null,
"timed_out": false
},
"from": "15556661234",
"to": "18005556666",
"uuid": "ee9d7f5daa81673b5461c6f5XXXXXXXX",
"conversation_uuid": "CON-baa6dcee-6750-4cde-82d2-XXXXXXXXXXXX",
"timestamp": "2023-12-22T03:47:55.378Z"
}Dieser JSON-Blob wird an unseren Server gesendet und von src/routes/DetermineAction.js.
// src/routes/DetermineAction.js
router.post('/determine_action', async (req, res) => {
const builder = new NCCOBuilder();
if (req.body.speech?.results[0].text === 'cancel ticket') {
const directus = getDirectusClient();
const user = await getUserFromPhone(req.body.from);
const tickets = await directus.request(readItems(
'tickets',
{
'fields': [
'id',
'showtime_id.*.*'
],
'sort': [
'showtime_id.start_time'
],
'filter': {
'user': {
'_eq': user.id
}
}
}
));
if (tickets.length === 0) {
builder
.addAction(new Talk('We do not see any tickets for you at the moment. Would you like to hear a list of available movies to purchase a ticket?'))
.addAction(new Input(
null,
{
'context': ['yes', 'no']
},
`${getDomain(req)}/list_movies`)
)
} else if (tickets.length === 1) {
builder
.addAction(new Talk(`Would you like to cancel your ticket for ${tickets[0].showtime_id.movie_id.title}?`))
.addAction(new Input(
null,
{
'context': ['yes', 'no']
},
`${getDomain(req)}/confirm_cancel?id=${tickets[0].id}`)
)
} else {
// Ask which upcoming tickets to cancel
}
} else if (req.body.speech.results[0].text === 'buy ticket') {
builder
.addAction(new Talk('Would you like to hear a list of available movies to purchase a ticket?'))
.addAction(new Input(
null,
{
'context': ['yes', 'no']
},
`${getDomain(req)}/list_movies`)
)
} else {
builder.addAction(new Talk('Sorry, I do not understand what you would like to do.'));
builder.addAction(new Notify(
{
action: 'restart',
from: req.body.from,
},
`${getDomain(req)}/notify`
));
}
return res.send(builder.build());
});Dieser Weg ist etwas komplizierter, da wir versuchen, herauszufinden, was der Benutzer getan hat. Der Demo-Code unterstützt die Stornierung eines Tickets sowie den Kauf eines neuen Tickets.
Wenn ein Ticket storniert wird, werden die aktuellen Tickets angezeigt, die der Nutzer gekauft hat. Wenn er noch keine gekauft hat, fragen wir ihn, ob er stattdessen eines kaufen möchte. Wenn ja, leiten wir sie zum Kauf-Workflow weiter.
Wenn sie nur ein Ticket haben, fragen wir sie, ob sie das Ticket stornieren möchten. Dies ist der Arbeitsablauf, den wir in diesem Beitrag beschreiben, also senden wir ein NCCO mit einem weiteren Talk und Input Wenn der Nutzer die Stornierung bestätigt, rufen wir die Directus-API erneut auf, um das Ticket zu löschen.
Wir übergeben auch die Film-ID als Abfrageparameter in der URL. HTTP selbst ist zustandslos, und normalerweise würden wir eine Sitzung verwenden, um die Abläufe zu verfolgen. Da die Sprach-API nicht mit Sitzungen arbeitet, behalten wir den Status auf die alte Weise bei, indem wir Abfrageparameter weitergeben. Wir übergeben die Ticket-ID über einen Abfrageparameter an die Bestätigungsroute.
Wenn der Benutzer mehr als ein Ticket hätte, würden wir ihn bitten, das Ticket auszuwählen, das er stornieren möchte, aber im Moment lassen wir diese Logik weg, da sie die Demo komplizierter macht.
// src/routes/ConfirmCancel.js
router.post('/confirm_cancel', async (req, res) => {
const builder = new NCCOBuilder();
if (req.body.speech.results[0].text === 'yes') {
const directus = getDirectusClient();
directus.request(deleteItem('tickets', req.query.id));
builder
.addAction(new Talk('We have removed your ticket'))
.build();
}
builder.addAction(new Notify(
{
action: 'restart',
from: req.body.from,
},
`${getDomain(req)}/notify`
));
return res.send(builder.build());Wenn wir die Stornierungsroute wählen, nehmen wir die Ticket-ID aus den Abfrageparametern und senden eine deleteItem() Anfrage zurück an Directus. Dadurch wird das Ticket aus der Directus-Datenbank entfernt, und der Kunde erfährt, dass wir sein Ticket storniert haben.
Wir könnten den Anruf an dieser Stelle beenden, aber um dem Benutzer zu helfen, leiten wir ihn an den Anfang des Arbeitsablaufs zurück. Dies geschieht durch Senden einer Notify Aktion als NCCO, mit der wir einen NCCO in den Ablauf einfügen können.
// src/routes/Notify
router.all('/notify', async (req, res) => {
const builder = new NCCOBuilder();
const domain = getDomain(req);
return res.send(
builder
.addAction(new Talk('What else can we help you with?'))
.addAction(new Input(
null,
{
'context': ['cancel ticket', 'buy ticket']
},
`${domain}/determine_action`))
.build()
);
});Dieses Notify NCCO fragt den Benutzer nur, ob wir ihm noch mit irgendetwas anderem helfen können, und leitet die Antwort an die Route weiter, um die gewünschte Aktion zu bestimmen, genau wie wenn der Benutzer einfach angerufen hätte. Der Benutzer kann jederzeit auflegen, um den Anruf zu beenden.
Schlussfolgerung
Wenn Sie eine Anwendung erstellen, sollten Sie sich auf Ihre Stärken konzentrieren. Kümmern Sie sich um die Entwicklung der Logik Ihrer Anwendung und überlassen Sie anderen Diensten die Handhabung der anderen Teile Ihrer Anwendung, die nicht zu Ihren Stärken gehören. Directus ist eine großartige Möglichkeit, Daten zu verarbeiten, und die Voice-APIs von Vonage machen den Umgang mit der Telefonie überflüssig. Beteiligen Sie sich an der Diskussion in unserer Vonage Entwickler-Community Slack oder senden Sie uns eine Nachricht auf X, früher bekannt als Twitter.
Teilen Sie:
Chris ist Developer Relations Tooling Manager und leitet das Team, das Ihre Lieblingstools entwickelt. Er programmiert seit mehr als 15 Jahren in verschiedenen Sprachen und für verschiedene Projekttypen, von der Kundenarbeit bis hin zu Big-Data-Großsystemen. Er lebt in Ohio, verbringt seine Zeit mit seiner Familie und spielt Video- und TTRPG-Spiele.