Startseite / Artikel

SMS versenden und empfangen in Node.js mit Twilio Webhooks und Express

Erstellen Sie einen kleinen Express-Server, der Texte über Twilio sendet, Antworten über ein über ngrok freigegebenes webhook erhält und diese automatisch mit TwiML beantwortet.

Verlag: REACTAPP.TOP6 Min. Lesezeit1483 Wörter

Quellen: **Build a Two-Way SMS with Node.js and Twilio (Full Code Included)**

Die meisten SMS-Lehrbücher enden bereits, sobald eine Nachricht Ihren Server verlässt. Der nützlichere Teil befasst sich mit der Einlaufkommunikation: dem Erkennen, dass ein Benutzer geantwortet hat, dem Lesen der Nachricht und der Reaktion darauf. Dieser Kreislauf macht OTP-Verfahren, Support-Bots sowie Terminerinnerungen möglich, die mit einem einzigen Wort bestätigt werden können. Am Ende dieses Leitfadens werden Sie einen Express-Server haben, der eine SMS an ein Telefon sendet und jedes Mal reagiert, wenn das Telefon zurückschreibt.

Voraussetzungen

  • Ein Twilio-Konto. Zum Zeitpunkt der Erstellung enthält die kostenlose Testversion etwa 15 US-Dollar an Kredit, was ausreicht, um zu experimentieren.
  • Postman oder jeder andere HTTP-Client.
  • Node.js, lokal installiert.
  • ngrok, das Twilio eine öffentliche Adresse gibt, die auf Ihren Laptop umgeleitet wird.

Ein minimaler Express-Server

Erstellen Sie ein Verzeichnis mit einer Datei namens server.js. Der Server bindet sich an localhost auf Port 3000, aktiviert die Verarbeitung von JSON sowie URL-kodierten Anfragen und stellt einen Health-Check-Endpunkt unter /api/v1/ zur Verfügung. Der URL-kodierte Parser ist wichtig: Twilio liefert eingehende Nachrichten als form-kodierte POST-Anfragen, weshalb req.body sonst später leer wäre.

const express = require("express");
const bodyParser = require("body-parser");
const hostname = "127.0.0.1";
const port = 3000;
const app = express();

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

const baseUrl = "/api/v1/";

app.get(`${baseUrl}`, (req, res) => {
  res.status(200).json({ message: "Hello World" });
});
app.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});

Installieren Sie die Abhängigkeiten:

npm init -y
npm install express body-parser

Führen Sie node server.js aus und öffnen Sie http://127.0.0.1:3000/api/v1/; eine „Hello World“-Antwort bestätigt, dass alles funktioniert. Express 4.16+ enthält bereits express.json() und express.urlencoded(), wodurch body-parser in neuen Projekten optional ist.

Veröffentlichung von localhost mit ngrok

Twilio befindet sich im öffentlichen Internet, während Ihr Server auf einem privaten Rechner läuft, weshalb Twilio nicht direkt darauf zugreifen kann. ngrok eröffnet einen Tunnel und stellt Ihnen eine öffentliche HTTPS-URL zur Verfügung, die auf einen lokalen Port weiterleitet. Heroku war früher die kostenlose Option dafür, doch seine kostenlose Stufe existiert nicht mehr.

Installieren und authentifizieren Sie ngrok mithilfe der ngrok-Dokumentation und fügen Sie anschließend ein Skript zu package.json hinzu:

"scripts": {
  "ngrok": "ngrok http http://127.0.0.1:3000/"
}

Lassen Sie den Server in einem Terminal laufen und starten Sie den Tunnel in einem zweiten Terminal:

npm run ngrok

ngrok gibt eine Adresse wie https://abcd-1234.ngrok-free.app aus. Lassen Sie dieses Terminal geöffnet.

Achtung: Im kostenlosen Plan ändert sich die generierte URL in der Regel jedes Mal, wenn ngrok neu gestartet wird. Wenn ein funktionierender webhook plötzlich nicht mehr reagiert, liegt die Ursache meist an einer veralteten URL in der Twilio-Konsole – aktualisieren Sie diese daher nach jedem Neustart.

Twilio konfigurieren

In der Konsole kopieren Sie Ihren Account SID sowie den Auth Token in eine .env-Datei. Der Token ist ein Geheimnis: Übergeben Sie ihn niemals und fügen Sie .env in .gitignore ein.

ACCOUNT_SID_TWILIO=your_account_sid
AUTH_TOKEN_TWILIO=your_auth_token

Produkte können in der Seitennavigation markiert werden; hier sind nur Messaging und Phone Numbers relevant.

