https://a.storyblok.com/f/270183/1032897/7d16f072f8/full-stack-nexmo-app-with-express-and-react.png

Crie um aplicativo Nexmo Full Stack com Express e React

Publicado em May 12, 2021

Tempo de leitura: 14 minutos

Com o Client SDK do Nexmo para JavaScript, você pode criar um aplicativo front-end que permita aos usuários controlar as conversas das quais fazem parte. Um Conversa Nexmo pode incluir dois usuários ou muitos e utilizar diversos tipos de mídia. Permitir que os participantes controlem elementos da conversa apenas amplia as opções do que seu aplicativo pode oferecer.

O React é uma opção muito popular para a criação de aplicativos para o front-end, e Concepts como Conversas e Usuários no Nexmo Client SDK se encaixam perfeitamente nos componentes do React, com seus próprios controles de estado. No entanto, há algumas coisas que o Client SDK não consegue fazer; portanto, o panorama completo do seu aplicativo só fica claro quando também levamos em conta o back-end. Usando o Express, você pode adicionar algumas rotas simples para auxiliar seu front-end no gerenciamento de usuários e realizar qualquer outra tarefa que considere melhor manter no servidor.

Ao contrário de um aplicativo tradicional hospedado em servidor, seu front-end React é um aplicativo independente, o que significa que seu aplicativo “full stack” é, na verdade, composto por dois aplicativos. Cada um ficará à escuta em sua própria porta e responderá às solicitações sem precisar se comunicar com o outro. Do ponto de vista da estrutura de arquivos, isso se traduz em um aplicativo dentro de outro aplicativo. Você configurará seu servidor Express na raiz do diretório e, em seguida, adicionará o aplicativo React — completo com seu próprio package.json— em um subdiretório.

Configuração do aplicativo

Na raiz do diretório do projeto, você começará criando um package.json para seu aplicativo Express e um server.js que irá contê-lo. Você também vai querer criar um .env arquivo que armazenará as credenciais confidenciais do seu aplicativo e da sua conta. Você precisará instalar alguns pacotes do npm: Express, body-parser, dotenve, é claro, o SDK do Nexmo para Node:

npm install -s express body-parser dotenv nexmo@beta

Para iniciar as duas applications ao mesmo tempo, você também vai precisar concurrently instalar o seguinte como dependência de desenvolvimento:

npm install --save-dev concurrently

Em seguida, você fornecerá as chaves, os IDs e os segredos necessários para identificar seu aplicativo Nexmo, que você pode armazenar no seu .env arquivo:

API_KEY="" API_SECRET="" APP_ID="" PRIVATE_KEY="/private.key"

Você pode encontrar sua chave e seu segredo da API na página “Introdução” do seu painel do Nexmo. Você pode obter um ID de aplicativo e uma chave privada gerada para baixar na página página “Criar um aplicativo” (se seu aplicativo for para mensagens, você pode usar a página “Criar um aplicativo de mensagens” ). No exemplo, a chave privada está salva na raiz do seu diretório; portanto, certifique-se de atualizar o caminho em .env caso você a mova para outro local.

Depois de concluir essa configuração, você pode passar para a criação do aplicativo cliente.

Criar um aplicativo React

Você pode criar rapidamente uma estrutura inicial para seu cliente usando o muito prático create-react-app. Como esse aplicativo React ficará em um subdiretório do seu projeto, você pode especificar o nome do subdiretório de sua preferência ao executar o comando (embora talvez seja melhor alterar o nome do aplicativo para algo mais descritivo depois que ele gerar seu package.json). No nosso exemplo, chamamos o subdiretório de “client”:

npx create-react-app client

Isso fornecerá a maior parte do que você precisa no cliente, incluindo as dependências do React e um conjunto de scripts para realizar tarefas como iniciar e compilar seu aplicativo. O único pacote adicional que você precisará baixar do npm é o Nexmo Client SDK:

npm install -s nexmo-client@beta

No seu cliente package.json , você também precisará adicionar um proxy, que fará referência ao seu servidor Express e à porta em que ele está rodando:

"proxy": "http://localhost:3001",

Express Server

