Comment envoyer un SMS depuis une application React avec Twilio ?

October 24, 2018
Rédigé par
Phil Nash
Twilion

Comment envoyer un SMS depuis une application React avec Twilio ?

Nous parlons souvent de l'envoi de messages SMS à partir d'applications Web, mais qu'en est-il de l'envoi de SMS depuis une application React ? Ce processus implique autre chose qu'une simple version côté serveur, mais cela ne nous prendra pas longtemps.

Pourquoi pas utiliser l'API REST côté client ?

Techniquement, vous pouvez envoyer un SMS en utilisant l'API REST Twilio directement à partir d'une application côté client JavaScript. Mais si vous procédez ainsi, vous exposez vos identifiants Twilio à quiconque utilise votre site, et ce n'est pas qu'un « petit détail ». Un utilisateur malveillant pourrait alors récupérer ces identifiants et en abuser, en générant une facture énorme avec votre compte.

Une animation montrant un faux hacker, avec une cagoule et des mains en plus.

Vue en direct d'un hacker avec vos identifiants de compte

Pour éviter cela, nous allons créer ensemble une application back-end qui met en œuvre l'API REST Twilio, regroupe vos identifiants et envoie des SMS pour vous. Vous pouvez ensuite appeler le back-end à partir de votre application React et envoyer des SMS sans exposer vos identifiants sur Internet.

Nos outils