Einen Telefonnummer erhalten

  1. Gehen Sie zu Phone Numbers und anschließend zu Buy a number.
  2. Wählen Sie eine verfügbare Nummer aus; US-Nummern sind in der Regel am günstigsten.
  3. Die Kosten werden vom Probekonto abgezogen und die Nummer erscheint unter Ihren aktiven Nummern.

Einen Messaging Service erstellen

  1. Gehen Sie zu Messaging und erstellen Sie einen neuen Messaging Service.
  2. Fügen Sie in Schritt Sender Pool Ihre Nummer hinzu.
  • In dem Schritt Integration wählen Sie Send a webhook aus und setzen Sie sowohl Request URL als auch Fallback URL auf Ihre Ngrok-Adresse mit dem angehängten Pfad /api/v1/sms. Dieser Pfad wird unten erstellt.
  • Überspringen Sie den optionalen Schritt und beenden Sie die Einrichtung.
  • Speichern Sie den Messaging Service SID sowie Ihre Nummer in .env:

    FROM_TWILIO=+1XXXXXXXXXX
    MESSAGING_SERVICE_SID_TWILIO=your_messaging_service_sid
    

    Verbindung des Twilio SDK mit Node

    Installieren Sie das SDK zusammen mit dotenv, welches .env in process.env lädt:

    npm install twilio dotenv
    

    Fügen Sie dies ganz oben in server.js ein. Die Reihenfolge ist wichtig: Der Client liest die Zugangsdaten beim Erstellen, daher sind beide Werte, wenn dotenv später geladen wird, undefined, und jede Anfrage scheitert an der Authentifizierung.

    require("dotenv").config();
    
    const client = require("twilio")(
      process.env.ACCOUNT_SID_TWILIO,
      process.env.AUTH_TOKEN_TWILIO
    );
    

    Senden einer eingehenden SMS

    Diese Route ruft client.messages.create() auf und gibt das Ergebnis als JSON zurück. Der try/catch-Block leitet Fehler von Twilio, wie beispielsweise eine fehlerhafte Nummer, an den Aufrufer weiter, anstatt den Prozess abzubrechen.

    app.post(`${baseUrl}sendSms`, async (req, res) => {
      try {
        await client.messages.create({
          body: "Hello",
          from: process.env.FROM_TWILIO,
          messagingServiceSid: process.env.MESSAGING_SERVICE_SID_TWILIO,
          to: "+92XXXXXXXXXX", // whoever you're texting
        });
        res.status(200).json({ message: "Text sent successfully" });
      } catch (err) {
        res.status(500).json({ error: err.message });
      }
    });
    

    Was die Felder bedeuten:

    • body: Der Text der Nachricht.
    • from: Ihre Twilio-Nummer.
    • messagingServiceSid: Verbindet die Nachricht mit dem Service sowie dessen Senderpool.
    • to: Der Empfänger im E.164-Format (+1..., +92...).

    Rufen Sie POST /api/v1/sendSms über Postman auf – das Telefon sollte dann „Hello“ erhalten. Im echten Code sollten to und body aus der Anfrage gelesen und überprüft werden, anstatt sie hardzugeben.

    Probekonten können nur an Nummern senden, die im Twilio-Konsole verifiziert wurden. Wenn der Anfragen erfolgreich ist, aber nichts ankommt, überprüfen Sie den Empfänger oder erweitern Sie Ihr Abonnement.

    Antworten über den webhook erhalten

    Wenn jemand eine Nachricht an Ihre Twilio-Nummer sendet, übermittelt Twilio die Nachricht an den im Messaging Service konfigurierten webhook. Ihr Server nimmt sie entgegen und antwortet mit TwiML, Twilios XML-Format zur Beschreibung der nächsten Aktion. Importieren Sie den Hilfscode zusammen mit Ihren anderen Anforderungen:

    const MessagingResponse = require("twilio").twiml.MessagingResponse;
    

    Fügen Sie anschließend den Zuhörer hinzu. Der klassische Fehler ist eine fehlerhafte Pfadangabe: Der Pfad muss dem im Konsole eingegebenen Pfad entsprechen, hier /api/v1/sms. Der Handler protokolliert req.body.Body und gibt ein leeres TwiML-Dokument im Format text/xml zurück, was bedeutet, dass es keine Antwort gibt.

    app.post(`${baseUrl}sms`, (req, res) => {
      // Fires every time someone replies to your number
      const twiml = new MessagingResponse();
    console.log("INCOMING ----->", req.body.Body);
      res.writeHead(200, { "Content-Type": "text/xml" });
      res.end(twiml.toString());
    });
    

    Geben Sie Ihre Twilio-Nummer ein, und die Nachricht erscheint im Terminal; der Schleifenlauf wird beendet. Die Nutzlast enthält außerdem den Wert From, die Nummer des Absenders, die Sie benötigen, um Gespräche pro Benutzer nachzuvollziehen.

    Weil das Endpunkt öffentlich ist, kann jeder, der die URL findet, falsche Nachrichten senden. Twilio signiert jeden webhook mit Ihrem Auth Token im Header X-Twilio-Signature, und das Node SDK bietet Validierungs-Hilfsmittel. Schalten Sie diese ein, bevor Sie über lokale Tests hinaus deployen.

    Automatische Antwort

    Der Server lauscht, bleibt aber stumm. Durch Hinzufügen einer Nachricht zur TwiML-Antwort wird daraus ein Schlüsselworts-Bot. Fügen Sie dies innerhalb des /sms-Handlers nach der Erstellung von twiml ein; dort wird der Text gekürzt und in Kleinbuchstaben umgewandelt, bevor eine Antwort ausgewählt wird:

    const incoming = req.body.Body.trim().toLowerCase();
    if (incoming === "hi") {
      twiml.message("Hey there! How can I help?");
    } else {
      twiml.message("Hmm, I didn't catch that.");
    }
    

    Twilio sendet die zurückgegebene Antwort an den Absender, daher ist kein zusätzlicher Aufruf von messages.create() notwendig.

    Der vollständige Server

    Alles in einer Datei, wobei die Automatenantwort ausgekommentiert ist:

    require("dotenv").config();
    
    const express = require("express");
    const bodyParser = require("body-parser");
    const MessagingResponse = require("twilio").twiml.MessagingResponse;
    const client = require("twilio")(
      process.env.ACCOUNT_SID_TWILIO,
      process.env.AUTH_TOKEN_TWILIO
    );
    const hostname = "127.0.0.1";
    const port = 3000;
    const app = express();
    app.use(bodyParser.json());
    app.use(bodyParser.urlencoded({ extended: true }));
    
    const baseUrl = "/api/v1/";
    // Health check
    app.get(`${baseUrl}`, (req, res) => {
      res.status(200).json({ message: "Hello World" });
    });
    
    // Goal 1: Send an SMS
    app.post(`${baseUrl}sendSms`, async (req, res) => {
      try {
        await client.messages.create({
          body: "Hello",
          from: process.env.FROM_TWILIO,
          messagingServiceSid: process.env.MESSAGING_SERVICE_SID_TWILIO,
          to: "+92XXXXXXXXXX", // whoever you're texting
        });
        res.status(200).json({ message: "Text sent successfully" });
      } catch (err) {
        res.status(500).json({ error: err.message });
      }
    });
    
    // Goal 2: Receive replies (this endpoint must match your Twilio webhook)
    app.post(`${baseUrl}sms`, (req, res) => {
      const twiml = new MessagingResponse();
      console.log("INCOMING ----->", req.body.Body);
      // Optional auto-reply:
      // const incoming = req.body.Body.trim().toLowerCase();
      // if (incoming === "hi") {
      //   twiml.message("Hey there! How can I help?");
      // } else {
      //   twiml.message("Hmm, I didn't catch that.");
      // }
      res.writeHead(200, { "Content-Type": "text/xml" });
      res.end(twiml.toString());
    });
    
    app.listen(port, hostname, () => {
      console.log(`Server running at http://${hostname}:${port}/`);
    });
    

    Wichtige Erkenntnisse

    • Ausgehende SMS erfordern einen einzigen SDK-Aufruf; eingehende SMS erfolgen über einen Webhook, weshalb der Server erreichbar sein muss und TwiML verstehen kann.
    • Twilio sendet form-kodierte Daten, weshalb ein URL-kodierter Parser verwendet wird.
    • Der Webhook-Pfad in der Konsole muss mit dem Express-Route übereinstimmen, und ein Neustart des kostenlosen Tunnels führt zu einer neuen URL.
    • Stille Testfehler sind oft auf die Einschränkung bezüglich verifizierter Nummern zurückzuführen und nicht auf einen Fehler.
    • Prüfen Sie Signaturen und entfernen Sie hardcodierte Werte vor der tatsächlichen Verwendung.

    Von hier aus können Sie Gespräche speichern, den Schlüsselwort-Bot erweitern oder einen OTP-Login hinzufügen. Für umfassendere Informationen zu Callbacks und Wiederholungsversuchen siehe sechs Integrationsmuster zur zuverlässigen Verbindung von Node.js-Diensten.