A inicialização do seu servidor Express deve parecer familiar se você já trabalhou com o Express antes. Você também vai querer incluir dotenv e body-parser, sendo que este último você irá anexar ao seu aplicativo como middleware:

require('dotenv').config();

// init server
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
app.use(bodyParser.json());

Em seguida, você deverá criar um novo cliente Nexmo, fornecendo as variáveis que você salvou no seu .env arquivo, que dotenv ficarão disponíveis como membros de process.env:

// create a Nexmo client
const Nexmo = require('nexmo');
const nexmo = new Nexmo({
  apiKey: process.env.API_KEY,
  apiSecret: process.env.API_SECRET,
  applicationId: process.env.APP_ID,
  privateKey: __dirname + process.env.PRIVATE_KEY 
}, {debug: true});

Para este exemplo simples, vamos criar apenas endpoints para obter um JWT e criar um novo usuário. Você pode definir as assinaturas dos seus endpoints agora, e forneceremos a lógica para ambos os aplicativos juntos em uma etapa posterior. Por fim, é claro, você vai querer que seu servidor Express fique escutando na porta que você especificou no React’s package.json:

app.post('/getJWT', function(req, res) {});
app.post('/createUser', function(req, res) {});

app.listen(3001);

Componente de aplicativo React

Quando você executou create-react-app, ele deveria ter criado um ponto de entrada para seu aplicativo em src/index.js no subdiretório do cliente. Isso carrega o componente definido em src/App.js e o exibe como o corpo da sua página de destino. Esse componente é o local ideal para realizar tarefas administrativas, como obter um JWT e fazer login no seu aplicativo Nexmo. Também é um bom contêiner para dois componentes filhos: User e Conversation. Para começar, importe dois componentes que você criará em instantes:

import React from 'react';
import User from './User';
import Conversation from './Conversation';

import nexmoClient from 'nexmo-client';

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {};
    
    this.login = this.login.bind(this);
    this.getJWT = this.getJWT.bind(this);
    this.userUpdated = this.userUpdated.bind(this);
  }
    
  login() {}  
  getJWT() {}  
  userUpdated() {}
  render() {}
    
};

export default App;

Já que estamos nisso, você pode ver que também importamos o Client SDK do Nexmo e organizamos a classe criada para que ela esteja pronta para ser preenchida com nossa lógica. Também adicionamos marcadores de posição para as funções de que precisaremos.

Agora que você tem referências aos dois componentes filhos (mesmo que ainda não os tenhamos criado), pode atualizar sua função de renderização para carregá-los na sua página:

render() {
  return (
    <div className="nexmo">
      <User onUpdate={this.userUpdated} />
      <Conversation app={this.state.app} loggedIn={!!this.state.token} />
    </div>
  );
}

Registro no aplicativo Nexmo

O User componente vai chamar userUpdated quando quiser relatar alterações em seu estado; assim, essa função se torna o primeiro elo da sua cadeia de execução. Você procurará uma username propriedade no objeto de estado que você receber e, se ela existir, continuará para obter um JWT para esse usuário:

userUpdated(user) {
  if (user.username) {
    this.getJWT(user.username);
  }
}

Sua getJWT função consistirá principalmente em um fetch e no tratamento da resposta recebida. Você precisará POST enviar o nome de usuário que a função recebe ao servidor Express como JSON, em seguida analisar os dados e salvar seu novo JWT como a propriedade state token. Feito isso, você pode chamar a login função para concluir a inicialização do seu aplicativo:

getJWT(username) {
  fetch('/getJWT', {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify({name: username})
  })
  .then(results => results.json())
  .then(data => {
    this.setState({
      token: data.jwt
    });
    this.login();
  });
}

Obter um JWT do servidor

Vamos voltar rapidinho para o seu aplicativo Express em server.js e forneçamos ao endpoint a função do lado do cliente getJWT . Usando o SDK do Nexmo para Node, podemos gerar um JWT informando nosso ID de aplicativo mais uma vez; o sub, ou nome de usuário, que estamos enviando do cliente; um prazo de validade em segundos; e as permissões que queremos que esse token tenha. No código abaixo, o usuário pode realizar ações relacionadas a usuário, conversas, sessões e Applications, o que é suficiente para nosso aplicativo bem simples:

