Hoje quero compartilhar com você como enviar SMS em NodeJS usando o serviço SNS da AWS. Esta aplicação é um micro-serviço e você pode utilizar como um componente do seu software atual. Você só precisa subir a app em algum ambiente node e adicionar itens na fila de notificações.
No exemplo estou usando uma base MongoDB, mas você pode adaptar o serviço para uma base MySQL ou uma fila em um RabbitMQ ou SQS.
Atualmente é comum utilizarmos e-mail ou sms para confirmar um cadastro, alterar senha, validar um celular ou até ajudar na validação de acesso.
O Google, Uber, Facebook e tantas outras empresas usam este tipo de funcionalidade em seus apps, é uma maneira eficiente e rápida de confirmar a autenticidade de um usuário. Neste tutorial vou te ajudar aplicar o mesmo recurso em seu software.
O que você precisa saber
O Amazon Simple Notification Service (SNS) é uma solução para envio de sms e notificações push, principalmente para envio de mensagens curtas, promocionais, notícias e avisos de atualizações.
Você pode enviar para mais de 200 países e conta com a escalabilidade e flexibilidade que praticamente todo serviço AWS oferece. Porém, este serviço tem custos, em geral você vai pagar pelo envio da notificação e o tráfego de saída.
No caso do SMS, os preços podem variar também por região e operadora de destino, consulte a página de pricing para não ter surpresas no fim do mês. 😉
Outro ponto importante é os tipos de conteúdo das mensagens, Transacionaos e Promocionais. Para cada também há um pricing diferente e regras de envio que você precisa considerar na integração. Neste exemplo, vamos enviar mensagens transacionais, porque é um shortcode para validar um cadastro.
Quer saber mais sobre os serviços da AWS, como usar em seu projeto e por onde começar? Cadastre-se na minha lista de e-mails e receba dicas práticas semanalmente.
Criando a aplicação
Vou ser direto neste tutorial, em breve este conteúdo vai estar gravado e disponível no meu canal no Youtube e lá vou explicar com mais detalhes cada etapa.
Dependências e Setup
Vamos criar um diretório para nosso app e adicionar as dependências.
mkdir bot-sendsms && cd bot-sendsms && npm init -f
npm i --save aws-sdk dotenv mongoose interval-promise babel babel-cli
npm i --save-dev babel-preset-env
Agora vamos criar o arquivo babel.rc e adicionar o preset env:
{
"presets": ["env"]
}
Adicione o arquivo .env para as variáveis de conexão ao MongoDB e as chaves de acesso ao seu perfil na AWS.
DB_MONGO_HOSTNAME = 'your_host_mongodb'
DB_MONGO_USERNAME = 'your_user'
DB_MONGO_PASSWORD = 'your_pass'
DB_MONGO_DATABASE = 'your_db_name'
DB_MONGO_PORT = 00000
AWS_ACCESS_KEY_ID = 'your_key'
AWS_SECRET_ACCESS_KEY = 'your_secret_key'
Acessando o MongoDB
Vamos criar a estrutura do model para manipular nossos dados, no seu MongoDb crie uma collection chamada notifications, após crie o diretório src e adicione o arquivo model.js com o seguinte script:
export default (conn, mongoose) => {
return conn.model("notifications", new mongoose.Schema({
to: {
type: String,
required: true,
},
body: {
type: String,
required: true,
},
sended: {
type: Boolean,
default: false,
required: true,
},
createdAt: {
type: Date,
default: Date.now,
},
sendedAt: Date,
}));
};
Agora crie o diretório util na raiz de sua app e adicione o arquivo datasource.js com o seguinte script de conexão.
import dotenv from 'dotenv'
import mongoose from 'mongoose'
import model from '../src/model'
dotenv.config()
const database = process.env.DB_MONGO_DATABASE
const username = process.env.DB_MONGO_USERNAME
const password = process.env.DB_MONGO_PASSWORD
const hostname = process.env.DB_MONGO_HOSTNAME
const port = process.env.DB_MONGO_PORT
let datasource = null;
export default () => {
if (!datasource) {
const uri = `mongodb://${hostname}:${port}/${database}`;
const options = {
user: username,
pass: password,
useNewUrlParser: true,
};
const connection = mongoose.createConnection(uri, options);
connection.Promise = global.Promise;
const models = [];
const modelNotifications = model(connection, mongoose);
models[modelNotifications.modelName] = modelNotifications;
datasource = {
connection,
mongoose,
models,
};
}
return datasource;
};
Consultando e atualizando o MongoDB
Crie o arquivo service.js em src e adicione o script a seguir.
O que fizemos foi criar dois métodos. O getAllToSend retornando a lista de notificações pendentes, limitando a quantidade de itens e ordenando pela data de criação. O updateToSended só atualiza a notificação.
export default (model) => {
const service = {
getAllToSend(amount) {
return new Promise((resolve, reject) => {
model.find({ sended: false }).sort({ createdAt: 'asc' }).limit(amount)
.then(items => {
if (items) { resolve(items) }
resolve();
})
.catch(err => {
reject(err)
})
})
},
updateToSended(id) {
return new Promise((resolve, reject) => {
model.findById(id)
.then(notification => {
const update = notification;
update.sended = true;
update.sendedAt = Date.now()
update.save()
resolve()
})
.catch(err => {
reject(err)
})
})
}
}
return service;
}
Enviando o SMS com a API SNS
Adicione o arquivo sms.js em /src com o script a seguir.
O que estamos fazendo é publicar nosso SMS no Simple Notification Service (SNS). Você precisa definir a região que vai utilizar, instanciar o serviço passando a versão da API e adicionar o atributo DefaultSMSType com o valor “Transactional”.
O conteúdo do SMS é um objeto com os parâmetros PhoneNumber, Message e MessageStructure. Com isto pronto, você chama o método publish.
import AWS from 'aws-sdk'
AWS.config.update({ region: 'us-west-2' })
export default () => {
const sms = {
send(notification) {
return new Promise(async(resolve, reject) => {
const sns = new AWS.SNS({ apiVersion: '2010-03-31' })
sns.setSMSAttributes({
attributes: {
DefaultSMSType: 'Transactional'
},
function(error) {
if (error) reject(error)
}
})
const params = {
PhoneNumber: notification.to,
Message: notification.body,
MessageStructure: 'string'
}
sns.publish(params, (err, data) => {
if (err) { reject(err) }
resolve()
})
})
}
}
return sms;
}
Unindo todos os eventos
Agora vamos criar o index.js na raiz da app.
Neste arquivo vamos criar um método assíncrono para chamar a função getAllToSend a cada 5 segundos e publicar o SMS na fila do SNS.
import datasource from './util/datasource'
import sms from './src/sms'
import service from './src/service'
import interval from 'interval-promise'
const db = datasource()
const model = db.models.notifications
const controller = service(model)
async function main() {
try {
const notifications = await controller.getAllToSend(5);
if (notifications !== undefined && notifications.length > 0) {
console.log(`Sending ${notifications.length} SMS`)
for (let index = 0; index < notifications.length; index++) {
const notification = notifications[index];
await sms().send(notification)
.then(() => {
controller.updateToSended(notification.id)
})
.catch(err => {
console.log('Could not send.')
console.error(err)
})
}
} else {
console.log('No SMS to send.')
}
} catch (error) {
console.error(error)
}
}
interval(async () => {
await main()
}, 5000)
Você pode alterar o valor passado no método getAllToSend para a quantidade que preferir e também o intervalo de 5 segundos.
No exemplo eu peguei as últimas 5 notificações, percorro o array caso tenha algum item e chamo o evento send(), função criada no src/sms. Caso o envio aconteça com sucesso, atualizo a notificação, caso contrário apenas exibo no meu terminal. Nesta exception você pode registrar um log ou adaptar ao que fizer mais sentido para seu contexto.
Agora vamos finalizar, adicione o script para start da sua app no package.json.
"scripts": {
"start": "babel-node index.js"
},
Pronto!
Agora é só digitar npm start no seu terminal e ver sua app rodar.
Este tutorial é um micro-serviço para rodar em qualquer ambiente nodejs. Você precisa apenas adicionar itens na fila de envio e deixar em execução. Embora este script apenas envie uma mensagem transacional, você pode customizar. Confira a documentação na AWS e adapte a sua necessidade.
O script não tem regras de negócio, é apenas um sender, a checagem do código ou a criação da mensagem é responsabilidade da sua aplicação. Você apenas vai se conectar no mongo e adicionar o item na fila.
Este micro-serviço pode evoluir para uma function serverless, na AWS você tem Lambda Functions, além disso pode integrar com o SQS e Cloud Watch. Seria necessário fazer algumas adaptações, mas o resultado seria ainda mais eficiente.
Espero ter ajudado, o código fonte deste script está neste repositório, qualquer dúvida ou sugestão deixa nos comentários. 🙂