Pour que notre application envoie des messages texte à l'aide de l'API REST Twilio, nous avons besoin des éléments suivants :

  • Un compte Twilio et un numéro de téléphone qui peut envoyer des SMS (inscrivez-vous ici pour créer un compte Twilio gratuitement)
  • Node.js pour construire notre application React et exécuter notre serveur (vous pouvez construire ce composant côté serveur dans n'importe quel langage, mais dans ce post, nous allons utiliser Node afin que tout soit en JavaScript)
  • Outils de développement React pour votre navigateur (facultatif, mais vraiment utile pour voir ce qui se passe dans l'application)

Pour commencer, téléchargez ou clonez l'application react-express-starter que j'ai construite dans mon dernier article.

git clone https://github.com/philnash/react-express-starter.git

Accédez au répertoire et installez les dépendances.

cd react-express-starter
npm install

Dans le répertoire du projet, créez un fichier nommé .env :

touch .env

Vous pouvez maintenant tester si le projet fonctionne en exécutant npm run dev. L'application se charge dans votre navigateur à l'adresse localhost:3000.

Cette application de démarrage est configurée pour avoir à la fois une application React et une application Express dans le même projet, que vous pouvez exécuter simultanément. Pour savoir comment cela fonctionne, consultez ce blog post.

Construction côté serveur

Comme mentionné précédemment, nous devons effectuer les appels d'API Twilio à partir du serveur. Nous allons ajouter un point de terminaison au serveur Express qui peut être appelé à partir de notre application React. Commencez par installer le module Twilio Node.js. Remarque : dans le cadre de cette application, j'enregistre les dépendances du serveur en tant que dépendances de développement afin de les séparer des dépendances côté client.

npm install twilio --save-dev

Ensuite, nous devons configurer l'application avec nos identifiants Twilio. Récupérez votre SID de compte Twilio et votre token d'authentification à partir de la console Twilio, ainsi qu'un numéro de téléphone Twilio pouvant envoyer des SMS. Saisissez ces trois informations dans le fichier .env que vous venez de créer, comme indiqué ci-dessous :

TWILIO_ACCOUNT_SID=YOUR_ACCOUNT_SID
TWILIO_AUTH_TOKEN=YOUR_AUTH_TOKEN
TWILIO_PHONE_NUMBER=YOUR_TWILIO_PHONE_NUMBER

De cette façon, vous définissez vos identifiants dans l'environnement. Maintenant, ouvrez le fichier server/index.js pour commencer à créer le code nécessaire à l'envoi du message. Sous les autres composants « require » du module en haut du fichier, requérez et initialisez la bibliothèque Twilio avec les identifiants de l'environnement.

const express = require('express');
const bodyParser = require('body-parser');
const pino = require('express-pino-logger')();
const client = require('twilio')(
  process.env.TWILIO_ACCOUNT_SID,
  process.env.TWILIO_AUTH_TOKEN
);

Nous allons envoyer les données au point de terminaison que nous construisons au format JSON. Nous devons être en mesure d'analyser le corps JSON. Configurez l'application Express avec le parser JSON du parser de corps :

const app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(pino);

Créez une route pour une requête POST. Ajoutez ce qui suit sous la route pour /api/greeting :

app.post('/api/messages', (req, res) => {

});

Vous allez également répondre avec JSON, donc définissez l'en-tête Content-Type sur application/json.

app.post('/api/messages', (req, res) => {
  res.header('Content-Type', 'application/json');

});

Vous allez ensuite utiliser le client Twilio que nous avons initialisé précédemment pour créer un message. Nous allons utiliser notre numéro Twilio comme numéro from, puis obtenir le numéro to et l'élément body du message à partir du corps de la requête entrante. Cela renvoie une promesse qui sera honorée si la requête d'API aboutit, ou rejetée en cas d'échec. Dans les deux cas, nous renverrons une réponse JSON pour indiquer au côté client si la requête a abouti ou non.

app.post('/api/messages', (req, res) => {
  res.header('Content-Type', 'application/json');
  client.messages
    .create({
      from: process.env.TWILIO_PHONE_NUMBER,
      to: req.body.to,
      body: req.body.body
    })
    .then(() => {
      res.send(JSON.stringify({ success: true }));
    })
    .catch(err => {
      console.log(err);
      res.send(JSON.stringify({ success: false }));
    });
});

C'est tout ce dont nous avons besoin sur le serveur. Passons à la partie React.

Construction côté client

Côté client, nous pouvons encapsuler le formulaire pour envoyer un SMS via le serveur dans un seul composant. Dans le répertoire src, créez un nouveau composant nommé SMSForm.js et commencez par le code standard pour un composant :

import React, { Component } from 'react';

class SMSForm extends Component {

}

export default SMSForm;

Nous allons créer un formulaire que l'utilisateur peut remplir avec un numéro de téléphone et un message. Lorsque le formulaire est soumis, il envoie les détails au point de terminaison du serveur et envoie le message sous forme de SMS au numéro.

Construisons d'abord la méthode render pour ce composant : elle inclut un formulaire, une entrée pour le numéro de téléphone, une zone de texte pour le message et un bouton pour le soumettre :

  render() {
    return (
      <form>
        <div>
          <label htmlFor="to">To:</label>
          <input
             type="tel"
             name="to"
             id="to"
          />
        </div>
        <div>
          <label htmlFor="body">Body:</label>
          <textarea name="body" id="body"/>
        </div>
        <button type="submit">
          Send message
        </button>
      </form>
    );
  }

Vous pouvez ajouter du code CSS pour une mise en page sommaire de ce formulaire. Créez le fichier src/SMSForm.css et ajoutez ce qui suit :

.sms-form {
  text-align: left;
  padding: 1em;
}
.sms-form label {
  display: block;
}
.sms-form input,
.sms-form textarea {
  font-size: 1em;
  width: 100%;
  box-sizing: border-box;
}
.sms-form div {
  margin-bottom: 0.5em;
}
.sms-form button {
  font-size: 1em;
  width: 100%;
}
.sms-form.error {
  outline: 2px solid #f00;
}

Importez le CSS en haut du composant SMSForm :

import React, { Component } from 'react';
import './SMSForm.css';

Importez maintenant le composant dans src/App.js et remplacez la méthode « render » par ce qui suit :

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import SMSForm from './SMSForm';

class App extends Component {
  render() {
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />

          <SMSForm />
        </header>
      </div>
    );
  }
}

export default App;

Démarrez votre application avec npm run dev : le formulaire apparaîtra sur la page.

formulaire

Le formulaire ne fait rien pour l'instant, alors il faut y remédier.

Création d'un formulaire interactif dans React

Pour connecter le formulaire HTML au composant, nous devons réaliser les opérations suivantes :

  • Maintenir l'état de l'entrée et de la zone de texte à jour dans l'état du composant
  • Gérer la soumission du formulaire et l'envoi des données au serveur
  • Gérer la réponse du serveur et effacer le formulaire si le message a été envoyé avec succès, ou afficher une erreur si ce n'est pas le cas

Nous allons commencer par configurer un état initial dans le constructeur. Nous avons besoin d'enregistrer les entrées du formulaire, si le formulaire est en cours de soumission (pour désactiver le bouton de soumission) et si une erreur s'est produite. Créez le constructeur comme suit pour le composant :

class SMSForm extends Component {
  constructor(props) {
    super(props);
    this.state = {
      message: {
        to: '',
        body: ''
      },
      submitting: false,
      error: false
    };
  }

  // rest of the component
}

Nous avons besoin d'une méthode capable de gérer les modifications dans les champs du formulaire et de mettre à jour l'état. Nous pourrions créer deux méthodes, l'une pour l'entrée et l'autre pour la zone de texte, mais puisque les noms des éléments de formulaire et des éléments dans l'état coïncident, nous pouvons construire une méthode pour couvrir ces deux aspects.

  onHandleChange(event) {
    const name = event.target.getAttribute('name');
    this.setState({
      message: { ...this.state.message, [name]: event.target.value }
    });
  }

Notez ici que nous utilisons les noms de propriétés calculés ES2015  pour définir la propriété appropriée dans l'état et l'opérateur de décomposition pour remplir le reste de l'état.

Nous devons lier cette méthode à l'objet pour nous assurer que l'élément this est correct lorsque nous l'utilisons pour recevoir un événement. Ajoutez ce qui suit au bas du constructeur :

  constructor(props) {
    super(props);
    this.state = {
      message: {
        to: '',
        body: ''
      },
      submitting: false,
      error: false
    };
    this.onHandleChange = this.onHandleChange.bind(this);
  }

Nous pouvons maintenant mettre à jour notre JSX rendu pour définir la valeur des champs de formulaire en utilisant l'état actuel et gérer les mises à jour avec la méthode onHandleChange :

  render() {
    return (
      <form>
        <div>
          <label htmlFor="to">To:</label>
          <input
            type="tel"
            name="to"
            id="to"
            value={this.state.message.to}
            onChange={this.onHandleChange}
          />
        </div>
        <div>
          <label htmlFor="body">Body:</label>
          <textarea
            name="body"
            id="body"
            value={this.state.message.body}
            onChange={this.onHandleChange}
          />
        </div>
        <button type="submit">Send message</button>
      </form>
    );
  }

Rechargez l'application pour pouvoir mettre à jour les champs du formulaire. Si vous disposez des outils de développement React adaptés à votre navigateur, vous verrez également l'état mis à jour.

formulaire avec inspecteur ouvert

Nous devons maintenant traiter la soumission du formulaire. Créez une autre fonction, onSubmit, qui commence par mettre à jour la propriété d'état submitting sur true. Ensuite, utilisez l'API fetch pour envoyer la requête au serveur. Si la réponse est une réussite, effacez le formulaire et définissez submitting sur false. Si la réponse est un échec, définissez submitting sur false, mais définissez error sur true.

  onSubmit(event) {
    event.preventDefault();
    this.setState({ submitting: true });
    fetch('/api/messages', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(this.state.message)
    })
      .then(res => res.json())
      .then(data => {
        if (data.success) {
          this.setState({
            error: false,
            submitting: false,
            message: {
              to: '',
              body: ''
            }
          });
        } else {
          this.setState({
            error: true,
            submitting: false
          });
        }
      });
  }

