Magma CRM — Frontend Architecture & Real-Time Systems
Led frontend architecture for a multi-role CRM, replacing a client-facing REST layer with server-driven data access and building a real-time notification system on Supabase Realtime, while mentoring a two-person frontend team.
Contents
Executive Summary
Magma is a multi-role CRM built at Cloudmate Technologies. Work centered on the frontend architecture: replacing a conventional client-facing REST layer with Next.js Server Actions so no data-fetching endpoint was directly reachable from the browser, building a real-time notification system on Supabase Realtime without flooding the UI with unrelated re-renders, and enforcing role-based access control at the same boundary that fetches data. Also mentored a two-person frontend team through the rebuild.
Business Context
Constraints
No client-exposed data endpoints
Every REST endpoint that returned CRM data was, in principle, callable directly by anyone with the URL and a valid session. The architecture needed to close that off structurally, not by convention or endpoint-level auth checks.
Real-time without re-render storms
Supabase Realtime pushes every relevant database change to subscribed clients. Naive subscription handling meant components re-rendering on changes that had nothing to do with what they displayed.
Small team, shared architecture
The frontend was maintained by two engineers besides me. Decisions had to be simple enough to onboard onto quickly and consistent enough not to require re-deriving the pattern for every new feature.
Role-scoped data everywhere
Every view, list, and action in the CRM had to respect the current user's role. There was no single access-control checkpoint to add correctness at — it had to hold at every data-fetching boundary.
System Blueprint
Server Actions replace direct REST calls between the browser and the data layer, notifications reach the UI through scoped Supabase Realtime subscriptions instead of polling or one broad subscription, and role checks sit at the same boundary as data fetching rather than in the UI.
Frontend
Next.js application rendering role-scoped CRM views, with no direct fetch calls to data-returning API routes.
Server-Driven Data Layer
Next.js Server Actions mediate every read and write, enforcing role checks before returning or mutating data.
Real-Time Layer
Supabase Realtime subscriptions scoped per view, so a component only re-renders on changes relevant to what it displays.
Data Layer
Supabase-backed datastore holding CRM records, roles, and permissions.
Architecture
Server Actions as the only data boundary
Every CRM read and write goes through a Next.js Server Action rather than a REST route the client can call directly, so there is no data-fetching URL to expose, guess, or hit with an unauthorized request.
Scoped real-time subscriptions
Instead of one broad Supabase Realtime subscription per session, components subscribe only to the tables and filters relevant to what they render, so a change in one part of the CRM doesn't re-render views that have nothing to do with it.
Role-based access control at the data boundary
Role checks live inside the same Server Actions that fetch and mutate data, so access control can't be bypassed by a component that forgets to check a role before rendering — the boundary that returns data is the boundary that enforces the role.
Team-legible patterns
Server Actions, subscription scoping, and role checks were structured as repeatable patterns rather than one-off implementations, so a two-person frontend team could extend the CRM without re-deriving the architecture for every new feature.
Engineering Decisions
Server Actions vs. a client-facing REST API
- Keep the REST API and rely on endpoint-level auth checks.
- Move all data access behind Next.js Server Actions, invoked only from the application's own server-rendered flows.
Broad vs. scoped Supabase Realtime subscriptions
- Keep one subscription per session covering all CRM tables.
- Scope subscriptions per component to only the tables and filters that component actually renders.
Production Stories
This section is planned but not yet documented.
Lessons Learned
Removing a client-exposed API surface is a stronger security property than auth-checking every endpoint on that surface — the endpoint that doesn't exist can't be misused.
Real-time features need subscription scoping designed up front; retrofitting it after every component has already subscribed broadly is a much bigger refactor.
Patterns that are easy to teach are as valuable as patterns that are technically elegant, when the team maintaining them is small.
Related Notes
- The Endpoint That Doesn't Exist Can't Be Misused
Auth-checking every REST endpoint is weaker than not exposing one at all — replacing a client-facing REST layer with Next.js Server Actions closed off a class of misuse by removing the surface, not by guarding it.