/ WEB APP

GraceEvents

One platform, many churches, each with their own events, calendars, and communities.

Node.jsExpressPrismaReactViteTypeScript
GraceEvents interface
0
Tenant architecture
0
Platform targets
0
Auth standard
0
Type-safe ORM

01 / CONTEXT

Overview

THE CHALLENGE

Churches using Facebook events or paper bulletins had no way to manage registrations, track attendance, or provide a branded calendar for their congregation. Off-the-shelf event tools were generic and not built around the church's rhythm of recurring services, special events, and community gatherings.

THE SOLUTION

A multi-tenant event management platform where each church has its own isolated workspace. Staff manage events and registrations through a React web dashboard; the public sees a branded calendar page. A Flutter mobile app lets attendees browse events and RSVP directly from their phones.

THE OUTCOME

Multiple churches can run on the same infrastructure with full data isolation. The platform supports recurring events, registration caps, and waitlists, features that Facebook events don't offer.

02 / WHAT IT DOES

Key features

Multi-Tenancy

Each church gets its own isolated workspace with its own events, registrations, and branding. Tenant isolation is enforced at the database query level via Prisma middleware.

Staff Web Dashboard

A React/Vite admin dashboard for church staff to create events, manage registrations, view attendance, and export attendee lists.

Public Calendar Pages

Each church gets a public-facing calendar page embeddable on their existing website. Events show available spaces, date, time, and registration links.

Flutter Mobile App

Attendees browse events and RSVP from iOS or Android. The app uses the same REST API as the web dashboard, sharing business logic without duplication.

Registration & Waitlists

Events have configurable capacity limits. When full, new registrants are added to an automatic waitlist and notified if a spot opens.

03 / STACK

Tech decisions

Tap any item to see why it earned a place.

04 / PROCESS

How it came together

05 / HARD PARTS

Challenges & solutions

Every database query had to be scoped to the active tenant. I implemented a Prisma middleware that automatically appends a WHERE tenantId = ? condition to every query, making it impossible to accidentally return another tenant's data.

LET'S BUILD SOMETHING TOGETHER

I'm always open to discussing new projects, partnerships, or just a good idea.