SYSTEM_CASE_STUDY

Lifeline

A Bangladesh-focused PWA for finding donors by district, posting emergency blood requests, running local communities, and chatting in real time — with a volunteer/admin moderation layer.

Technologies Leveraged
Next.jsTypeScriptExpress.jsPrismaPostgreSQLSocket.IOZustandCloudflare R2PWA
specs_manifest.json
Engineering Role
Lead Full-Stack
System Scope
Social-impact PWA
Operation Scale
Division → district geo
Infra Architecture
Vercel + Docker VPS
Timeline
6 Weeks
Architecture Highlights
  • >Custom JWT access tokens plus HttpOnly refresh sessions (not a hosted auth SaaS)
  • >Hyper-local donor search across Bangladesh division, district, and upazila
  • >Feed ranking, Socket.IO chat, and Next.js rewrites that proxy API + websockets
Lifeline screenshot

Project Vision & Meaning

In Bangladesh, urgent blood requests still move through Facebook groups and forwarded phone numbers. That is slow, unscoped, and unsafe: a Dhaka post reaches people who cannot donate in time, and a stranger's number is the only coordination channel. Lifeline is a hyper-local community network — donors, requests, communities, and chat — so a request is visible to people who actually live in that district, not to an unbounded social graph.

Real-World Problems Solved

Guests can search donors by geography and blood group without an account. Members post emergency requests into a ranked feed, join local communities, log donations, and coordinate in 1:1 or community chat. Volunteers and admins moderate reports, flagged posts, and events. The frontend is a PWA on Vercel; the API runs blue-green Docker on a VPS behind Nginx, with Next rewrites so browser cookies and Socket.IO share one origin in development and production.

Challenges & Solutions

CHALLENGE_1ERR_RESOLVED

Keeping Auth and WebSockets Same-Origin Across Vercel and a VPS

Problem Context:The UI deploys to Vercel; the Express API and Socket.IO server deploy to a VPS. If the browser called the API on a different host, HttpOnly refresh cookies needed a shared parent domain, CORS preflights broke mobile networks, and Socket.IO fell back to long-polling or failed entirely. A cookie-only session also could not travel as a Bearer token for background refetch.

Engineering Solution:

Issued a short-lived JWT access token in memory and an opaque refresh token in an HttpOnly cookie. Next.js rewrites /api/v1/* and /socket.io/* to the internal backend URL so the browser talks to one origin. requireAuth accepts Bearer JWT first, then the refresh cookie. A client session gate serializes concurrent 401s through a single refresh. The Next middleware cookie is_logged_in is treated as UX routing only — authorization stays on the API.

CHALLENGE_2ERR_RESOLVED

Hyper-Local Matching Instead of a Global Donor List

Problem Context:A national donor dump is useless in an emergency. Bangladesh addressing is division → district → upazila/area, and a request in Chittagong should not rank a willing donor in Rangpur first. Naive ILIKE search across the user table ignored availability, blood group, and recency, and it could not power both the public Find Donors page and the authenticated feed.

Engineering Solution:

Stored geo on the User model (division, district, upazila, area) plus donor availability fields. Donor search filters those dimensions together with blood group. The feed scores posts (emergency, geo, engagement) into a cachedScore so ranking is not recomputed on every scroll. Blood requests are a first-class PostType with units and location, not a generic text post with a hashtag.

CHALLENGE_3ERR_RESOLVED

Realtime Chat and Push Without Blocking the Request Path

Problem Context:Blood requests expire in hours. If chat is polling, coordinators miss replies. If every notification is sent inline on the request thread, a busy feed endpoint stalls. The product needed 1:1 and community rooms, unread notifications, and optional push — without making Redis a boot requirement for every environment.

Engineering Solution:

Mounted Socket.IO on the same HTTP server as Express. Chat has REST history plus socket events for live delivery. Push uses VAPID web-push subscriptions. Background work (ranking, notifications) can run through BullMQ when Redis is present, and falls back to a sync queue driver so local and small VPS deploys do not hard-depend on Redis. Uploads go to Cloudflare R2 so avatars and post media never sit on the API container.