A full-stack, real-time blood donation and emergency-response platform connecting hospitals, blood banks, and donors — with geo-matched donor discovery, live inventory tracking, and on-demand ambulance dispatch.
| Web app | blood-bank-finder.vercel.app |
| API | bloodbank-finder-api.onrender.com |
Hosted on free-tier infrastructure — the API spins down when idle, so the first request after inactivity can take 30–50 seconds to wake it up.
BloodBank Finder solves a real coordination problem: when a hospital needs blood urgently, who has it, how much, and how fast can a compatible donor get there? The platform models three cooperating roles — donors, hospitals/blood banks, and admins — and layers in real-time notifications, geospatial donor matching by blood-type compatibility, and an ambulance dispatch workflow for getting donors or patients where they need to be.
It ships as three coordinated client apps sharing one backend:
- A Next.js Progressive Web App for desktop and mobile browsers
- A native Expo / React Native app for iOS and Android
- An Express + PostgreSQL API that powers both, with a Socket.IO layer for real-time updates
For Donors
- Register with blood group, location, and availability
- Real-time alert the moment a compatible, in-range request opens — pushed over WebSocket, plus email and SMS, no polling
- One-tap "offer to donate" with automatic distance-to-request calculation
- Donation history and an eligibility check that enforces a minimum interval between donations
- Toggle availability for matching on/off at any time
For Hospitals & Blood Banks
- Post emergency blood requests with blood group, units needed, urgency level, and auto-expiry
- The backend automatically finds and notifies every compatible, eligible, nearby donor using a blood-type compatibility matrix combined with a Haversine-distance radius search
- Track responses live as donors offer, get confirmed, and complete donations
- Blood banks: manage live inventory per blood group, with automatic low-stock alerts
- Search available donors directly by blood group and location
- Dispatch an ambulance for a confirmed donor or for patient transport, and track it through assigned → en route → arrived → completed in real time
For Admins
- Review and verify newly registered organizations before they can post requests or appear in search
- System-wide stats overview
Cross-cutting
- Real-time updates everywhere via Socket.IO — new requests, donor offers, status changes, ambulance tracking, notifications
- Every notification is also delivered by email (Resend) and SMS (Twilio), alongside the in-app feed
- Role-based access control across donor / hospital / blood bank / admin
- Installable PWA — web manifest, custom service worker, "Add to Home Screen," cache-first static assets
- Map-based search (Leaflet / OpenStreetMap) with an adjustable search radius
- Native mobile app with full feature parity for requests, responses, ambulance dispatch, and notifications
flowchart LR
subgraph Clients
A[Next.js PWA<br/>Vercel]
B[Expo Mobile App<br/>iOS / Android]
end
subgraph API [Express API · Render]
C[Routes] --> D[Controllers] --> E[Services] --> F[(Prisma ORM)]
G[Socket.IO Server]
end
A -- REST + JWT --> C
A <-.WebSocket.-> G
B -- REST + JWT --> C
B <-.WebSocket.-> G
F --> H[(PostgreSQL)]
Request flow: route → middleware (auth / validate / rate-limit) → controller → service (business logic + Prisma) → database, with services emitting Socket.IO events to push live updates back to connected clients.
Backend (/server)
| Runtime | Node.js, TypeScript |
| Framework | Express 5 |
| Database | PostgreSQL + Prisma ORM 6 |
| Real-time | Socket.IO 4 (JWT-authenticated connections, per-user rooms) |
| Resend | |
| SMS | Twilio |
| Auth | JWT (jsonwebtoken) + bcryptjs password hashing |
| Validation | Zod schemas on every route |
| Security | Helmet, CORS, tiered express-rate-limit |
| Testing | Vitest + Supertest — 82 unit/integration tests |
Web (/web)
| Framework | Next.js 16 (App Router) + React 19 |
| Styling | Tailwind CSS 4, custom red theme |
| Maps | Leaflet / react-leaflet (OpenStreetMap — no API key required) |
| Real-time | Socket.IO client |
| PWA | Web manifest + custom service worker (installable, cache-first static assets) |
| Icons | lucide-react |
Mobile (/mobile)
| Framework | Expo 56 + React Native 0.85 |
| Navigation | React Navigation (native-stack + bottom-tabs) |
| Real-time | Socket.IO client |
| Location | expo-location |
| Secure storage | expo-secure-store (JWT persistence) |
BloodBank-Finder/
├── server/ Express API
│ ├── src/
│ │ ├── routes/ auth, donors, organizations, requests, ambulances, inventory, notifications, admin, stats
│ │ ├── controllers/ Request handlers
│ │ ├── services/ Business logic — matching, requests, ambulance, notifications, inventory, eligibility
│ │ ├── middleware/ Auth, role guards, validation, rate limiting
│ │ ├── schemas/ Zod validation schemas
│ │ ├── sockets/ Socket.IO server + event handlers
│ │ ├── utils/ Blood-type compatibility matrix, Haversine distance, JWT, eligibility
│ │ └── config/ Env parsing, Prisma client
│ ├── prisma/ Schema, migrations, seed script
│ └── tests/ Unit + integration tests (Vitest)
├── web/ Next.js PWA
│ └── app/ App Router pages — role dashboards, requests, ambulance tracking, admin
├── mobile/ Expo / React Native app
│ └── src/
│ ├── screens/ auth, home, requests, ambulance, notifications, profile, search
│ ├── navigation/ Stack/tab navigators per section
│ └── context/ Auth + Socket context providers
└── render.yaml Render Blueprint — API + PostgreSQL, one-click deploy
PostgreSQL schema managed by Prisma — 9 models:
| Model | Purpose |
|---|---|
User |
Auth identity; role is one of DONOR, HOSPITAL, BLOOD_BANK, ADMIN |
DonorProfile |
Blood group, location, availability, donation history |
Organization |
Hospital/blood bank profile, location, verification status |
InventoryItem |
Units on hand per blood group per organization |
EmergencyRequest |
A blood request — group, units, urgency, status, expiry |
RequestResponse |
A donor's offer/confirmation against a request |
Ambulance |
A registered vehicle and driver, owned by an organization |
AmbulanceRequest |
A dispatch — pickup/dropoff, assigned ambulance, live status |
Notification |
Per-user in-app notification feed |
All 8 blood groups (A_POS … O_NEG) are modeled with a full compatibility matrix that drives donor matching — e.g. O_NEG can donate to all 8 groups, while AB_POS can only receive from all groups but donate to AB_POS.
| Event | Fired when |
|---|---|
request:new |
A new emergency request matches a donor's blood group and search radius |
request:updated |
A request's status or fulfilled-units count changes |
response:new |
A donor offers to fulfill a request |
response:updated |
An offer is confirmed, completed, declined, or cancelled |
ambulance:updated |
An ambulance dispatch is assigned or its status changes |
notification:new |
Any new notification is created for a user |
All endpoints are prefixed with /api. Auth uses JWT bearer tokens; protected routes enforce DONOR / HOSPITAL / BLOOD_BANK / ADMIN access.
| Resource | Endpoints |
|---|---|
| Auth | POST /auth/register/donor, POST /auth/register/organization, POST /auth/login, POST /auth/logout, GET /auth/me |
| Donors | GET/PUT /donors/me, GET /donors/me/donations, GET /donors/search |
| Organizations | GET /organizations/search, GET/PUT /organizations/me, GET /organizations/:id |
| Requests | POST /requests, GET /requests, GET /requests/:id, PATCH /requests/:id, POST /requests/:id/responses, PATCH /requests/:id/responses/:responseId |
| Ambulances | POST/GET /ambulances, PATCH /ambulances/:id, POST/GET /ambulances/requests, GET/PATCH /ambulances/requests/:id |
| Inventory | GET/PUT /inventory/me |
| Notifications | GET /notifications, PATCH /notifications/:id/read, PATCH /notifications/read-all |
| Admin | GET /admin/organizations, PATCH /admin/organizations/:id/verify |
| Stats | GET /stats/overview |
- Passwords hashed with bcrypt (10 salt rounds)
- JWT-based auth with role-based access control on every protected route
- Zod validation on every request body, query, and param
- Tiered rate limiting — stricter limits on auth and request-creation endpoints
- Helmet security headers and a configurable CORS origin
- Ownership checks in the service layer — an organization can only modify its own requests, inventory, and ambulances
82 automated tests (Vitest + Supertest):
- Unit — blood-type compatibility matrix, Haversine distance calculation, donation eligibility rules
- Integration — auth flows, organization management, the full emergency-request lifecycle (create → match → offer → confirm → complete), ambulance dispatch, notifications, and donor search
cd server
npm test- Node.js 18+
- PostgreSQL (local or hosted)
git clone https://github.com/PerinbaBuilds/BloodBank-Finder.git
cd BloodBank-Findercd server
npm install
cp .env.example .env # fill in DATABASE_URL, JWT_SECRET, etc.
npx prisma migrate dev
npm run seed # optional: loads demo organizations, donors, and a sample request
npm run dev # http://localhost:4000cd web
npm install
npm run dev # http://localhost:3000cd mobile
npm install
npx expo startserver/.env
| Variable | Purpose |
|---|---|
DATABASE_URL |
PostgreSQL connection string |
PORT |
API port (default 4000) |
JWT_SECRET |
Secret used to sign auth tokens |
JWT_EXPIRES_IN |
Token lifetime (default 7d) |
CORS_ORIGIN |
Allowed origin for the web app |
DEFAULT_MATCH_RADIUS_KM |
Search radius for donor matching (default 15) |
MIN_DONATION_INTERVAL_DAYS |
Minimum days required between donations (default 90) |
LOW_STOCK_THRESHOLD |
Units at or below which a low-stock alert fires (default 5) |
RESEND_API_KEY |
Resend API key for sending notification emails; leave unset to disable email |
EMAIL_FROM |
Sender address for outgoing email (default BloodBank Finder <onboarding@resend.dev>) |
TWILIO_ACCOUNT_SID |
Twilio account SID for sending notification SMS; leave unset to disable SMS |
TWILIO_AUTH_TOKEN |
Twilio auth token |
TWILIO_PHONE_NUMBER |
Twilio phone number to send SMS from, in E.164 format |
web/.env.local
| Variable | Purpose |
|---|---|
NEXT_PUBLIC_API_URL |
Base URL of the API, including /api |
NEXT_PUBLIC_SOCKET_URL |
Base URL of the API for the Socket.IO connection |
The API and database deploy as a single Render Blueprint defined in render.yaml — a free-tier web service plus a managed PostgreSQL instance, with migrations run automatically on every deploy. The web app deploys separately to Vercel with the root directory set to web. This repository's live demo runs on exactly this setup.
All seeded accounts use the password Password123!.
| Role | |
|---|---|
| Admin | admin@bloodbankfinder.org |
| Blood Bank | central@bloodbank.demo, adyar@bloodbank.demo, velachery@bloodbank.demo |
| Hospital | mylapore@hospital.demo, tnagar@hospital.demo |
| Donor | donor1@demo.com … donor10@demo.com |
The seeded admin account above is for local development only. For a real deployment, create your own admin account instead of relying on seed data:
cd server
ADMIN_EMAIL=you@example.com ADMIN_PASSWORD=choose-a-strong-password npm run create-adminOmit ADMIN_PASSWORD to have a random one generated and printed once. The script refuses to run if an account with that email already exists, so it's safe to keep around. Never commit real admin credentials to this repository.
- Inter-blood-bank stock transfer requests, so a low-stock bank can pull units from a nearby bank with surplus
- Automated donor eligibility/cooldown reminders as the next allowed donation date approaches
Built by PerinbaBuilds.