Starting with the data model
Every good platform starts with a well-reasoned data model. Getting the schema right early prevents painful migrations later. The core entities in BumpEvents are straightforward: User, Event, Category, and Booking — each with clear ownership and relation constraints.
Events belong to a creator, belong to a category, and can have many bookings. Bookings belong to a user and an event. That's the whole domain, and keeping it that shape resists the temptation to over-engineer.
Authentication architecture
Auth is handled entirely server-side using NextAuth v5 with a credentials provider and a database adapter. Sessions are stored in the database, not in JWTs, which means session invalidation works correctly and there is no token leakage risk.
The auth() helper function is the key primitive. It works identically in Server Components, Route Handlers, and middleware — no context providers, no prop drilling. Each protected page calls it at the top:
const session = await auth()
if (!session) redirect("/login")
Next.js automatically deduplicates these calls within a single request, so there is no performance cost to calling auth() in multiple places on the same page.
Page architecture — Server Components first
Every page in BumpEvents is a React Server Component by default. Data fetching happens directly in the component, the result is streamed as HTML, and the client receives a fully-rendered page with zero loading states for initial data.
Client components are used only where the browser is genuinely required: the filter drawer, the RSVP button, the image upload widget, and modals. The boundary between server and client is explicit and intentional.
Discovery and filtering
The /discover page is the most architecturally interesting. Filters (category, city, date, format) are driven entirely by URL search parameters. This means:
- Filters are shareable and bookmarkable
- No client-side state synchronisation required
- The server reads searchParams and builds a typed Prisma query directly
The page uses ISR with a 60-second revalidation window. Fresh enough for an event platform, cached enough to handle traffic spikes without hitting the database on every request.
Image upload pipeline
Uploaded images go through a multi-step pipeline before being stored:
1. Session check — unauthenticated requests are rejected immediately
2. File size cap — requests over 5 MB are rejected before reading the body
3. MIME type validation — the file buffer is inspected for magic bytes, not just the file extension
4. Processing via sharp — image is resized, converted to WebP, and EXIF metadata is stripped
5. UUID filename — stored under a cryptographically random name to prevent enumeration
Security layers
Security is not a single feature — it is a series of overlapping layers. BumpEvents applies rate limiting on all sensitive endpoints, HTTP security headers on every response (CSP, HSTS, X-Frame-Options), and input validation via Zod at every API boundary.
Access control is enforced server-side: only an event's creator can edit or delete it. The check happens in the Route Handler before any mutation runs.
The combination of these layers means there is no single point of failure.