Como enviar uma mensagem SMS do React com o Twilio

October 24, 2018
Escrito por
Phil Nash
Twilion

Como enviar uma mensagem SMS do React com o Twilio

Falamos muito sobre o envio de mensagens SMS de aplicativos da Web. Mas e sobre o envio de mensagens SMS de um aplicativo React? Ele requer um pouco mais do que apenas a versão do servidor, mas não demorará muito.

Por que não devo usar a API REST do lado do cliente?

Tecnicamente, você pode enviar um SMS usando a API REST da Twilio diretamente de um aplicativo JavaScript do lado do cliente. Mas (e trata-se de um grande "mas") se fizesse isso, você exporia suas credenciais do Twilio para qualquer pessoa que usasse seu site. Um usuário mal-intencionado poderia, então, fazer mau uso dessas credenciais, causando um grande estrago na sua conta.

GIF de um hacker de mentira, com uma balaclava e uma mão extra.

Visualização ao vivo de um hacker com as credenciais da sua conta

Para evitar isso, criaremos um aplicativo de back-end que implementa a API REST da Twilio, encerra suas credenciais e envia mensagens SMS para você. Você pode então chamar o back-end pelo aplicativo React e enviar mensagens SMS sem divulgar suas credenciais na Internet.

Nossas ferramentas

Para que nosso aplicativo envie mensagens de texto usando a API REST da Twilio, precisaremos do seguinte:

Para começar, baixe ou clone o aplicativo react-express-starter que eu criei no meu último post do blog.

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

Mude para o diretório e instale as dependências.

cd react-express-starter
npm install

No diretório do projeto, crie um arquivo chamado .env:

touch .env

Agora, você pode executar o comando npm run dev para testar se o projeto está funcionando. O aplicativo será carregado no navegador no localhost:3000.

Este aplicativo inicial é configurado para ter um aplicativo React e um aplicativo Express no mesmo projeto que você pode executar simultaneamente. Se quiser descobrir como isso funciona, confira este post do blog.

Criar o lado do servidor

Conforme discutido, precisamos fazer as chamadas da API da Twilio pelo servidor. Adicionaremos um endpoint ao servidor Express que possa ser chamado pelo nosso aplicativo React. Comece instalando o Módulo Twilio Node.js. Observação: para fins deste aplicativo, estou salvando as dependências do servidor como dependências de desenvolvimento para separá-las das dependências do cliente.

npm install twilio --save-dev

Em seguida, precisamos configurar o aplicativo com nossas credenciais da Twilio. Reúna seu Account SID (SID de conta) da Twilio e o Auth Token (Token de autenticação) do console da Twilio junto com um Número de telefone da Twilio que possa enviar mensagens SMS. Insira todos os três no arquivo .env que você criou anteriormente, assim:

TWILIO_ACCOUNT_SID=YOUR_ACCOUNT_SID
TWILIO_AUTH_TOKEN=YOUR_AUTH_TOKEN
TWILIO_PHONE_NUMBER=YOUR_TWILIO_PHONE_NUMBER

Isso definirá suas credenciais no ambiente. Agora, abra server/index.js para que possamos começar com o código necessário de envio de mensagem. No outro módulo, na parte superior do arquivo, solicite e inicialize a biblioteca da Twilio com as credenciais do ambiente.

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
);

Vamos enviar os dados para o endpoint que estamos criando como JSON, então precisaremos analisar o corpo do JSON. Configure o app Express com o analisador JSON do analisador de corpo:

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

Crie uma rota para uma solicitação POST. Adicione o seguinte abaixo da rota para /api/greeting:

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

});

Também responderemos com JSON, portanto, defina o cabeçalho Content-Type como application/json.

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

});

Em seguida usaremos o Twilio Client que iniciamos anteriormente para criar uma mensagem. Usaremos o número de telefone da Twilio como o número from e obteremos o número to e o body da mensagem do corpo da solicitação de entrada. Isso retorna uma Promise (Promessa) que será cumprida quando a solicitação de API for bem-sucedida ou rejeitada, se falhar. Em ambos os casos, retornaremos uma resposta JSON para informar ao cliente se a solicitação foi bem-sucedida ou não.

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 }));
    });
});

Isso é tudo de que precisamos no servidor. Vamos começar com a parte do React.

Construir o lado do cliente

No lado do cliente, podemos encapsular o formulário para enviar nosso SMS por meio do servidor inteiramente em apenas um componente. Portanto, no diretório src, crie um componente chamado SMSForm.js e comece com o texto clichê para um componente:

import React, { Component } from 'react';

class SMSForm extends Component {

}

export default SMSForm;

Criaremos um formulário que o usuário possa preencher com um número de telefone e uma mensagem. Quando o formulário for enviado, ele enviará os detalhes do endpoint do nosso servidor e enviará a mensagem como um SMS para o número.

