Pular para conteúdo

AWS SNS - Notificações Pub/Sub

O que é este Node?

O AWS SNS é o node responsável por enviar notificações em massa via Amazon SNS (Simple Notification Service), permitindo publicar mensagens para múltiplos assinantes, enviar SMS e gerenciar tópicos.

Por que este Node existe?

Sistemas precisam notificar múltiplos componentes simultaneamente. O AWS SNS existe para:

  1. Broadcasting: Enviar uma mensagem para múltiplos destinos ao mesmo tempo
  2. Fan-out pattern: Disparar múltiplas ações a partir de um evento
  3. SMS em massa: Enviar mensagens SMS para telefones
  4. Push notifications: Notificar aplicativos mobile
  5. Integração multi-canal: Email, SMS, HTTP, Lambda, SQS simultaneamente
  6. Desacoplamento: Publicadores não precisam conhecer assinantes

Como funciona internamente?

Quando o AWS SNS é executado, o sistema:

  1. Autentica usando accessKeyId e secretAccessKey
  2. Cria cliente SNS configurado para região especificada
  3. Executa operação conforme especificado (publish, subscribe, createTopic, sendSMS)
  4. Distribui mensagem para todos os assinantes do tópico
  5. Retorna resultado com messageId ou subscriptionArn
  6. Se erro: Lança exceção com detalhes

Código interno (aws-executors.service.ts:161-222):

async executeAWSSNS(data: any, variables: Record<string, any>): Promise<any> {
  try {
    this.logger.log('🔔 [AWS SNS] Executing operation');

    const sns = new AWS.SNS({
      accessKeyId: data.accessKeyId,
      secretAccessKey: data.secretAccessKey,
      region: data.region,
    });

    switch (data.operation) {
      case 'publish':
        const publishResult = await sns.publish({
          TopicArn: data.topicArn,
          Message: data.message,
          Subject: data.subject,
          MessageAttributes: data.messageAttributes,
        }).promise();
        return {
          success: true,
          messageId: publishResult.MessageId,
        };

      case 'subscribe':
        const subscribeResult = await sns.subscribe({
          TopicArn: data.topicArn,
          Protocol: data.protocol,
          Endpoint: data.endpoint,
        }).promise();
        return {
          success: true,
          subscriptionArn: subscribeResult.SubscriptionArn,
        };

      case 'createTopic':
        const createTopicResult = await sns.createTopic({
          Name: data.topicName,
          Attributes: data.attributes,
        }).promise();
        return {
          success: true,
          topicArn: createTopicResult.TopicArn,
        };

      case 'sendSMS':
        const smsResult = await sns.publish({
          PhoneNumber: data.phoneNumber,
          Message: data.message,
        }).promise();
        return {
          success: true,
          messageId: smsResult.MessageId,
        };

      default:
        throw new Error(`Unknown SNS operation: ${data.operation}`);
    }
  } catch (error) {
    this.logger.error('AWS SNS execution error:', error);
    throw error;
  }
}

Quando você DEVE usar este Node?

Use AWS SNS quando precisar de notificações para múltiplos destinos:

Casos de uso:

  1. Alertas críticos: Notificar equipe via SMS, email e Slack simultaneamente
  2. Eventos de negócio: Disparar múltiplas ações quando pedido é criado
  3. SMS transacional: Enviar códigos de verificação, confirmações
  4. Push notifications: Notificar apps mobile sobre eventos
  5. Webhook fan-out: Chamar múltiplas APIs quando algo acontece
  6. Monitoramento: Alertas de CloudWatch para múltiplos canais

Quando NÃO usar AWS SNS:

  • Fila de trabalho: Use SQS (SNS não garante ordem nem reprocessamento)
  • Mensagem única: Use SES para email ou direct SMS
  • Armazenamento: Use DynamoDB ou S3
  • Processamento em batch: Use SQS + Lambda

Parâmetros Detalhados

accessKeyId (string, obrigatório)

O que é: Chave de acesso AWS IAM para autenticação.

Segurança: NUNCA exponha em código! Use variáveis de ambiente.

secretAccessKey (string, obrigatório)

O que é: Chave secreta AWS IAM correspondente ao accessKeyId.

Segurança: NUNCA exponha em código! Use variáveis de ambiente.

region (string, obrigatório)

O que é: Região AWS onde o tópico está localizado.

Exemplos: us-east-1, us-west-2, sa-east-1 (São Paulo)

operation (string, obrigatório)

