Building a Full-Stack Platform Backend for a US Client: Architecture Lessons
A US client needed a complete platform backend — admin panel, user dashboard, Telegram bot layer, wallet system, and webhook integrations. Here is what I built and what I learned.
Table of contents
The brief was deceptively simple: build a platform backend for a US-based online gaming operation. Admin tools, user management, automated workflows, and a payment layer.
What the brief did not mention: the system needed a Telegram bot for operational commands, a real-time webhook layer for third-party integrations, a wallet system with transaction logging, and an admin panel granular enough for a non-technical operator to run daily without engineering support.
This post covers the architecture decisions, the challenges, and what I would do differently on the next project like this.
Architecture Overview
The system split into four distinct layers:
Admin panel — Built on Next.js with Supabase Auth. Role-based access control with three levels: super admin, manager, operator. Each role sees a different navigation and different data. Managers see aggregate metrics and cannot touch user wallets. Operators can process specific transaction types but cannot export full user data.
User-facing dashboard — Separate Next.js deployment from the admin. Users see their account status, transaction history, balance, and active items. All reads go through Supabase with row-level security — each user can only query their own rows. No backend API layer needed for reads.
Telegram bot layer — The client wanted operational commands executable from Telegram without opening the admin panel. Balance lookups, status checks, and batch operations all available via bot commands. Built on Telegraf.js, connected to the same Supabase instance. Commands are rate-limited and require a per-operator token.
Webhook and API integration layer — Third-party services push events via webhook. Each webhook source has its own signature verification. Events are logged to a webhook_events table before processing, so failed processing can be retried without data loss.
The Wallet System
The most technically sensitive part. Every credit and debit is an INSERT, never an UPDATE. The current balance is always SUM(amount) WHERE user_id = X — this makes the balance auditable and makes fraud detectable. No balance field on the user record that can be edited directly.
Transaction types are an enum: deposit, withdrawal, bonus, adjustment, refund. Adjustments require a reason field and an approver ID. Every adjustment creates an audit record.
Challenges
Permissions granularity. The client had very specific ideas about who could do what, and those ideas evolved during the build. The RLS policy layer in Supabase handles per-role read restrictions well, but write permission nuance — "managers can approve refunds but not bonuses" — required application-level checks on top of RLS. Both layers now exist.
Telegram rate limits. The bot hit Telegram API rate limits during a batch operation test. Solution: queue large batches with a delay between messages, and return a job ID immediately while processing happens asynchronously.
Webhook idempotency. Third-party services retry webhooks. Processing the same event twice caused duplicate transactions in early testing. Solution: store the webhook event ID and check for duplicates before processing. ON CONFLICT (event_id) DO NOTHING in the processing step.
What I Would Do Differently
Separate the admin database user from the application database user from day one. Both ended up using the anon key with RLS during development, which made testing permission boundaries harder than it needed to be.
Write the audit logging layer before any business logic. Every write to a sensitive table should create an audit record. Adding this after the fact meant retrofitting 12 different insert paths.
Document the webhook event schema before the first integration. The first two integrations had slightly different field names for the same concepts — amount vs value, user_id vs userId. A schema document agreed on before any code would have prevented two days of debugging.
The Result
The platform went live on schedule. The client operates it daily without engineering support, which was the primary success criterion. The Telegram bot handles roughly 40% of the routine operational queries that would otherwise require opening the admin panel.
The architecture — immutable wallet transactions, role-based RLS, webhook idempotency — is holding up well under real operational load.
Resources
- —Telegraf.js — the Telegram bot framework used for the operational command layer
- —Supabase Row Level Security — how RLS handles per-role read restrictions at the database level
- —Supabase pgcrypto / Vault — encrypting sensitive fields at rest; the right approach for a multi-user system
Related Posts
- —How I Structure a Supabase + Vercel Admin Panel From Scratch — the admin panel architecture that underpins this project
- —API Key Management: Building a Central Vault for Multiple AI Providers — how sensitive credentials are managed across the platform layers
- —3 Subdomains, 1 Azure Database, 1 Vercel Frontend — the multi-layer architecture pattern applied in this project
Have a complex platform to build? Admin panels, bots, webhook layers, wallet systems — I take on technically demanding client projects. See my work or get in touch.
Get the free AI Prompt Pack + weekly frameworks
30+ tested prompts for images, captions, scripts & keywords, delivered instantly. Plus real insights on AI + marketing — no generic tips.
Want to implement this with guidance?
Santosh helps founders turn insights like this into real systems.
External Resources
Further Reading & Tools
Content Marketing Institute
Annual content marketing benchmarks — budgets, channels, and outcomes
HubSpot Marketing Blog
Data-driven marketing research, inbound strategy, and content guides
Semrush Blog
SEO, content, and digital marketing research and strategy guides
Marketing Week
UK's leading marketing news, strategy insight, and industry research