Vamos criar o método render para este componente primeiro: ele incluirá um formulário, uma entrada para o número de telefone, uma área de texto para a mensagem e um botão para enviar:

  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>
    );
  }

Podemos adicionar alguns arquivos CSS para modelar um pouco esse formulário. Crie o arquivo src/SMSForm.css e adicione o seguinte:

.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;
}

Importe o CSS na parte superior do componente SMSForm:

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

Agora, importe o componente no arquivo src/App.js e substitua o método de renderização pelo seguinte:

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;

Inicie o aplicativo com o comando npm run.dev e verá o formulário na página.

Tela inicial do app React

O formulário ainda não faz nada, então vamos corrigir isso.

Criar um formulário interativo em React

Para vincular o formulário HTML ao componente, precisamos fazer algumas coisas:

  • Mantenha o estado da entrada e da área de texto atualizado no estado do componente
  • Administre o envio do formulário e o envio dos dados para o servidor
  • Administre a resposta do servidor e limpe o formulário caso a mensagem tenha sido enviada com êxito ou mostre um erro caso não tenha sido enviada

Começaremos configurando algum estado inicial no constructor (construtor). Precisaremos armazenar as entradas do formulário, verificar se o formulário está sendo enviado no momento (para que possamos desativar o botão "submit" [enviar]) e conferir se houve um erro. Crie o constructor (construtor) para o componente da seguinte maneira:

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

  // rest of the component
}

Precisaremos de um método que possa lidar com alterações nos campos do formulário e atualizar o estado. Poderíamos criar dois métodos, um para a entrada e outro para a área de texto, mas como os nomes dos elementos do formulário e itens no estado correspondem, podemos criar um método para cobrir ambos.

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

Observe aqui que usamos os nomes de propriedade computados do ES2015 para definir a propriedade correta no estado e o operador de distribuição para preencher o restante do estado.

Precisaremos vincular esse método ao objeto para garantir que this esteja correto quando o utilizarmos para receber um evento. Adicione o seguinte à parte inferior do constructor (construtor):

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

Agora, podemos atualizar nosso JSX renderizado para definir o valor dos campos do formulário usando o estado atual e gerenciar atualizações com nosso método 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>
    );
  }

Recarregue o app e poderá atualizar os campos do formulário. Se você tiver as ferramentas de desenvolvimento React para seu navegador, também poderá ver o estado sendo atualizado.

Tela do navegador com o ambiente de desenvolvimento aberto e mostrando a aba do React

Agora, precisamos lidar com o envio do formulário. Crie outra função, onSubmit,  que começa atualizando a propriedade do estado submitting para verdadeira. Em seguida, use a fetch API para fazer a solicitação ao servidor. Se a resposta for bem-sucedida, limpe o formulário e defina submitting como falso. Se a resposta não tiver êxito, defina submitting como falso, mas defina error como verdadeiro.

  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
          });
        }
      });
  }

Assim como com o método onHandleChange, também vinculamos este método no constructor (construtor):

  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);
  }

Agora, no JSX, adicionamos o método onSubmit como gerenciador de envio no formulário. Também definimos a classe do formulário como "error" (erro) se recebermos um erro da solicitação. E, enquanto o formulário está sendo enviado, definimos a propriedade do botão como disabled.

  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>
    );
  }

Isso é tudo que precisamos. Atualize o app novamente e insira seu número de celular e uma mensagem a ser enviada. Envie o formulário e, se os detalhes estiverem corretos, sua mensagem será enviada; caso contrário, o formulário mostrará que o estado está em erro.

Agora quando informar valores inválidos o formulário vai mostrar um erro e quando você informar um valor correto a mensagem é enviada.

Enviar mensagens e manter as credenciais seguras

O envio de mensagens SMS de um app da Web é interessante. O envio da mensagem SMS do app React sem expor suas credenciais é ainda mais interessante.

Você pode verificar todo o código deste aplicativo de exemplo no repositório do Github.

Agora que tem a base de um app React que pode enviar mensagens SMS, você pode fazer algumas melhorias. A primeira provavelmente seria melhorar a validação e as mensagens de erro. Com um design semelhante, é possível adicionar lookups de número de telefonegerar chamadas telefônicas ou implementar a autenticação de 2 fatores diretamente do app React também.

Eu adoraria saber mais sobre o app que você está criando com React. Deixe um comentário abaixo, encontre-me no Twitter em @luisleao ou envie um e-mail para lleao@twilio.com.

Este artigo foi traduzido do original "How to send an SMS from React with Twilio". Enquanto melhoramos nossos processos de tradução, adoraríamos receber seus comentários em help@twilio.com - contribuições valiosas podem render brindes da Twilio.