AI News Hub Logo

AI News Hub

Notifying Admins When Users Confirm Their Email — The Right Way with Supabase and Next.js"

DEV Community
Frank Mendez

Full blog => here Hook / Problem Statement — Why polling or signup-time notifications are wrong; email confirmation is async and outside your app's request cycle. Architecture Overview — The 6-step event chain diagram (confirmation link → auth.users.confirmed_at → Postgres trigger → public.profiles.confirmed_at → Supabase Database Webhook → Next.js API route). Why bridging via a public-schema table is necessary (Supabase webhooks can't observe auth.*). The Data Layer — The add_confirmed_at_to_profiles migration: adding the nullable confirmed_at column and the Postgres AFTER UPDATE trigger function handle_user_confirmed() that syncs it. Key insight: OLD.confirmed_at IS NULL AND NEW.confirmed_at IS NOT NULL guard. The Webhook API Route — app/api/webhooks/user-confirmed/route.ts. Cover: shared-secret verification via x-webhook-secret header (security), parsing Supabase's { type, table, record } payload, independent try/catch per notification channel, and why always returning 200 OK prevents Supabase retry storms. Notification Services — user-confirmed.ts. Resend for email (HTML body with name/email/ID/timestamp), Slack Incoming Webhook via fetch. Emphasize isolation — one failure never blocks the other. Supabase Dashboard Config — The one-time manual step: create the DB webhook pointing to the deployed route, set the header, add a confirmed_at IS NOT NULL filter to reduce noise. supabase.com Environment Variables — Table of the 4 new vars (RESEND_API_KEY, ADMIN_EMAIL, SLACK_WEBHOOK_URL, WEBHOOK_SECRET) with descriptions. What's Out of Scope (and Why) — No retry logic (intentional), no multi-admin, no notification prefs UI — acknowledge these and link to potential follow-ups. Closing — Summary of the pattern: Postgres trigger → public table → Supabase webhook → app route. Reusable for other auth events (password reset, MFA enrollment, etc.).