The backend for DLSU CSO LEAP event websites. Leapify ships as:
| Mode | When to use |
|---|---|
| Standalone Worker | You want a zero-code deploy — just configure secrets and run wrangler deploy |
| npm module | You have your own Worker / Next.js / SvelteKit app and want to mount Leapify inside it |
Both modes share exactly the same routes, auth, caching, and email logic.
No code required. Clone, configure, deploy.
git clone https://github.com/access-dlsu/leapify
cd leapify
npm install
cp wrangler.toml.example wrangler.tomlEdit wrangler.toml with your D1 database ID and KV namespace ID, then set your secrets:
# CORS — comma-separated allowed origins
wrangler secret put ALLOWED_ORIGINS
# → "https://yoursite.com,https://www.yoursite.com"
# Firebase
wrangler secret put FIREBASE_PROJECT_ID
wrangler secret put FIREBASE_WEB_API_KEY
# Google Forms
wrangler secret put GFORMS_SERVICE_ACCOUNT_JSON
wrangler secret put GFORMS_WEBHOOK_SECRET
# Contentful
wrangler secret put CONTENTFUL_SPACE_ID
wrangler secret put CONTENTFUL_ACCESS_TOKEN
wrangler secret put CONTENTFUL_ENVIRONMENT
# Amazon SES (primary email)
wrangler secret put SES_REGION
wrangler secret put SES_ACCESS_KEY_ID
wrangler secret put SES_SECRET_ACCESS_KEY
wrangler secret put SES_FROM_ADDRESS
# Resend (optional email fallback — omit to disable)
wrangler secret put RESEND_API_KEY
wrangler secret put RESEND_FROM_ADDRESS
# Internal route security
wrangler secret put INTERNAL_API_SECRETBuild and deploy:
npm run build
wrangler deployVerify:
curl https://your-worker.your-subdomain.workers.dev/health
# → { "status": "ok", "providers": { "ses": true, "resend": false } }
wrangler.toml.examplehas full comments for every binding. See .dev.vars.example for local dev.
Install into your own project and mount the handler:
npm install leapifyServer layer (Cloudflare Worker / Pages Function):
import { createLeapify } from 'leapify'
export default createLeapify({
allowedOrigins: ['https://yourdomain.com'],
})Browser / client components:
import { createLeapifyClient, getLeapifyToken } from 'leapify/client'
import { auth } from '@/lib/firebase'
const api = createLeapifyClient(
process.env.NEXT_PUBLIC_API_URL!,
() => getLeapifyToken(auth.currentUser),
)
const events = await api.getEvents() // → LeapEvent[]
const config = await api.getConfig() // → SiteConfig
const me = await api.getMe() // → UserProfile | nullFull setup and per-endpoint examples → Frontend Integration Guide
Leapify exposes /api/ endpoints that your frontend consumes. The backend handles Firebase Auth, Cloudflare D1 (database), Contentful (CMS), and transactional email (Amazon SES primary / Resend fallback) — all credentials live in .env / wrangler.toml, never in browser code.
/api/*— restricted to your site's origin (allowedOriginsCORS gate)/health— publicly accessible for uptime monitoring; reports which email providers are configured
| Layer | Technology | Purpose |
|---|---|---|
| Framework | Hono | Edge-optimized, <1ms cold start |
| ORM | Drizzle + D1 | Type-safe SQL on Cloudflare's serverless SQLite |
| Validation | Zod | Runtime schema validation |
| Cache | Cloudflare KV + CDN | JWT caching, slot availability, edge response cache |
| CMS | Contentful | Headless CMS for all event/FAQ/site content |
| Amazon SES + CF Queues | Async transactional email — SES primary, Resend fallback | |
| Auth | Firebase Auth | Google Sign-In, restricted to @dlsu.edu.ph |
| Testing | Vitest + CF pool | Unit + integration tests on real CF runtime |
| Service | Quota | Role |
|---|---|---|
| Cloudflare D1 | 5M reads/day, 100k writes/day (Pro) | Primary relational database |
| Cloudflare KV | 10M reads/day (Pro) | JWT cache + slot availability |
| Cloudflare Queues | — | Async job dispatch |
| Contentful | 100k API calls/mo, 50GB CDN (Free) | All CMS content |
| Firebase Auth | Usage-based | Identity + JWT issuance |
| Amazon SES | ~62k emails/mo free tier; $0.10/1k after | Primary transactional email |
| Resend | 50k emails/mo (Pro) | Fallback transactional email |
Three Cloudflare cache tiers keep D1/Firebase within quota at 30k concurrent users:
| Tier | TTL | What it caches |
|---|---|---|
| CF CDN Edge | 7 days | GET /events list (ETag-gated) |
| CF KV | 3,600s | Firebase JWT tokens |
| CF KV | 5s | Slot availability per event |
All users must sign in with @dlsu.edu.ph Google accounts. The backend rejects any other domain with 403 DOMAIN_RESTRICTED.
| Role | Token | Access |
|---|---|---|
guest |
None | Public endpoints only |
user |
Firebase JWT (@dlsu.edu.ph) |
Protected user endpoints |
admin |
JWT + admin: true claim |
Admin mutation endpoints |
Firebase Auth (~50 QPS limit): JWTs are cached in Cloudflare KV with a 3,600s TTL — >90% of requests skip Firebase entirely.
Email (200–500ms send latency): All email jobs are pushed to a Cloudflare Queue. The HTTP response returns immediately (202 Accepted); the consumer worker sends via Amazon SES (primary). If SES returns a non-retryable error and RESEND_API_KEY is set, the job is retried via Resend before landing in the DLQ. If RESEND_API_KEY is not set, fallback is skipped and the job goes straight to the DLQ after SES exhausts its retries.
Why SES? SES supports 14 emails/second by default (vs. Resend's 10/s), has a native suppression list, and is significantly cheaper at high volumes ($0.10 per 1k). Resend is an optional fallback — configure it for a deliverability dashboard and better incident visibility, or omit
RESEND_API_KEYto run SES-only.
Leapify targets Cloudflare Workers (standalone) or Cloudflare Pages Functions (colocated). Configure wrangler.toml with D1, KV, Queue bindings and Worker secrets.
Standalone:
npm run build && wrangler deploynpm module (inside your own repo):
wrangler deploy # your consumer worker that imports leapifySee wrangler.toml.example and .dev.vars.example for the full config shape.