O que é: Operação a ser executada.

Valores possíveis: - publish: Publicar mensagem em tópico - subscribe: Inscrever endpoint em tópico - createTopic: Criar novo tópico - sendSMS: Enviar SMS direto (sem tópico)

topicArn (string, obrigatório para publish/subscribe)

O que é: ARN (Amazon Resource Name) do tópico SNS.

Formato: arn:aws:sns:{region}:{account-id}:{topic-name}

Flow completo para testar publish:

{
  "name": "Teste AWS SNS - Publish",
  "nodes": [
    {
      "id": "start_1",
      "type": "start",
      "position": { "x": 100, "y": 100 },
      "data": { "label": "Início" }
    },
    {
      "id": "variable_1",
      "type": "variable",
      "position": { "x": 300, "y": 100 },
      "data": {
        "label": "Dados do Alerta",
        "parameters": {
          "assignments": [
            {
              "variable": "alerta",
              "value": {
                "tipo": "Erro Crítico",
                "sistema": "API de Pagamentos",
                "mensagem": "Taxa de erro acima de 5%",
                "timestamp": "{{$now}}"
              }
            }
          ]
        }
      }
    },
    {
      "id": "sns_1",
      "type": "aws_sns",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Notificar Equipe",
        "parameters": {
          "accessKeyId": "{{AWS_ACCESS_KEY}}",
          "secretAccessKey": "{{AWS_SECRET_KEY}}",
          "region": "sa-east-1",
          "operation": "publish",
          "topicArn": "arn:aws:sns:sa-east-1:123456789:alertas-criticos",
          "subject": "🚨 ALERTA: {{alerta.tipo}}",
          "message": "Sistema: {{alerta.sistema}}\n{{alerta.mensagem}}\n\nTimestamp: {{alerta.timestamp}}"
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 700, "y": 100 },
      "data": {
        "label": "Confirmar",
        "parameters": {
          "message": "✅ Alerta enviado para equipe!\nMessage ID: {{messageId}}"
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 900, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "variable_1" },
    { "source": "variable_1", "target": "sns_1" },
    { "source": "sns_1", "target": "message_1" },
    { "source": "message_1", "target": "end_1" }
  ]
}

Teste: Mensagem é publicada no tópico e todos os assinantes (email, SMS, Lambda, etc.) recebem.

message (string, obrigatório)

O que é: Conteúdo da mensagem a ser publicada/enviada.

Formato: String de texto (pode incluir JSON se assinantes processarem)

subject (string, opcional para publish)

O que é: Assunto da mensagem (usado em notificações por email).

Limite: 100 caracteres

phoneNumber (string, obrigatório para sendSMS)

O que é: Número de telefone no formato internacional.

Formato: +55119xxxxxxxx (+ código país + DDD + número)

Flow completo para testar sendSMS:

{
  "name": "Teste AWS SNS - Send SMS",
  "nodes": [
    {
      "id": "start_1",
      "type": "start",
      "position": { "x": 100, "y": 100 },
      "data": { "label": "Início" }
    },
    {
      "id": "input_1",
      "type": "input",
      "position": { "x": 300, "y": 100 },
      "data": {
        "label": "Pedir Telefone",
        "parameters": {
          "message": "Digite seu telefone com DDD (ex: 11987654321):",
          "variable": "telefone"
        }
      }
    },
    {
      "id": "variable_1",
      "type": "variable",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Formatar Número",
        "parameters": {
          "assignments": [
            {
              "variable": "telefoneInternacional",
              "value": "+55{{telefone}}"
            },
            {
              "variable": "codigoVerificacao",
              "value": "{{$random(100000,999999)}}"
            }
          ]
        }
      }
    },
    {
      "id": "sns_1",
      "type": "aws_sns",
      "position": { "x": 700, "y": 100 },
      "data": {
        "label": "Enviar SMS",
        "parameters": {
          "accessKeyId": "{{AWS_ACCESS_KEY}}",
          "secretAccessKey": "{{AWS_SECRET_KEY}}",
          "region": "sa-east-1",
          "operation": "sendSMS",
          "phoneNumber": "{{telefoneInternacional}}",
          "message": "Seu código de verificação é: {{codigoVerificacao}}\n\nNão compartilhe este código."
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 900, "y": 100 },
      "data": {
        "label": "Confirmar",
        "parameters": {
          "message": "📱 SMS enviado para {{telefoneInternacional}}!\n\nCódigo: {{codigoVerificacao}}\nMessage ID: {{messageId}}"
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 1100, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "input_1" },
    { "source": "input_1", "target": "variable_1" },
    { "source": "variable_1", "target": "sns_1" },
    { "source": "sns_1", "target": "message_1" },
    { "source": "message_1", "target": "end_1" }
  ]
}

Teste: SMS é enviado para número especificado.

protocol (string, obrigatório para subscribe)

O que é: Protocolo do endpoint que receberá notificações.

Valores possíveis: - email: Email comum - email-json: Email com JSON - sms: SMS - http: Webhook HTTP - https: Webhook HTTPS - lambda: Função Lambda - sqs: Fila SQS - application: Mobile push

endpoint (string, obrigatório para subscribe)

O que é: Destino que receberá as notificações.

Exemplos: - Email: jose@empresa.com.br - HTTP: https://api.empresa.com.br/webhook/sns - Lambda: arn:aws:lambda:sa-east-1:123456789:function:processar-notificacao - SQS: arn:aws:sqs:sa-east-1:123456789:minha-fila

Flow completo para testar subscribe:

{
  "name": "Teste AWS SNS - Subscribe",
  "nodes": [
    {
      "id": "start_1",
      "type": "start",
      "position": { "x": 100, "y": 100 },
      "data": { "label": "Início" }
    },
    {
      "id": "input_1",
      "type": "input",
      "position": { "x": 300, "y": 100 },
      "data": {
        "label": "Pedir Email",
        "parameters": {
          "message": "Digite seu email para receber alertas:",
          "variable": "email"
        }
      }
    },
    {
      "id": "sns_1",
      "type": "aws_sns",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Inscrever Email",
        "parameters": {
          "accessKeyId": "{{AWS_ACCESS_KEY}}",
          "secretAccessKey": "{{AWS_SECRET_KEY}}",
          "region": "sa-east-1",
          "operation": "subscribe",
          "topicArn": "arn:aws:sns:sa-east-1:123456789:alertas-criticos",
          "protocol": "email",
          "endpoint": "{{email}}"
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 700, "y": 100 },
      "data": {
        "label": "Confirmar",
        "parameters": {
          "message": "✅ Email {{email}} inscrito no tópico de alertas!\n\nVerifique sua caixa de entrada para confirmar a inscrição.\n\nSubscription ARN: {{subscriptionArn}}"
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 900, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "input_1" },
    { "source": "input_1", "target": "sns_1" },
    { "source": "sns_1", "target": "message_1" },
    { "source": "message_1", "target": "end_1" }
  ]
}

Teste: Email recebe confirmação de inscrição no tópico.

topicName (string, obrigatório para createTopic)

O que é: Nome do novo tópico a ser criado.

Regras: Letras, números, hífens e underscores. Máximo 256 caracteres.

Parâmetros

Campo Tipo Obrigatório Descrição
accessKeyId string Sim Chave de acesso AWS IAM
secretAccessKey string Sim Chave secreta AWS IAM
region string Sim Região AWS (ex: sa-east-1)
operation string Sim publish, subscribe, createTopic, sendSMS
topicArn string Condicional ARN do tópico (publish/subscribe)
message string Sim Conteúdo da mensagem
subject string Não Assunto (usado em email)
phoneNumber string Condicional Telefone internacional (sendSMS)
protocol string Condicional Protocolo do assinante (subscribe)
endpoint string Condicional Destino da notificação (subscribe)
topicName string Condicional Nome do tópico (createTopic)
messageAttributes object Não Metadados customizados
attributes object Não Configurações do tópico/assinatura

Exemplo 1: Sistema de Alertas Multi-Canal

Objetivo: Enviar alertas críticos para SMS, email e Lambda simultaneamente

JSON para Importar

{
  "name": "AWS SNS - Alertas Multi-Canal",
  "nodes": [
    {
      "id": "start_1",
      "type": "start",
      "position": { "x": 100, "y": 100 },
      "data": { "label": "Início" }
    },
    {
      "id": "input_1",
      "type": "input",
      "position": { "x": 300, "y": 100 },
      "data": {
        "label": "Tipo de Alerta",
        "parameters": {
          "message": "Qual tipo de alerta?\n1 - CPU Alta\n2 - Disco Cheio\n3 - API Down",
          "variable": "tipoAlerta"
        }
      }
    },
    {
      "id": "switch_1",
      "type": "switch",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Identificar Alerta",
        "parameters": {
          "variable": "tipoAlerta",
          "cases": [
            {
              "value": "1",
              "nextNode": "var_cpu"
            },
            {
              "value": "2",
              "nextNode": "var_disco"
            },
            {
              "value": "3",
              "nextNode": "var_api"
            }
          ]
        }
      }
    },
    {
      "id": "var_cpu",
      "type": "variable",
      "position": { "x": 700, "y": 50 },
      "data": {
        "label": "Alerta CPU",
        "parameters": {
          "assignments": [
            {
              "variable": "alertaMensagem",
              "value": "🚨 CPU acima de 90% por 5 minutos!\n\nServidor: prod-api-1\nCPU Atual: 94%\nAção Recomendada: Investigar processos"
            }
          ]
        }
      }
    },
    {
      "id": "var_disco",
      "type": "variable",
      "position": { "x": 700, "y": 100 },
      "data": {
        "label": "Alerta Disco",
        "parameters": {
          "assignments": [
            {
              "variable": "alertaMensagem",
              "value": "💾 Disco com 95% de uso!\n\nServidor: prod-db-1\nEspaço Livre: 2GB\nAção Recomendada: Limpar logs antigos"
            }
          ]
        }
      }
    },
    {
      "id": "var_api",
      "type": "variable",
      "position": { "x": 700, "y": 150 },
      "data": {
        "label": "Alerta API",
        "parameters": {
          "assignments": [
            {
              "variable": "alertaMensagem",
              "value": "⚠️ API não responde!\n\nEndpoint: /api/pagamentos\nStatus: Timeout após 30s\nAção Recomendada: Verificar logs e reiniciar"
            }
          ]
        }
      }
    },
    {
      "id": "sns_1",
      "type": "aws_sns",
      "position": { "x": 900, "y": 100 },
      "data": {
        "label": "Notificar Todos",
        "parameters": {
          "accessKeyId": "{{AWS_ACCESS_KEY}}",
          "secretAccessKey": "{{AWS_SECRET_KEY}}",
          "region": "sa-east-1",
          "operation": "publish",
          "topicArn": "arn:aws:sns:sa-east-1:123456789:alertas-infraestrutura",
          "subject": "🚨 ALERTA CRÍTICO - Infraestrutura",
          "message": "{{alertaMensagem}}\n\nTimestamp: {{$now}}"
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 1100, "y": 100 },
      "data": {
        "label": "Confirmar",
        "parameters": {
          "message": "✅ Alerta enviado para TODOS os canais!\n\n📧 Email\n📱 SMS\n⚡ Lambda\n\nEquipe notificada!"
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 1300, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "input_1" },
    { "source": "input_1", "target": "switch_1" },
    { "source": "var_cpu", "target": "sns_1" },
    { "source": "var_disco", "target": "sns_1" },
    { "source": "var_api", "target": "sns_1" },
    { "source": "sns_1", "target": "message_1" },
    { "source": "message_1", "target": "end_1" }
  ]
}

Saída esperada:

Sistema: Qual tipo de alerta?
1 - CPU Alta
2 - Disco Cheio
3 - API Down
Usuário: 1
Sistema: ✅ Alerta enviado para TODOS os canais!

📧 Email
📱 SMS
⚡ Lambda

Equipe notificada!

Exemplo 2: Sistema de Verificação 2FA via SMS

Objetivo: Enviar código de verificação por SMS usando SNS

JSON para Importar

{
  "name": "AWS SNS - 2FA SMS",
  "nodes": [
    {
      "id": "start_1",
      "type": "start",
      "position": { "x": 100, "y": 100 },
      "data": { "label": "Início" }
    },
    {
      "id": "phone_1",
      "type": "phone",
      "position": { "x": 300, "y": 100 },
      "data": {
        "label": "Solicitar Telefone",
        "parameters": {
          "message": "Digite seu celular com DDD:",
          "variable": "telefone"
        }
      }
    },
    {
      "id": "variable_1",
      "type": "variable",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Gerar Código",
        "parameters": {
          "assignments": [
            {
              "variable": "codigo2FA",
              "value": "{{$random(100000,999999)}}"
            },
            {
              "variable": "telefoneInternacional",
              "value": "+55{{telefone}}"
            }
          ]
        }
      }
    },
    {
      "id": "sns_sms",
      "type": "aws_sns",
      "position": { "x": 700, "y": 100 },
      "data": {
        "label": "Enviar Código SMS",
        "parameters": {
          "accessKeyId": "{{AWS_ACCESS_KEY}}",
          "secretAccessKey": "{{AWS_SECRET_KEY}}",
          "region": "sa-east-1",
          "operation": "sendSMS",
          "phoneNumber": "{{telefoneInternacional}}",
          "message": "Seu código de verificação Lumina:\n\n{{codigo2FA}}\n\nVálido por 5 minutos.\nNão compartilhe este código."
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 900, "y": 100 },
      "data": {
        "label": "Informar",
        "parameters": {
          "message": "📱 Código enviado para {{telefone}}!\n\nDigite o código de 6 dígitos:"
        }
      }
    },
    {
      "id": "number_1",
      "type": "number",
      "position": { "x": 1100, "y": 100 },
      "data": {
        "label": "Receber Código",
        "parameters": {
          "message": "Digite o código:",
          "variable": "codigoDigitado",
          "min": 100000,
          "max": 999999
        }
      }
    },
    {
      "id": "condition_1",
      "type": "condition",
      "position": { "x": 1300, "y": 100 },
      "data": {
        "label": "Validar",
        "parameters": {
          "conditions": [
            {
              "variable": "codigoDigitado",
              "operator": "==",
              "value": "{{codigo2FA}}",
              "nextNode": "message_success"
            }
          ],
          "defaultNextNode": "message_error"
        }
      }
    },
    {
      "id": "message_success",
      "type": "message",
      "position": { "x": 1500, "y": 50 },
      "data": {
        "label": "Sucesso",
        "parameters": {
          "message": "✅ Código correto!\n\nVerificação 2FA concluída com sucesso."
        }
      }
    },
    {
      "id": "message_error",
      "type": "message",
      "position": { "x": 1500, "y": 150 },
      "data": {
        "label": "Erro",
        "parameters": {
          "message": "❌ Código incorreto!\n\nTente novamente."
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 1700, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "phone_1" },
    { "source": "phone_1", "target": "variable_1" },
    { "source": "variable_1", "target": "sns_sms" },
    { "source": "sns_sms", "target": "message_1" },
    { "source": "message_1", "target": "number_1" },
    { "source": "number_1", "target": "condition_1" },
    { "source": "message_success", "target": "end_1" },
    { "source": "message_error", "target": "end_1" }
  ]
}

Saída esperada:

Sistema: Digite seu celular com DDD:
Usuário: 11987654321
Sistema: 📱 Código enviado para 11987654321!

Digite o código de 6 dígitos:
Usuário: 847392
Sistema: ✅ Código correto!

Verificação 2FA concluída com sucesso.

Resposta do Node

Publish:

{
  "success": true,
  "messageId": "abc123-def456-ghi789"
}

Subscribe:

{
  "success": true,
  "subscriptionArn": "arn:aws:sns:sa-east-1:123456789:alertas:abc123-def456"
}

CreateTopic:

{
  "success": true,
  "topicArn": "arn:aws:sns:sa-east-1:123456789:meu-topico"
}

SendSMS:

{
  "success": true,
  "messageId": "sms-abc123-def456"
}

Boas Práticas

SIM:

  • Use tópicos para notificar múltiplos destinos
  • Configure filtros de mensagem para assinantes específicos
  • Use messageAttributes para metadados estruturados
  • Implemente idempotência (MessageId para deduplicação)
  • Configure Dead Letter Queue (DLQ) para falhas
  • Use FIFO topics quando ordem for crítica
  • Monitore métricas (NumberOfMessagesPublished, NumberOfNotificationsFailed)

NÃO:

  • Não use SNS para filas de trabalho (use SQS)
  • Não envie mensagens > 256KB
  • Não exponha credenciais AWS em código
  • Não confie em ordem de entrega (use FIFO se necessário)
  • Não ignore erros de publicação

Dicas

💡 SMS Sandbox: Conta nova AWS precisa verificar números antes de enviar SMS 💡 Custos: Calcule custos por mensagem (varia por protocolo e região) 💡 Fan-out: Combine SNS → SQS para processamento paralelo confiável 💡 Filtros: Use subscription filter policies para routing inteligente 💡 Retry: SNS retenta automaticamente entregas com falha 💡 Mobile Push: Configure plataformas (APNS, FCM) antes de usar 💡 Cross-region: SNS não replica entre regiões automaticamente

Próximo Node

AWS SQS - Fila de mensagens → AWS DYNAMODB - Banco de dados NoSQL → AWS LAMBDA - Executar funções serverless