minha resposta: não precisa de WebSocket e nem de polling para notificações. use push notifications com service worker.
agora pedi pro chat elaborar pois estou com preguiça mental e ja compartilhei o meu conhecimento
obs as chaves:
'publicKey' => 'PUBLIC_KEY',
'privateKey' => 'PRIVATE_KEY',
são só chaves de assinatura de criptografia, não são referentes a nenhum serviço externo pode gerar de graça aqui usando qualquer email porque ele não importa: https://vapidkeys.com/
resposta do chat sobre push:
Boa solução, e faz sentido dentro do contexto de serverless, mas dá para evoluir isso sem cair no custo e complexidade de WebSocket.
O ponto principal aqui é que você acabou resolvendo “tempo real” com polling porque o problema foi tratado como necessidade de conexão contínua, quando na prática isso não é obrigatório na maioria dos casos de notificação.
Uma alternativa mais moderna e geralmente mais eficiente é trocar o polling por Push Notifications via Service Worker (PWA). Isso te tira do modelo de “consultar a cada X segundos” e te coloca no modelo de “ser avisado quando algo acontece”.
💡 Como isso mudaria sua arquitetura
Em vez de:
- frontend perguntando a cada 30s se existe notificação nova
- backend respondendo repetidamente com queries leves
Você passa a ter:
- backend dispara evento quando a candidatura acontece
- navegador recebe um push mesmo com a aba fechada
- service worker exibe a notificação imediatamente
🌐 Exemplo de implementação (sem WebSocket e sem polling)
1. Frontend (JS) registrando push
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js');
}
async function subscribe() {
const reg = await navigator.serviceWorker.ready;
const sub = await reg.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: 'SUA_VAPID_PUBLIC_KEY'
});
await fetch('/save-subscription.php', {
method: 'POST',
body: JSON.stringify(sub),
headers: { 'Content-Type': 'application/json' }
});
}
2. Service Worker recebendo notificação
self.addEventListener('push', event => {
const data = event.data?.json() || {};
event.waitUntil(
self.registration.showNotification(data.title || 'Nova notificação', {
body: data.message || 'Você tem uma atualização',
icon: '/icon.png'
})
);
});
3. Backend PHP salvando subscription
$input = json_decode(file_get_contents("php://input"), true);
$stmt = $pdo->prepare("
INSERT INTO push_subscriptions (endpoint, p256dh, auth)
VALUES (?, ?, ?)
");
$stmt->execute([
$input['endpoint'],
$input['keys']['p256dh'],
$input['keys']['auth']
]);
4. Backend PHP disparando notificação
use Minishlink\WebPush\WebPush;
use Minishlink\WebPush\Subscription;
$webPush = new WebPush([
'VAPID' => [
'subject' => 'mailto:admin@site.com',
'publicKey' => 'PUBLIC_KEY',
'privateKey' => 'PRIVATE_KEY',
],
]);
$subscription = Subscription::create([
'endpoint' => $endpoint,
'keys' => [
'p256dh' => $p256dh,
'auth' => $auth
]
]);
$webPush->queueNotification($subscription, json_encode([
'title' => 'Nova candidatura',
'message' => 'Um doador acabou de se candidatar à sua campanha'
]));
$webPush->flush();
⚖️ Comparando com sua abordagem
O que você fez:
- polling simples
- previsível
- fácil de debugar
- funciona bem em serverless
O que o Push adiciona:
- elimina requisições repetitivas
- reduz carga no backend
- entrega imediata real
- funciona mesmo com aba fechada
🧠 Conclusão
Seu modelo com polling é totalmente válido para MVP e até escala moderada, principalmente em Vercel serverless.
Mas se o objetivo for evoluir o BloodLink para algo mais “real time de verdade”, o próximo passo natural não é WebSocket, e sim Push via Service Worker.
Ele mantém sua arquitetura serverless intacta, só troca o mecanismo de entrega de “consulta periódica” para “evento disparado”.