An enterprise-grade, multi-role REST API powering Bangladesh's premier blood donation & medical crowdfunding ecosystem.
Overview • Architecture • Features • Database • Security • API Docs • Setup
BloodHelp Backend is the high-performance, secure API server powering the full BloodHelp ecosystem. It bridges blood donors, hospitals, charitable organisations, and individuals urgently seeking medical funds — all within a single, unified platform.
The system is built on strict Role-Based Access Control (RBAC), a hybrid JWT authentication strategy, and a modular domain-driven architecture designed for production scalability.
Live API Base URL:
https://bloodhelp-backend.vercel.app/api/v1Frontend: https://bloodhelp.vercel.app · Frontend Repo: BloodHelp-Frontend
The application strictly adheres to the Controller → Service → Repository pattern, ensuring complete decoupling of business logic from routing and data access layers.
graph TD
Client[Client Apps / Frontend] -->|HTTPS Requests| Router[API Gateway & Router Layer]
subgraph Core Infrastructure
Router -->|Validated by Zod| Controllers[Controllers Layer]
Controllers -->|Business Rules| Services[Services Layer]
Services -->|Data Access| Prisma[Prisma ORM]
end
subgraph Security Layer
AuthM[Hybrid Auth Middleware]
RBACM[Role Guard Middleware]
GlobalErr[Global Error Handler]
end
Router -.-> AuthM
Router -.-> RBACM
Controllers -.-> GlobalErr
Prisma --> DB[(PostgreSQL Database)]
Services --> Cloudinary[(Cloudinary CDN)]
Services --> SSLCommerz[(SSLCommerz Gateway)]
Services --> Email[(Nodemailer / SMTP)]
src/
├── app/
│ ├── config/ # Centralised, type-safe environment config
│ ├── db/ # Super Admin seed script
│ ├── errors/ # Custom AppError class hierarchy
│ ├── builder/ # QueryBuilder: pagination, filtering, sorting
│ ├── middlewares/ # Auth, RBAC Guard, Global Error, Not Found
│ ├── routes/ # Unified v1 route assembler
│ └── modules/ # Domain-driven feature modules
│ ├── auth/ # Login, Register, Token Rotation, OTP
│ ├── user/ # Profiles, Donor search with geo-filter
│ ├── post/ # Blood Finding, Donation, Crowdfunding
│ ├── post.engagement/ # Likes, Comments, Nested Replies
│ ├── hospital/ # Verified donation ledgers & requests
│ ├── organisation/ # Volunteer management & history tracking
│ ├── admin/ # Platform analytics & account moderation
│ ├── manage-admins/ # Super Admin: admin lifecycle management
│ ├── notification/ # Action-triggered in-app alerts
│ ├── payment/ # SSLCommerz IPN, webhooks, ledger
│ └── upload/ # Cloudinary signed upload & destroy
└── server.ts # Entry point: Express app bootstrap
Each module follows a strict 5-file pattern:
modules/[name]/
├── [name].controller.ts # Thin: calls service, formats response
├── [name].service.ts # All business logic + domain rules
├── [name].route.ts # Route definitions + middleware chain
├── [name].validation.ts # Zod schemas for all request inputs
└── [name].interface.ts # TypeScript types for the module
- 🛡️ Enterprise Security — Hybrid JWT pattern immune to both CSRF and XSS attack vectors
- 🩸 Intelligent Donor Matchmaking — Geo-filtered search by Division → District → Upazila with blood group, availability, and eligibility rule enforcement (Male: 2-month interval, Female: 3-month interval)
- 📝 3-Type Post System —
BLOOD_FINDING(urgent requests),BLOOD_DONATION(donor/camp announcements),HELPING(medical crowdfunding with SSLCommerz) - 🏥 Verified Hospital Ledger — Hospitals officially record physical donations against user profiles, auto-updating donor availability — replacing unreliable self-reporting
- 🏢 NGO Volunteer Network — Consent-based invitations, scoped donation history visibility, external volunteer management
- 💸 Automated Crowdfunding — Full SSLCommerz integration with IPN-verified
raisedAmounttracking againsttargetAmount - 🔔 Action-Triggered Notifications — Alerts for donation requests, consent invitations, post approvals, and payment confirmations
- ☁️ Signed Cloud Uploads — Cloudinary signed upload signatures; no media stored on application servers
Prisma v7's Multi-File Schema Architecture keeps database definitions atomic and maintainable. All entities use a global soft-delete pattern (isDeleted: Boolean @default(false)) — no data is permanently destroyed.
erDiagram
USER ||--o| DONOR_PROFILE : has
USER ||--o| HOSPITAL : is
USER ||--o| ORGANISATION : is
USER ||--o{ SESSION : tracks
USER ||--o{ POST : creates
USER ||--o{ NOTIFICATION : receives
USER ||--o{ PAYMENT : makes
BLOOD_DONOR }o--o| USER : linked_to
BLOOD_DONOR ||--o{ DONATION_HISTORY : records
BLOOD_DONOR ||--o{ HOSPITAL_DONATION_RECORD : in
BLOOD_DONOR ||--o{ ORGANISATION_VOLUNTEER : part_of
POST ||--o{ LIKE : has
POST ||--o{ COMMENT : has
POST ||--o{ PAYMENT : funds
POST ||--o| POST_DONATION_CONSENT : pending
COMMENT ||--o{ COMMENT : replies_to
Key Design Decisions:
BloodDonoris decoupled fromUser— supports both platform-registered users and externally-added volunteers (by hospitals/organisations) without requiring account creationPostDonationConsenttracks the 7-day consent window when User A posts a donation on behalf of User BSessionstores hashed refresh tokens with device + IP metadata, enablinglogout from all devices
| Access Token | Refresh Token | |
|---|---|---|
| Lifetime | 15 minutes | 30 days |
| Transport | JSON response body | HttpOnly + Secure + SameSite=None cookie |
| Client Storage | Memory / React state | Browser cookie store |
| CSRF Risk | ✅ None — not auto-sent | ✅ Mitigated via Origin validation |
| XSS Risk | ✅ None — not in localStorage | ✅ None — inaccessible to JavaScript |
Access Token expires (15 min)
│
▼
Client calls POST /auth/refresh-token ←── no body required
│
▼
Server reads HttpOnly cookie, validates against Session table
│
▼
New Access Token issued → queued requests resume
User never sees a logout or interruption
- Passwords hashed with
bcrypt(12 salt rounds) - All inputs validated through Zod before reaching controller logic
- Session table enables revocation of individual or all device sessions
- RBAC enforced at middleware level — not just the controller level
Base URL: https://bloodhelp-backend.vercel.app/api/v1
Auth Header: Authorization: Bearer <access_token>
🔑 Auth Module — 7 endpoints
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| POST | /auth/register |
Register User / Hospital / Organisation | — |
| POST | /auth/login |
Authenticate, receive access + refresh tokens | — |
| POST | /auth/logout |
Invalidate current session | 🔐 |
| POST | /auth/refresh-token |
Rotate access token via HttpOnly cookie | — |
| POST | /auth/change-password |
Update password (requires current password) | 🔐 |
| POST | /auth/forgot-password |
Request OTP to registered email | — |
| POST | /auth/reset-password |
Reset password using OTP | — |
👤 User & Donor Module — 4 endpoints
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| GET | /users/me |
Fetch current authenticated user's full profile | 🔐 |
| PUT | /users/me |
Update profile (name, location, health data) | 🔐 |
| GET | /users/donation-history |
Personal blood donation history | 🔐 USER |
| GET | /users/donors |
Search donors — filter by bloodGroup, division, district, upazila | 🔐 |
📝 Post Module — 9 endpoints
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| POST | /posts |
Create post (BLOOD_FINDING / BLOOD_DONATION / HELPING) | 🔐 |
| GET | /posts |
Public feed with filters (type, bloodGroup, location) | — |
| GET | /posts/:id |
Single post with full details | — |
| GET | /posts/user/:userId |
All posts by a specific user | 🔐 |
| PATCH | /posts/:id |
Update post content | 🔐 Author/Admin |
| DELETE | /posts/:id |
Soft-delete a post | 🔐 Author/Admin |
| PATCH | /posts/:id/resolve |
Mark blood-finding post as resolved | 🔐 Author |
| PATCH | /posts/:id/approve |
Approve a HELPING post for public display | 🔐 ADMIN |
| PATCH | /posts/:id/verify |
Apply verification badge | 🔐 ADMIN |
💬 Engagement Module — 6 endpoints
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| POST | /posts/engagement/like |
Toggle like / unlike | 🔐 |
| POST | /posts/engagement/comment |
Add comment (supports parentId for nested replies) |
🔐 |
| GET | /posts/engagement/:postId/comments |
Fetch all comments + nested replies | — |
| PATCH | /posts/engagement/comments/:commentId |
Edit own comment | 🔐 Author |
| DELETE | /posts/engagement/comments/:commentId |
Delete comment | 🔐 Author/Admin |
| DELETE | /posts/engagement/comments/:postId/delete-all |
Purge all comments on a post | 🔐 ADMIN |
🏥 Hospital Module — 3 endpoints
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| POST | /hospitals/record-donation |
Officially record a physical blood donation | 🔐 HOSPITAL |
| GET | /hospitals/donation-records |
All records managed by this hospital | 🔐 HOSPITAL |
| PATCH | /hospitals/requests/:requestId |
Accept or reject a donor request | 🔐 USER |
🏢 Organisation Module — 7 endpoints
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| POST | /organisations/volunteers |
Add a volunteer (platform user or external) | 🔐 ORG |
| PATCH | /organisations/volunteers/consent/:volunteerId |
Accept/reject an organisation invite | 🔐 USER |
| GET | /organisations/volunteers |
List all organisation volunteers | 🔐 ORG |
| GET | /organisations/volunteers/history |
Donation history of volunteers in this org | 🔐 ORG |
| PATCH | /organisations/volunteers/:id/donation-date |
Manually update volunteer donation date | 🔐 ORG |
| PATCH | /organisations/volunteers/:id |
Update volunteer details | 🔐 ORG |
| DELETE | /organisations/volunteers/:id |
Remove a volunteer | 🔐 ORG |
🛡️ Admin Module — 7 endpoints
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| GET | /admin/analytics |
Platform-wide statistics dashboard | 🔐 ADMIN |
| GET | /admin/users |
List all users (filter: role, status) | 🔐 ADMIN |
| PATCH | /admin/users/:id/status |
Approve / Block / Reject a user account | 🔐 ADMIN |
| GET | /admin/hospitals |
List all registered hospitals | 🔐 ADMIN |
| PATCH | /admin/hospitals/:id/status |
Update hospital registration status | 🔐 ADMIN |
| GET | /admin/organisations |
List all registered organisations | 🔐 ADMIN |
| PATCH | /admin/organisations/:id/status |
Update organisation registration status | 🔐 ADMIN |
👑 Manage Admins — 6 endpoints (Super Admin only)
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| POST | /manage-admins |
Create a new Admin account | 🔐 SUPER_ADMIN |
| GET | /manage-admins |
List all Admin accounts | 🔐 SUPER_ADMIN |
| GET | /manage-admins/:id |
Fetch a specific Admin's details | 🔐 SUPER_ADMIN |
| PATCH | /manage-admins/:id |
Update Admin profile | 🔐 SUPER_ADMIN |
| PATCH | /manage-admins/:id/access |
Toggle Admin's access rights | 🔐 SUPER_ADMIN |
| DELETE | /manage-admins/:id |
Delete an Admin account | 🔐 SUPER_ADMIN |
🔔 Notification Module — 3 endpoints
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| GET | /notifications |
Fetch all notifications for current user | 🔐 |
| PATCH | /notifications/mark-all-read |
Mark all notifications as read | 🔐 |
| PATCH | /notifications/:id |
Mark a single notification as read | 🔐 |
💳 Payment Module — 5 endpoints
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| POST | /payments/initiate |
Create transaction, get SSLCommerz gateway URL | 🔐 USER |
| POST | /payments/success |
Payment success callback (IPN) | Gateway |
| POST | /payments/fail |
Payment failure callback | Gateway |
| POST | /payments/cancel |
Payment cancellation callback | Gateway |
| POST | /payments/ipn |
Instant Payment Notification handler | Gateway |
☁️ Upload Module — 2 endpoints
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| POST | /upload/signature |
Generate Cloudinary signed upload signature | 🔐 |
| DELETE | /upload/destroy |
Delete an uploaded image from Cloudinary | 🔐 |
Total: 52 endpoints across 11 modules
- Node.js v20+ — nodejs.org
- PostgreSQL v16 — local instance or cloud (Supabase, Neon, Railway)
- Cloudinary account — cloudinary.com
- SSLCommerz sandbox credentials — developer.sslcommerz.com
git clone https://github.com/ambakhtiar/BloodHelp-Backend.git
cd BloodHelp-Backend
npm installcp .env.example .envFill in all required values — see Environment Variables below.
# Generate the Prisma client
npx prisma generate
# Push schema to your PostgreSQL instance
npx prisma db push
# (Optional) Seed the initial Super Admin account
npm run seed:superAdminnpm run dev
# Server starts at http://localhost:5000 with hot-reload via tsx watchCreate a .env file at the project root:
# ── Application ──────────────────────────────────────
NODE_ENV=development
PORT=5000
# ── Database ─────────────────────────────────────────
DATABASE_URL="postgresql://postgres:password@localhost:5432/BloodHelp?schema=public"
# ── JWT Authentication ────────────────────────────────
JWT_SECRET="your_access_token_secret_min_32_chars"
JWT_EXPIRES_IN="15m"
JWT_REFRESH_SECRET="your_refresh_token_secret_min_32_chars"
JWT_REFRESH_EXPIRES_IN="30d"
BCRYPT_SALT_ROUNDS=12
# ── URLs ──────────────────────────────────────────────
BACKEND_URL="http://localhost:5000"
FRONTEND_URL="http://localhost:3000"
CLIENT_URL="http://localhost:3000"
# ── SSLCommerz Payment Gateway ────────────────────────
SSL_COMMERZ_STORE_ID="your_store_id"
SSL_COMMERZ_STORE_PASSWORD="your_store_password"
SSL_COMMERZ_IS_LIVE=false
# ── Cloudinary ────────────────────────────────────────
CLOUDINARY_CLOUD_NAME="your_cloud_name"
CLOUDINARY_API_KEY="your_api_key"
CLOUDINARY_API_SECRET="your_api_secret"
# ── Email (Nodemailer) ────────────────────────────────
SMTP_HOST="smtp.gmail.com"
SMTP_PORT=587
SMTP_USER="your_email@gmail.com"
SMTP_PASS="your_app_password"
⚠️ Never commit.envto version control. Ensure it is listed in.gitignore.
| Script | Command | Description |
|---|---|---|
dev |
tsx watch src/server.ts |
Development server with hot-reload |
build |
prisma generate && tsup |
Compile TypeScript to ESM output |
start |
node dist/server.js |
Serve the production build |
seed:superAdmin |
tsx src/app/db/index.ts |
Seed initial Super Admin account |
lint |
eslint . ./src/**/* |
Run ESLint across the codebase |
- Connect your GitHub repository to Vercel
- Set all required environment variables in the Vercel project dashboard
- Set Build Command to
npm run build - Ensure
vercel.jsonis present at the root:
{
"version": 2,
"builds": [{ "src": "dist/server.js", "use": "@vercel/node" }],
"routes": [{ "src": "/(.*)", "dest": "dist/server.js" }]
}- Click Deploy — subsequent pushes to
maindeploy automatically.
- Fork the repository
- Create a feature branch:
git checkout -b feature/your-feature-name - Commit using conventional commits:
git commit -m "feat: add X" - Push and open a Pull Request against
main - Ensure
npm run lintpasses before requesting review
Distributed under the MIT License. See LICENSE for details.