app.post('/getJWT', function(req, res) {
  const jwt = nexmo.generateJwt({
    application_id: process.env.APP_ID,
    sub: req.body.name,
    exp: Math.round(new Date().getTime()/1000)+3600,
    acl: {
      "paths": {
        "/v1/users/**":{},
        "/v1/conversations/**":{},
        "/v1/sessions/**":{}
      }
    }
  });
  res.send({jwt: jwt});
});

Agora que seu servidor está enviando um token para o aplicativo React, você pode voltar para App.js e implementar a lógica da sua função final, login. Na verdade, não há muito o que fazer. Usando o novo token salvo no estado do componente App, você faz login no cliente Nexmo e recebe uma referência ao seu aplicativo Nexmo conectado. Você pode salvar isso no estado do componente, e pronto: este componente está concluído!

login() {
  let nexmo = new nexmoClient();
  nexmo.createSession(this.state.token).then(app => {
    this.setState({
      app: app
    });
  });
}

Componente de usuário do React

Como o seu componente App está aguardando que o componente User acione o fluxo de login, vamos criar esse componente agora. Em um novo User.js arquivo no mesmo diretório que App.js, você pode definir um esboço do componente:

import React from 'react';

class User extends React.Component {
  constructor(props) {
    super(props);
    this.state = {};
    
    this.createUser = this.createUser.bind(this);
    this.setUsername = this.setUsername.bind(this);
  };
  
  createUser() {}
  setUsername() {}
  render() {}
    
};

export default User;

Em uma aplicação real, seria recomendável usar um sistema para gerenciar e armazenar seus usuários, além de autenticá-los. Para este exemplo básico, no entanto, você simplesmente criará um novo usuário toda vez que acessar esta página. Na sua render função, você pode verificar se o estado do componente contém uma userId propriedade. Se sim, você pode exibir uma mensagem confirmando que o usuário está conectado. Caso contrário, você pode mostrar um campo de texto e um botão para criar o novo usuário:

render() {
  if (this.state.userId) {
    return (
      <div className="userinfo userconnected">
        Connected as <span className="username">{this.state.username}</span>
      </div>
    );
  } else {
    return (
      <div className="userinfo">
        <input type="text" onChange={evt => this.setUsername(evt)} />
        <button onClick={this.createUser}>Create user</button>
      </div>
    );
  }
}

Criação de novos usuários

A criação do usuário é, na verdade, um processo em duas etapas que começa monitorando as alterações no texto do seu campo de texto e armazenando o valor atualizado. Se você quisesse tornar esse aplicativo mais robusto, poderia começar verificando se esse valor está de acordo com as regras de nome de usuário e com a lista de usuários existentes, e notificar o usuário sobre problemas de validade ou duplicação por meio de uma mudança no estilo da interface. Mas, para este exemplo, vamos simplesmente armazenar qualquer texto que o usuário tenha digitado:

setUsername(evt) {
  this.setState({
    username: evt.target.value
  });
}

Assim que o usuário clicar no botão “Criar usuário”, você poderá enviar outra solicitação ao seu servidor Express. Você enviará o nome de usuário conforme armazenado no estado por meio de setUsername, e, quando o servidor responder, acione a onUpdate função fornecida pelo App componente quando este foi instanciado:

createUser() {
  fetch('/createUser', {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify({name: this.state.username})
  })
  .then(results => results.json())
  .then(data => { 
    this.setState({
      userId: data.id
    }, () => this.props.onUpdate(this.state));
  });
}

O createUser endpoint é a última parte do seu servidor Express que ainda não abordamos; então, vamos voltar para server.js e preencher essa lógica. Você pode chamar users.create no seu objeto Nexmo, passando o nome de usuário do cliente e um nome de exibição opcional (que não incluímos no código do cliente para este aplicativo, mas você pode optar por fornecer mais tarde). Se a operação for bem-sucedida, você passará o ID do novo usuário de volta para o cliente:

app.post('/createUser', function(req, res) {
  nexmo.users.create({
    name: req.body.name,
    display_name: req.body.display_name || req.body.name
  },(err, response) => {
    if (err) {
      res.sendStatus(500);
    } else {
      res.send({id: response.id});
    }
  });
});

Agora, toda a lógica necessária para criar um usuário tanto no seu aplicativo React quanto no Express já está disponível; assim, seu aplicativo React poderá fazer o login e realizar ações como criar uma conversa.

Componente de conversa do React

O último arquivo que você precisa criar é Conversation.js, no mesmo diretório que App.js e User.js. O esboço do componente é ainda menor do que os dois que você já criou, mas, em uma aplicação real, provavelmente seria o componente que conteria mais lógica e, talvez, até mesmo vários componentes filhos:

import React from 'react';

class Conversation extends React.Component {
  constructor(props) {
    super(props);
    this.state = {};
    
    this.createConversation = this.createConversation.bind(this);
  }
  
  createConversation() {}
  render() {}
    
};

export default Conversation;

A função desse componente render função precisa, na verdade, apenas fornecer um botão, mas você pode torná-la um pouco mais agradável desativando o botão até que o App componente notificar que Conversation que concluiu sua inicialização. Você pode então ocultar o botão assim que a conversa for iniciada e uma referência a ela for armazenada no estado do componente:

render() {
  if (this.state.conversation) {
    return (
      <div className="conversation">Joined conversation!</div>
    );
  } else {
    return (
      <div className="conversation">
        <button 
          onClick={this.createConversation} 
          disabled={!this.props.loggedIn}>Start conversation</button>
      </div>
    );
  }
}

A maior parte da lógica na qual a createConversation função se baseia nos objetos Applications e Conversation da Nexmo. Quando o usuário clica no botão, você pode criar uma nova conversa chamando a app propriedade passada a este componente. Isso retornará uma conversa, à qual você pode ingressar e, em seguida, salvar como uma propriedade de estado:

createConversation() {
  this.props.app.newConversation().then(conv => {
    conv.join().then(member => {
      this.setState({
        conversation: conv
      });
    });
  });
}

A partir daqui, você pode convidar outros usuários para a conversa, começar a definir manipuladores para eventos da conversa ou abrir um fluxo de áudio para permitir que os participantes conversem entre si.

Iniciando os aplicativos

Você quer poder iniciar seus aplicativos Express e React como se fossem um só; portanto, a última coisa que precisa fazer é fornecer o mecanismo para que eles sejam iniciados juntos. Você já editou o package.json para que ele reconheça o aplicativo Express; agora é hora de adicionar alguns scripts ao package.json para que npm start ele realmente inicie tudo.

Na package.json diretório raiz do seu projeto, adicione ou modifique três scripts: start, client, e server:

"scripts": {
    "client": "cd client && npm start",
    "server": "node server.js",
    "start": "concurrently --kill-others-on-fail \"npm run server\" \"npm run client\""
  },

O concurrently pacote que você instalou no início deste tutorial iniciará o servidor Express ao mesmo tempo em que acessa o diretório “client” (observe que você deve substituir “client” pelo nome do seu subdiretório do React, caso tenha dado outro nome a ele) e executa o start script fornecido por create-react-app. Se você executar npm start agora, você deverá conseguir abrir um navegador para acessar o aplicativo React em http://localhost:3000 e ver seu aplicativo em execução.

Quer ver uma versão um pouco mais complexa desse aplicativo em ação? Você pode ver o código completo no Glitch e remixá-lo para explorar ainda mais o Nexmo Conversations. E agora que você já domina os fundamentos, pode seguir em frente para criar um aplicativo de bate-papo com React e Nexmo.

Compartilhar:

https://a.storyblok.com/f/270183/250x250/f231d97f1b/garann-means.png
Garann MeansFormador de Desenvolvedores

Sou desenvolvedor de JavaScript e instrutor de desenvolvimento na Vonage. Ao longo dos anos, tenho me interessado muito por modelos, Node.js, aplicativos web progressivos e estratégias “offline-first”, mas o que sempre adorei de verdade é uma API útil e bem documentada. Meu objetivo é tornar a sua experiência com nossas APIs a melhor possível.