Skip to content
msaouab
Get in Touch
msaouabIT Architect | Software EngineerAvailable for work
Get in Touch
← All projects
1337 · 42 Network

Matcha

Real-time dating platform, load-tested to 1,000 users

Matcha is the 42 Network dating platform — register, verify, build a profile, discover people, match on mutual likes, then chat, call, and arrange to meet. Around 55,000 lines of TypeScript across a React SPA and an Express/PostgreSQL API, packaged with Docker Compose behind Nginx. Every mandatory requirement is complete and four of the five bonuses shipped; the part I am most pleased with is what came after the features worked — a load test and a metrics stack that turned "it feels fast" into a number, and then into a named bottleneck.

1k VUs · 62 endpoints

Highlights

Problem

The subject asks for a complete product rather than a demo: secure auth and email verification, complete profiles, orientation-aware discovery and search, mutual matching, match-gated messaging, live notifications, location with consent, a fame score, and safety tools. Bonuses only count if the mandatory path is flawless, so correctness had to come before anything else — and "correct" includes behaving under load, which nothing in the brief tells you how to check.

Role

Full-stack co-author with Manar Ben Salah at 1337 Khouribga. I owned the identity and profile surfaces including the photo editor, shared the discovery, chat, and real-time work, and built the performance and observability tooling — the k6 suite, the Prometheus exporters inside the API, and both Grafana dashboards.

Architecture

React + Vite SPA against an Express API, both TypeScript, with PostgreSQL reached through 17 repository modules so no route talks to SQL directly. Twenty ordered migrations own the schema. Socket.io carries presence, chat, and notifications; WebRTC handles calls peer-to-peer with the API only brokering signalling. Nginx terminates TLS and fronts both apps, and Docker Compose layers three files — base, observability, load test — so the same stack serves development, monitoring, and performance runs.

Security

Session cookies are httpOnly and sameSite, and stored as a SHA-256 hash so a database leak yields hashes rather than usable cookies. Passwords go through zxcvbn strength scoring before bcrypt. Every query is parameterized; uploads are validated by magic bytes rather than by extension; helmet sets a strict CSP; and the auth routes are rate limited independently of the API. Messaging, calls, and date proposals are gated on a real mutual match, checked server-side on every request rather than hidden in the UI.

Testing

Four levels, all in CI: 59 unit tests across 17 suites for the pure logic; 29 API tests asserting the permission matrix — who may read, message, block, or report whom; 9 Playwright journeys through the browser; and two parity tests that run the fame-rating and match-score algorithms against the SQL functions mirroring them, because the same formula living in TypeScript and in Postgres will drift silently otherwise. CI also runs lint, formatting, both typechecks, and a production build on every push.

Performance

The load test models journeys, not endpoints: a page view costs a session check, an unread count, and a filter-metadata call before its own query, and 5–25 seconds of think time separates journeys so 1,000 virtual users mean 1,000 people rather than 1,000 request loops. Three scenarios run together — browsing, signing in, signing up — for 1,080 VUs, 135,357 requests at 187 req/s, with 99.99% of 138,640 checks passing. It found what guessing would not: p95 of 2.36s against a 1.5s budget, and connection-pool waiters spiking to 17 against a pool of 10 — naming the pool, not the queries, as the first constraint.

Observability

The API exports roughly 70 Prometheus metric families and two provisioned Grafana dashboards read them: one for the running product — users, matches, messages, moderation, per-route latency — and one for load-test runs. The runtime metrics are the ones that answer "why": cgroup CPU throttling, which docker stats cannot show; event-loop utilisation, the real ceiling for a single-threaded runtime; a probe that measures libuv threadpool queueing directly; GC pauses; and pg_stat_statements for per-statement cost. Pool queueing is measured at the call site, because a snapshot gauge only catches contention a scrape happens to land on.

Outcome

A defense-ready platform with the full mandatory scope and four bonuses — photo editor, live map of users, WebRTC calls, and match-gated dates with map-based meetup picking — leaving OAuth sign-in as the one deliberate gap. The lasting lesson was the second half of the project: features that work are not the same as a system you can reason about, and the difference is measurement you can point at.

Stack

ReactViteTypeScriptExpressPostgreSQLSocket.ioWebRTCNginxDocker ComposePrometheusGrafanak6Playwright
Loading