Madrasa Manager
An all-in-one administrative platform built for Islamic schools to manage students, finances, and staff.
The Challenge
The core engineering challenge was designing a multi-tenant architecture that feels single-tenant to each user. The solution pushes tenancy enforcement down to Supabase Row Level Security policies at the database layer, so unauthorized access is structurally impossible. A second challenge was building a financial system that remains auditable. The solution uses parallel Promise.all() fetches on the client and a custom activity_log table that records every mutation, giving administrators a tamper-evident audit trail. Authentication needed to work seamlessly across server components, client components, and Next.js middleware simultaneously. Using Supabase SSR cookie-based session approach allowed the middleware to gate every protected route at the edge before any page component even renders.
Architectural Decisions
Multi-tenancy via Supabase RLS, not application-layer filtering
All data is scoped to a madrasa_id foreign key. Rather than filtering in every query, Supabase Row Level Security policies enforce isolation at the database level. This means even a miscoded query cannot return another school data, and adding new tables automatically inherits the tenancy model.
Direct client-side Supabase queries over a custom API layer
Instead of building a REST or tRPC API, pages query Supabase directly from the browser using the typed JS client. This eliminates an entire server layer for CRUD operations, cuts latency, and lets Supabase handle connection pooling and query optimization.
Next.js App Router with route groups for protected layout
All authenticated pages live inside a (dashboard) route group sharing a single layout with the sidebar and header. Middleware validates the Supabase session cookie on every request and redirects unauthenticated users before any React component is evaluated.
Activity log as a first-class audit table
Every write operation inserts a row into an activity_log table with the actor, entity type, action category, and a human-readable description. This was prioritized early because madrasas are accountable to donors and parents; having a built-in audit trail meant the reports page could show who did what and when.
Parallel data fetching with Promise.all on dashboard and reports
The dashboard and reports pages need 4-6 independent data sets simultaneously. Sequential awaits would stack latencies; instead, all queries fire in parallel via Promise.all(). This keeps page load times flat regardless of how many modules exist.