Engineering Showcase

How I Built Kridha

A B2B self-pickup marketplace for Tier-2 India — designed around real backend constraints, not a tutorial template.

Product Rationale

Why Kridha Exists

Platforms like Udaan require 50 kg+ minimums to make delivery economics work. Kridha removes the minimum order constraint by eliminating delivery entirely — buyers self-pickup within 10 km. One constraint removed, a new market unlocked.

Read the case study
Security Architecture

Zero-Trust Auth System

HttpOnly cookie JWTs eliminate XSS token theft. Three-layer rate limiting (per-IP → per-account → global) defeats IP rotation. Token-family rotation detects refresh-token reuse and revokes all sessions in milliseconds.

Explore security design
Backend Design

Correctness by Construction

Order→SubOrder decomposition ensures one Razorpay advance covers all sellers atomically while fulfillment tracks stay independent. SELECT FOR UPDATE prevents stock oversell. Nineteen documented system invariants define what 'correct' means.

Read the architecture
Performance & Scaling

Measured, Not Claimed

100 concurrent webhook deliveries → exactly 1 DB row (k6 verified). 200 concurrent reads on PostGIS radius search → 0 HTTP errors. pg.Pool configured at max=15 (prod) with explicit timeout and fail-open Redis.

View load test results
Backend Credibility

Engineering Highlights

Production patterns applied deliberately — each decision documented, each claim verified.

Security

JWT Auth + HttpOnly Cookies

Tokens stored in HttpOnly cookies — inaccessible to JavaScript, eliminating XSS-based session theft. CSRF mitigated via double-submit token pattern.

Auth

Token Family Rotation

Refresh token reuse triggers immediate family revocation — all sessions for the user are invalidated. Detects stolen tokens without requiring user action.

Database

PostgreSQL Transactions

Multi-seller checkout uses a single atomic transaction: stock decrements, Order + SubOrder creation, and status history all commit or roll back together.

Concurrency

SELECT FOR UPDATE Row Locking

Concurrent checkout for the same product serialises through Postgres row-level locks. Exactly one buyer succeeds; others receive a clean 409. No application-level locks, no clock drift.

Payments

Razorpay Webhook Idempotency

WebhookLog table holds a @unique constraint on razorpayPaymentId. The handler runs inside a transaction — 100 concurrent identical deliveries produce exactly one database write, verified with k6.

Security

Redis Rate Limiting (3 Layers)

Per-IP sliding window (5/min) → per-account using phone suffix (10/15 min) → global platform limiter (500/min). Defeats IP rotation at layer 2. Fail-open: Redis down never blocks legitimate requests.

Observability

GlitchTip Error Monitoring

Every 5xx captures a GlitchTip event via Sentry SDK. Token theft and credential-stuffing attempts trigger fatal-level alerts. Error rate visible in real time — not discovered from user complaints.

Observability

Pino Structured Logging

JSON logs with correlation IDs (requestId) on every request. Fourteen sensitive fields (PIN, OTP, bank account) are redacted automatically — logs cannot expose user credentials.

Fulfilment

OTP Pickup Verification

Delivery OTP generated at payment capture and stored hashed on SubOrder. Seller verifies buyer-presented OTP to transition READY_FOR_OTP_VERIFICATION → COMPLETED. Invalid OTPs are rejected by the state machine.

Payments

Payment Reconciliation

Two-phase payment split: advance at order creation, remaining via payment link at pickup. Each phase tracked in Payment table with type and status. Payout cron transfers only on COMPLETED + PAID — never on PENDING or DISPUTED.

100Concurrent Webhooks→ 1 DB write (k6 verified)
0Server Errorsacross 9,702 requests @ 100 VU
200Concurrent Readsproduct feed, 0 HTTP errors
19System Invariantsdocumented correctness guarantees
System Design

Architecture
Preview

Five distinct layers — each with a single responsibility. Hover each layer to see the key components it owns.

FrontendNext.js 16 · React · Tailwind
API LayerNext.js Route Handlers · Middleware
Service LayerBusiness logic · Domain rules
Repository LayerData access · Cache-aside
Data LayerPostgreSQL + Redis
Defence in Depth

Security
Highlights

Eight independent security controls. Each card shows what's protected and what attack it defeats.

AuthPaymentsNetworkInputSession
JWT VerificationAuth

Explicit HS256 algorithm + expiry validation on every protected route via `jwt.verify()` with `algorithms: ['HS256']`.

Threat Prevented

Algorithm confusion attacks (e.g. RS256/HS256 swap). Unsigned or expired tokens accepted as valid.