De même qu'avec la méthode onHandleChange, nous lions cette méthode dans le constructeur :

  constructor(props) {
    super(props);
    this.state = {
      message: {
        to: '',
        body: ''
      },
      submitting: false,
      error: false
    };
    this.onHandleChange = this.onHandleChange.bind(this);
    this.onSubmit = this.onSubmit.bind(this);
  }

Maintenant, dans le JSX, nous ajoutons la méthode onSubmit comme gestionnaire de soumission sur le formulaire. Nous avons également défini la classe du formulaire sur « error » si nous recevons une erreur de la requête. Pendant la soumission du formulaire, nous définissons la propriété disabled du bouton.

  render() {
    return (
      <form
        onSubmit={this.onSubmit}
        className={this.state.error ? 'error sms-form' : 'sms-form'}
      >
        <div>
          <label htmlFor="to">To:</label>
          <input
            type="tel"
            name="to"
            id="to"
            value={this.state.message.to}
            onChange={this.onHandleChange}
          />
        </div>
        <div>
          <label htmlFor="body">Body:</label>
          <textarea
            name="body"
            id="body"
            value={this.state.message.body}
            onChange={this.onHandleChange}
          />
        </div>
        <button type="submit" disabled={this.state.submitting}>
          Send message
        </button>
       </form>
    );
  }

Vous avez tout ce qu'il faut, alors actualisez l'application et entrez votre numéro de téléphone mobile et un message à envoyer. Soumettez le formulaire. Si les détails sont corrects, votre message sera envoyé. Si ce n'est pas le cas, le formulaire indiquera l'état d'erreur.

Maintenant, lorsque vous saisissez des valeurs incorrectes, le formulaire affiche une erreur. Lorsque vous saisissez les valeurs correctes, le message est envoyé.

Envoi de messages et sécurisation de vos identifiants

Envoyer des SMS à partir d'une application Web, c'est bien. Envoyer des SMS à partir de votre application React sans exposer vos identifiants, c'est encore mieux.

Vous pouvez consulter tout le code de cet exemple d'application dans le repo GitHub.

Maintenant que vous avez la base d'une application React capable d'envoyer des SMS, vous pouvez apporter quelques améliorations. Vous pourriez commencer par améliorer la validation et les messages d'erreur. Avec une conception similaire, vous pouvez aussi ajouter des recherches de numéro de téléphonegénérer des appels téléphoniques ou mettre en œuvre l'authentification à double facteur directement depuis votre application React.

J'aimerais en savoir plus sur l'application que vous construisez avec React. Ajoutez un commentaire ci-dessous, écrivez-moi sur Twitter via @philnash ou envoyez-moi un e-mail à l'adresse philnash@twilio.com.