
Crie um agendador de consultas usando Node, Firebase e Vonage
Tempo de leitura: 12 minutos
Introdução
Neste tutorial, vamos criar um aplicativo web para agendamento de compromissos usando Node.js, Express, Firebase e a Messages API for Vonage. O repositório do GitHub para este projeto também está disponível; fique à vontade para cloná-lo aqui.
Configurar o Firebase
Para começar, vamos criar um novo projeto a partir do console do Firebase.
Clique em
add a new projectDê ao seu projeto um nome significativo, por exemplo
vonage appointment schedulerVerifique se você está satisfeito com o identificador único (id) do seu projeto (ele é usado na URL do seu Realtime Database, nos subdomínios do Firebase Hosting e em outros lugares. Ele não pode ser alterado após a criação do projeto)
Clique no botão para continuar
Console view with a text field to enter project and name and edit the project id
Selecione essa opção se quiser ativar o sistema de análise. Neste tutorial, não vamos fazer isso
Clique no botão para criar o projeto
Aguarde até que o projeto seja criado
Project being created
Quando o projeto estiver pronto, clique para continuar. Você será direcionado para a visualização do console do seu projeto
Defina o tipo de cobrança clicando no ícone de engrenagem, depois em “Uso e cobrança” e, em seguida, na guia “Detalhes e configurações” para alterar o plano para o Blaze. Esse plano pré-pago é obrigatório ao usar uma API de terceiros
Instalar a CLI do Firebase Tools
No seu terminal, instale as ferramentas do Firebase com o NPM, caso ainda não as tenha, digitando: npm install -g firebase-tools. Em seguida, digite firebase login. Isso abrirá uma janela no seu navegador que irá autenticá-lo automaticamente (se você já estiver conectado) ou solicitará suas credenciais. Assim que isso for concluído, você terá a CLI do Firebase instalada.
Criar e configurar um banco de dados RealTime
Agora é hora de criarmos a instância do banco de dados NoSQL que armazenará as informações dos horários disponíveis para agendamento. Nosso aplicativo incluirá uma tela na qual o usuário poderá marcar ou cancelar agendamentos. À medida que a pessoa que estiver interagindo com a tela escolher uma data e um horário para o agendamento, esse horário será adicionado ou removido do Firebase RealTime Database.
No menu do Console do Firebase, clique em “Realtime Database” na seção “Build”
Button to create the database
Clique em “Criar banco de dados”
Selecione o local do Realtime Database onde seus dados serão armazenados e clique em
nextSelecione se você usará o banco de dados no modo bloqueado ou de teste. Neste exemplo, estou usando o modo de teste
Clique
enable
Database created
Importar o arquivo JSON do banco de dados
Vamos importar um banco de dados de exemplo que já contém alguns slots alocados e no qual você poderá adicionar e remover slots no futuro. Você pode criar um arquivo chamado myAppointments.json contendo o JSON do trecho abaixo e, em seguida, importar esse arquivo pelo console.
myAppointments.json
{
"myAppointments": {
"0": {
"date": "2021-06-01T09:00",
"userId": "1234abcd"
},
"new_activity_7kh3a3a3z": {
"date": "2023-06-01T08:50",
"userId": "_7kh3a3a3z"
},
"new_activity_etxen95x3": {
"date": "2021-06-01T08:40",
"userId": "_etxen95x3"
}
}
}
Import Database
Adicionar as regras do banco de dados
As regras do Firebase Realtime Database determinam quem pode acessar seu banco de dados, como seus índices são criados e como seus dados são estruturados.
No console do Firebase, na visualização do banco de dados em tempo real, você verá a opção “Regras”; clique nessa aba. Você será direcionado para uma tela que permitirá editar suas regras
Copie e cole as regras do trecho de código abaixo no seu console para definir a
myAppointmentscoleção a ser indexada pelodatecampo.Clique em
Publish
{
"rules": {
".read": "now < 1643842800000", // 2022-2-3
".write": "now < 1643842800000", // 2022-2-3
"myAppointments": {
".indexOn": ["date"]
}
}
}
Edit Firebase Database Rules
Criar a estrutura do projeto
Ao final deste tutorial, a estrutura do seu projeto ficará mais ou menos assim. Nas etapas a seguir, criaremos os arquivos que compõem o conteúdo, a aparência e as funcionalidades, além de gerenciar os serviços que utilizaremos.
Project Structure
Configuração
Crie a pasta do projeto e
cdnela:mkdir appointment-scheduler && cd appointment-schedulerInicializar o NPM:
npm init. Esse comando solicita que você insira informações sobre o projetoInstale as dependências:
npm install @vonage/server-sdk dotenv uuid express firebase-admin firebase-functionsTipo
firebase init. Como já criamos um projeto no painel, você pode selecionarUse an existing project, o que solicitará que você escolha o projeto desejado. Você pode ver meu exemplo com o ID do meu projetovonage-appointment-schedulerabaixo. Também optei por usar oRealtime Databaserecurso
Criar o conteúdo HTML
Você sabia que o elemento `input` do HTML oferece várias opções de tipo para a seleção de data e hora? Por exemplo, temos: date, datetime-local, time. Para este tutorial, usaremos <input type="datetime-local">. Essa abordagem talvez não seja tão robusta quanto usar uma biblioteca de data e hora, pois pode haver algumas inconsistências, mas funciona para os fins deste tutorial. O usuário poderá agendar horários a cada 5 minutos que terminem em 0 ou 5; por exemplo, 18h00 está disponível para agendamento, mas 18h01 não está.
Crie a
public/index.htmlque contenha o conteúdo da visualização para selecionar um novo compromisso ou cancelá-lo, adicionando o trecho de código abaixo
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
<title>Appointment Scheduler</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="styles/styles.css" />
</head>
<body>
<main>
<h1>Appointment Scheduler</h1>
<!-- datepicker from html. Source: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/datetime-local -->
<form action="/appointment" method="POST">
<div>
<label for="slot">Choose your slot: </label>
<input
id="slot"
type="datetime-local"
name="slotdate"
min="2021-06-01T08:30"
max="2023-10-30T16:30"
step="300"
required
/>
<span class="validity"></span>
</div>
<div>
<label for="phonenumber">Your phone number:</label>
<input type="tel" id="phonenumber" name="phonenumber" required />
<span class="validity"></span>
</div>
<div>
<input type="submit" value="Book slot!" />
</div>
</form>
<form action="/cancelAppointment" method="POST">
<div>
<input type="text" name="code" placeholder="code" />
<input type="submit" value="Remove slot!" />
</div>
</form>
</main>
</body>
</html>
Adicionar estilos CSS
Para este aplicativo web de demonstração, vamos aplicar alguns estilos para centralizar o conteúdo na página e também exibir um ✖ vermelho caso a entrada seja inválida e um ✓ caso seja válida.
Crie o
public/styles.cssarquivoCole o código CSS abaixo
body {
margin: auto;
width: 50%;
padding: 10px;
}
div {
margin-bottom: 10px;
display: flex;
align-items: center;
}
label {
display: inline-block;
width: 300px;
}
input:invalid+span:after {
content: '✖';
color: red;
padding-left: 5px;
}
input:valid+span:after {
content: '✓';
color: green;
padding-left: 5px;
} Criar o arquivo de variáveis de ambiente
Crie o
.enve preencha-o com as informações abaixo
O
FIREBASE_DATABASE_URLpode ser encontrado no console do FirebaseO
VONAGE_API_KEYe oVONAGE_API_SECRETpodem ser encontrados no Painel da VonageO
VONAGE_FROM_NUMBERcontém o número, o nome ou a marca que aparecerá como remetente da mensagemO
VONAGE_TO_NUMBERé o número que receberá as mensagens SMS
Crie o arquivo JavaScript server.js
Vamos criar o server.js para informar ao Express como lidar com as solicitações enviadas pela interface do usuário. Vou mostrar passo a passo como vamos construí-lo. Você pode encontrar o arquivo completo do servidor aqui.
Nosso aplicativo web utilizará o Express e lerá os arquivos estáticos que criamos anteriormente na public pasta.
Para adicionar as dependências e os arquivos de importação, insira o trecho de código abaixo no seu
script/server.js
// script/server.js
require('dotenv').config();
const express = require('express');
const app = require('express')();
const port = 3000; //setting the port to listen to as 3000
const admin = require('firebase-admin');
const Vonage = require('@vonage/server-sdk');
const SMS = require('@vonage/server-sdk/lib/Messages/SMS';
const { v4: uuidv4 } = require('uuid');
app.use(express.static('public'));
app.use(express.json());
app.use(express.urlencoded({ extended: true })); Adicionar a conta de serviço
Uma conta de serviço do Firebase pode ser usada para autenticar diversos recursos do Firebase; para o nosso projeto, usaremos o SDK de administração do Firebase para acessar a URL do nosso banco de dados.
No Console do Firebase, clique na engrenagem e selecione a aba “Conta de serviço”
Clique no botão para
generate keyAdicione o arquivo gerado à raiz do seu projeto e renomeie-o para
serviceAccountKey.jsonCopie e cole o trecho de código de configuração do Admin SDK no seu projeto, conforme mostrado na etapa seguinte deste tutorial, para inicializar o Firebase. Estamos usando
${process.env.FIREBASE_DATABASE_URLpara ler a URL do.envarquivo, mas é a mesma URL do banco de dados encontrada na configuração do Admin SDK do Firebase.
Admin SDK configuration
Inicializar o Firebase
Usamos initializeApp para criar e inicializar uma instância do aplicativo Firebase que utilizará a /myAppointments instância do banco de dados do Firebase que criamos e preenchemos anteriormente no Console do Firebase.
Adicione este trecho de código abaixo ao seu
server.jspara inicializar o Firebase.
const serviceAccount = require('../serviceAccountKey.json');
// Initializes firebase
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: `${process.env.FIREBASE_DATABASE_URL}`,
});
// A Reference represents a specific location in your Database and can be
// used for reading or writing data to that Database location.
ref = admin.database().ref('/myAppointments'); Inicializar o objeto da API da Vonage
Criamos a instância da classe cliente da Vonage, inicializando-a com a chave e o segredo da API da Vonage que você adicionou anteriormente ao seu .env arquivo.
Adicione o trecho de código abaixo ao seu
server.jspara adicionar o Vonage.
const vonage = new Vonage({
apiKey: process.env.VONAGE_API_KEY,
apiSecret: process.env.VONAGE_API_SECRET,
}); Criar a função getDateTime()
O tipo de entrada HTML datetime-local é formatado como AAAA-MM-DDThh:mm. Portanto, vamos escrever uma função para separar a data da hora, dividindo-a no caractere T. Por exemplo, no exemplo 2018-06-12T19:30, teríamos 2018-06-12 para a data e 19:30 para a hora.
Adicione este trecho de código abaixo ao seu
server.jspara adicionar agetDateTime()função
const getDateTime = (slot) => {
return slot.split('T');
};
Crie o /appointment ponto final
É hora de criar o /appointment endpoint para lidar com as solicitações POST de criação de um compromisso. Esse endpoint verificará se o horário está disponível, adicionará o horário ao banco de dados do Firebase e, por fim, enviará uma confirmação por SMS para o celular do usuário usando a Messages API do Vonage.
Adicione este trecho de código abaixo ao seu
server.jspara criar o/appointmentponto de extremidade.
app.post('/appointment', async (request, response) => {
let phonenumber = request.body.phonenumber;
let slot = request.body.slotdate;
let [date, time] = getDateTime(slot);
// Checks if a slot is available
checkIfAvailable = async (slot) => {};
// Adds to Database
addToDatabase = () => {};
// Sends an SMS back to the user's phone using the Vonage Messages API
sendSMStoUser = async (code) => {};
});
Você deve ter percebido que grande parte da funcionalidade dentro do manipulador de solicitações ainda não foi implementada; portanto, vamos agora expandir os esboços das funcionalidades necessárias.
Verificar a disponibilidade de vagas
Essa função verifica se um slot está disponível, verificando se ele já existe no banco de dados. Estamos consultando ref.orderByChild('date'). As consultas podem ordenar uma chave por vez. Definimos previamente nosso índice por meio das .indexOn nas Regras do Firebase para obter melhor desempenho. Em seguida, utilizamos .once('value') para escutar exatamente um evento com o valor e, em seguida, interrompe a escuta.
Adicione este trecho de código abaixo ao seu
server.jspara criar acheckIfAvailable()função
// Checks if a slot is available
checkIfAvailable = async (slot) => {
let snapshot = await ref.orderByChild('date').once('value');
let available = true;
snapshot.forEach((data) => {
let dataval = data.val();
for (let key in dataval) {
let datapoint = dataval[key];
if (slot === datapoint) {
available = false;
}
}
});
return available;
};
Adicionar o slot ao banco de dados
A função a seguir addToDatabase() adiciona o horário e um código ao banco de dados do Firebase. Esse código é necessário para cancelar o agendamento.
// Adds the slot to the database
addToDatabase = () => {
let code = uuidv4();
ref.child(code).set({
date: slot,
userId: code,
});
return code;
};
Envie um SMS com as informações da consulta
Por fim, assim que o horário for reservado, é enviada uma mensagem de SMS de confirmação ao usuário com o seguinte texto Meeting booked at ${time} on date: ${date}. Please save this code: ${code} in case you'd like to cancel your appointment. como você pode ver na função sendSMStoUser().
Adicione este trecho de código abaixo ao seu
server.jspara criar asendSMStoUser()função
// Sends an SMS back to the user's phone using the Vonage Messages API
sendSMStoUser = async (code) => {
const to = phonenumber;
const text = `Meeting booked at ${time} on date: ${date}. Please save this code: ${code} in case you'd like to cancel your appointment.`;
const result = await new Promise((resolve, reject) => {
vonage.messages.send(
new SMS(text, process.env.VONAGE_TO_NUMBER, "Vonage"),
(err, data) => {
if (err) {
console.error(err);
} else {
console.log(data.message_uuid);
}
}
);
});
};
Finalizar a lógica de negócios
O trecho de código abaixo é responsável por chamar as funções auxiliares criadas anteriormente. Se o horário estiver disponível, o horário do usuário será adicionado ao banco de dados e ele receberá um SMS de confirmação. Caso contrário, será solicitado que ele escolha um horário diferente.
let available = await checkIfAvailable(slot);
if (available) {
let code = addToDatabase();
await sendSMStoUser(code);
response.send(`This slot is available, booking it for you now: ${slot}`);
} else {
// Sends user error
response.send(
`Sorry, you'll need to choose a different slot.${slot} is already busy.`
);
} Cancelar a consulta /cancelAppointment
Vamos criar o /cancelAppointment endpoint que lida com as solicitações POST para cancelar um agendamento do banco de dados, usando um código fornecido pelo usuário, que ele recebeu ao agendar o compromisso.
app.post('/cancelAppointment', async (request, response) => {
let code = request.body.code;
// Removes slot from the database
removeSlotFromDB = (code) => {
ref.child(code).remove();
};
removeSlotFromDB(code);
response.send(`This slot has been removed.`);
});
Ouça o Porto
Por fim, o aplicativo ficará à escuta na porta especificada; se for executado localmente, ele estará acessível em https://localhost:${port}. Nessa URL, você pode interagir com a interface do usuário deste aplicativo de demonstração e verificar os slots que estão sendo adicionados ou removidos na página da web do console do Firebase.
app.listen(port, () => {
console.log(`I run on port ${port}`);
});
Experimente
No seu
package.jsonarquivo, adicione o script de inicialização"start": "node script/server.js"logo abaixo de"test": "echo \"Error: no test specified\" && exit 1",. Deve ficar assim:
Instale todas as dependências
npm installExecute o comando NPM para executar o projeto
npm run startAcesse
http://localhost:3000Adicione e remova horários de agendamento e veja como eles são adicionados e removidos do Banco de Dados em Tempo Real do Firebase
Example adding a slot and it being shown on the Firebase Realtime database
Conclusão e próximos passos
Hoje você viu como criar um aplicativo web de demonstração para agendamento de consultas. Agora você pode seguir em frente e adicionar um estilo mais sofisticado e outras funcionalidades. Você pode usar o que aprendeu aqui para criar diversos sistemas de agendamento, seja para uma academia ou para agendar horários de vacinação — deixe a criatividade fluir!
Entre em contato conosco no Twitter e junte-se à nossa comunidade no Slack.