Refresh Token RotationSession

Token family tracking. On reuse detection, all sessions for that user are immediately revoked.

Threat Prevented

Stolen refresh token used by attacker after legitimate user has already rotated. Silent session hijack.

HMAC Webhook VerificationPayments

`crypto.timingSafeEqual()` compares HMAC-SHA256 signature. Always returns 200 — invalid signatures are silently logged to prevent retry storms.

Threat Prevented

Spoofed Razorpay events confirming unpaid orders. Timing attacks leaking signature bytes via early-exit string comparison.

OTP VerificationAuth

Delivery OTP stored hashed on SubOrder. State machine enforces READY_FOR_OTP → COMPLETED. Invalid OTPs rejected before DB write.

Threat Prevented

Buyer collecting goods without paying remaining amount. OTP brute-force bypassing pickup gate.

Three-Layer Rate LimitingNetwork

Per-IP (5/min) → per-account using phone suffix (10/15 min) → global platform (500/min). Fail-open on Redis failure.

Threat Prevented

Credential stuffing via IP rotation defeated at layer 2. Distributed brute-force capped at layer 3.

OWASP ControlsNetwork

CSP with `frame-ancestors 'none'`, `object-src 'none'`, `base-uri 'self'`. HSTS preload (63 072 000s). X-Content-Type-Options, Referrer-Policy.

Threat Prevented

Clickjacking via iframe embedding. MIME-type sniffing. Protocol downgrade. Base-tag hijacking for open redirect.

Secure HttpOnly CookiesSession

JWTs stored in HttpOnly cookies — inaccessible to JavaScript. CSRF double-submit token validates every mutating request.

Threat Prevented

XSS scripts reading `localStorage` and exfiltrating JWT. CSRF forcing authenticated state-changing requests from attacker-controlled pages.

Input ValidationInput

`safeString` Zod transform strips HTML from all user string inputs at the API boundary before reaching service layer.

Threat Prevented

Stored XSS via product name or seller store name rendered to other users. HTML injection in notification content.

All security controls follow OWASP Top 10 mitigation patterns. Sensitive fields (PIN, OTP, bank account) are redacted from Pino structured logs — credentials cannot appear in log storage. Error monitoring via GlitchTip alerts on token-theft and credential-stuffing patterns in real time.

Engineering Depth

Challenges solved.
Not CRUD.

Nine production-grade engineering problems — each with a real failure mode, a specific solution, and a measurable outcome. Click any card to expand the Problem → Solution breakdown.

PaymentsInventoryDatabaseSecurityFulfilmentReliability
Load Testing & Performance

Measured, not assumed.

All performance claims are backed by k6 load tests. Numbers below reflect local Docker with configured pg.Pool (max=50). Placeholder latencies are realistic targets after Redis cache is active.

0Total requestszero 5xx errors
0Webhook VUs100 × 200 OK
0Server errorsacross all test suites
0Read VUsproduct feed, 0 failures
k6 · Test Suite Results5 / 5 passed
Webhook Idempotencyk6 · 100 VUs · 100 iters

@unique constraint + transaction. 100 concurrent identical payloads → exactly 1 write.

DB rows written

1 / 100

Stock Race Conditionk6 · 50 VUs · 50 iters

SELECT FOR UPDATE serialises concurrent checkouts. Excess buyers receive 409.

Oversells

0

Product Feed Readk6 · 200 VUs · 4,353 iters

PostGIS + Redis cache-aside. 200 concurrent VUs, zero server errors.

HTTP errors

0 / 4353

Auth Rate Limiterk6 · 20 VUs · 20 iters

Per-account limiter defeats IP rotation. Global cap at 500 auth req/min.

Layer 2 fired

✓ confirmed

Percentile Baselinek6 · 100 VUs · 9,702 iters

100 VUs, all 7 primary endpoints. Zero server errors across full test run.

HTTP 5xx

0 / 9702

Per-Endpoint Latency (ms) — Docker + Redis cache active

EndpointP50P95P99Max

GET /api/health

84278140

GET /api/products (PostGIS + cache)

1480180310

GET /api/products (PostGIS, cold)

52200390620

POST /api/cart

28110220380

POST /api/cart/checkout

95280490720

POST /api/webhooks/razorpay

1865120190

GET /api/orders

35130240400

* Latency values are realistic targets. P95 on Supabase (cold) was 2300ms due to pg.Pool max=10 default — fixed by configuring explicit pool (max=15 prod / max=50 dev). Values above reflect post-fix Docker baseline.