Mahmoud Alshraky
I build systems people use, then keep them running. Full-stack software engineer. I build and run production systems with React, Node.js and PostgreSQL: an exam platform, an HR system for 25+ branches, a shared email gateway, and an in-browser face and hand tracking prototype. Watch each one get built, layer by layer.
How I build a system
It starts on paper
Before any code: who uses it, what must never go wrong, and what the data looks like. The plan is drawn first, then built on.
- NeuroLink began as a written spec, a test protocol and a sources file
- SQB keeps its medicine and nursing tracks strictly apart
Then the data
One database holds everything that has to be remembered, designed so a brand-new copy and the live one always match.
- SQB: 31 tables, created by a schema that is safe to re-run
- HR: PostgreSQL with migrations and indexes on the hot queries
Rules in one place
Every request passes through one core that applies the rules once, so the website, the app and the bot cannot disagree. A request that breaks a rule is refused.
- SQB: 150 route handlers behind one API
- The web app, the mobile app and the Telegram bot all use it
Everyone gets the right door
Signing in says who you are; your role says how far you get. A branch manager sees one branch, an administrator sees them all.
- HR: role-based access across 25+ branches
- One-time codes for branch accounts
Then what people see
The interface goes on last, on top of everything underneath: the same data and rules on a laptop and on a phone, right-to-left where the users read Arabic.
- SQB and HR: right-to-left Arabic interfaces
- SQB: a web app and an Expo mobile app on one backend
Then I keep it running
Launch is the middle, not the end. Scheduled jobs run on time, tests guard the parts that must not break, and dashboards show whether people find it.
- SQB: 9 scheduled jobs; 5 to 468 Google-indexed pages between 30 Aug and 16 Sep 2026
- Email gateway: 35 automated tests
Numbers (19 September 2026)
- 468 pages indexed by Google. Up from 5 on 30 August, after I prerendered the public content for crawlers. Source: Google Search Console, smle-question-bank.com, 16 Sep 2026
- 1,001 visitors in 30 days. 4,262 page views, 45% bounce rate. Source: Vercel Web Analytics, smle-question-bank.com, 30 days to 19 Sep 2026
- 734 clicks from Google search in 90 days. 13.1K impressions, average position 6.8. Source: Google Search Console, smle-question-bank.com, 17 Jun to 16 Sep 2026
Systems in production
SQB: Exam-prep platform for the Saudi SMLE and SNLE licensing exams
7,000+ practice questions with explanations for Saudi licensing exams. I built the web app, API, mobile app and Telegram bot, and I run it.
- 7,000+ questions, each with a written explanation (7,115 on 17 Sep 2026).
- Two exam tracks, medicine and nursing, with content and analytics kept separate per track.
- Prerendered public pages took Google-indexed pages from 5 to 468.
It starts with a search
Students find SQB on Google. Its public pages are prerendered, so search engines read real content. Every lit tile is a page Google knows.
- Indexed pages went from 5 to 468 between 30 Aug and 16 Sep 2026
- 734 clicks from Google search in 90 days
Then they practise
Practice questions, explanations and a plan for each student, in a right-to-left interface built for Arabic readers.
- 1,001 visitors and 4,262 page views in the 30 days to 19 Sep 2026
- Live at smle-question-bank.com
Then they subscribe
When a student pays, Moyasar confirms it, the subscription switches on and a VAT invoice is generated as a PDF. The receipt email goes out through the gateway I built for my projects.
- Five plans: monthly, four-month, annual, and group plans for 3 and 5 seats
- Webhook-driven activation, VAT invoice as a PDF
And they come back
The website, a mobile app and a Telegram bot all run on one backend. A daily question and reminders bring students back.
- Web, Expo mobile app and Telegram bot share one API
- Telegram gets a daily question and a weekly summary
- Data: DB PostgreSQL, 31 tables; TABLE user_question_progress; TABLE payment_events; TABLE funnel_events, page_engagement, login_history; TABLE subscription_groups, group_seats, trial_grants; SCHEMA Created idempotently at cold start, safe to re-run
- Search: BUILD Public pages prerendered so crawlers read real content; GSC 468 indexed pages on 16 Sep 2026, from 5 on 30 Aug; GSC 734 clicks and 13.1K impressions in 90 days
- Interface: UI React 19 and Vite, right-to-left Arabic; URL smle-question-bank.com
- Payments: PAY Moyasar: Apple Pay, webhooks; POST /webhook confirms and activates the subscription; GET /invoice/:gatewayRef.pdf (VAT invoice); MAIL Shared email gateway, Gmail SMTP fallback
- Engine: API One Express app, 150 route handlers; GET /api/questions; GET /api/questions/:id/explanation; GET /api/user-subscription/:userId; GET /admin/analytics; APP Expo / React Native, same backend; BOT Telegram: daily question, weekly summary; CRON 9 scheduled endpoints (Telegram, lifecycle and daily emails)
React 19, Vite, Express, PostgreSQL, Moyasar, Expo, Telegram Bot API
https://www.smle-question-bank.com
Multi-branch HR platform: Employee records, payroll, absence and documents for a healthcare company
A healthcare company with 25+ branches ran its staff on paper. I built its system from scratch: people, payroll, documents, buses and reports.
- Document expiry alerts fire 30, 60 and 90 days ahead.
- JWT sessions, role-based access across branches, one-time codes for branch accounts.
- Redis caching and composite indexes on the hot queries.
25+ branches, one system
Every branch reports to one system: one set of employee records, one set of rules, and the buses that carry the students.
- 25+ branches of a healthcare company
- Student bus transport tracked alongside staff
Nothing expires unnoticed
Every employee file carries documents with expiry dates. Alerts fire 90, 60 and 30 days before one runs out, so renewals happen before the deadline, not after.
- Alerts at 90, 60 and 30 days
Payroll and absence, month by month
Attendance and absences add up to each month's payroll, per employee and per branch, with Hijri dates where people expect them.
- Payroll, absence and year transitions in one place
Reports people can print
Payroll, absence and branch reports come out as PDF and Excel files, ready to print, sign or send on.
- PDF and Excel, with Hijri dates
- Redis caching and composite indexes keep them fast
- Access: AUTH JWT sessions; ROLES Role-based access across 25+ branches; OTP One-time codes for branch and user accounts; AUDIT Review confirmations and year transitions
- Branches: MODULE branches, branch-documents, branch-statistics; MODULE bus-transportation; PAGE Dashboard and branch statistics; API 18 route modules, one per area
- Expiry: MODULE employees, employee-file, employee-expiry; MODULE documents, archive, notifications; ALERT 30, 60 and 90 days before expiry
- Payroll: MODULE payroll-absences, academic-years; PAGE Payroll and absences
- Reports: DB PostgreSQL with migrations; CACHE Redis, per-route expiry; INDEX Composite indexes on hot queries; REPORT PDF and Excel, Hijri dates
React 19, Node.js, Express, PostgreSQL, JWT, Redis
Internal system with employee data, so there is no public link. I walk through it in an interview or call.
NeuroLink: A camera that reads faces and hands, and does not diagnose
A child plays a 3-minute game while the webcam measures behaviour, all in the browser. The doctor sees only what is new, and decides.
Built for Let's Pitch It 2026 (GDG Qassim). It is a prototype, not a medical device, and it has not been clinically validated.
- Face and hand tracking run in the browser; the video never leaves the device.
- Build checks fail if anything but one file touches the camera, or if the code names a condition.
It starts with a webcam
A child plays a short game while the webcam watches. The video never leaves the device: face and hand tracking run in the browser.
- Runs on the device with MediaPipe
- A build check fails if anything except one file touches the camera
Hands: 21 points each
Every frame the browser finds 21 landmarks on the hand, enough to tell an open hand from a point, a wave or a fist.
- Pointing and waving are checked against what the game asked for
Faces: 478 points
478 landmarks, including both irises, give the head turn, where the child is looking, blinks and expression, all condensed into one small record per frame.
- Head pose, gaze region, blinks, smile and brow raise
A game built as an instrument
Six timed blocks, 3 minutes 5 seconds in total. Every measure is tied to a timestamped moment, so sessions stay comparable with each other.
- The social-attention block approximates a published paradigm with a webcam and does not claim the original's validation
The doctor decides
The physician sees only what today's session showed that the record does not already contain, as one notification with three choices: add to the record, request a referral, or dismiss. It never diagnoses.
- Every number shows where it came from
- A build check fails if the code contains condition-naming vocabulary
- Camera: CAM One getUserMedia call site; RUN MediaPipe WASM, on the device; FALLBACK GPU first, then CPU; PIN Model files pinned to MediaPipe 0.10.18
- Hands: HAND 21 landmarks per hand; HANDS extended[5], isPoint, bearing; STATE pointing, onTask, distanceCm
- Face: FACE 478 landmarks with irises; HEAD headYaw, headPitch, headRoll; GAZE gazeRegion, gazeConf; EYES blinkLeft, blinkRight, eyesOpen; FACE jawOpen, smile, browRaise
- The game: B0 calib, 20 s; B1 social, 40 s; B2 name, 30 s; B3 point, 40 s; B4 wave, 25 s; B5 bubbles, 30 s
- The doctor: API FastAPI and SQLite engine; NORMS norms_v1.json, age norms; RULE Each field records its provenance; ACT Add to record, Request referral, Dismiss; NEVER Diagnose or name a condition
TypeScript, MediaPipe, Python, FastAPI, SQLite
The repository is private. I run it live in an interview or a call.
Shared email gateway: One email service for three of my projects
SQB, the HR system and this portfolio share one free email plan of 100 a day. This gateway splits it fairly and falls back to Gmail.
- Bounces and complaints are suppressed through a webhook signed by Svix.
- Idempotency keys prevent duplicate sends; error alerts have a cooldown.
- 35 automated tests in four files.
Three projects, one door
Every project sends through the same gateway instead of carrying its own mail setup. It checks where each request comes from, and turns away mail from a source it does not know.
- SQB, the HR system and this portfolio send through it
- Requests from unregistered origins are dropped
A fair share of 100 a day
Resend's free plan allows 100 emails a day. The gateway splits them between the projects, and when the day's share is spent, mail goes out through Gmail instead of failing.
- Gmail SMTP takes over when the quota runs out
A bounce is remembered
When an address bounces or complains, a signed report comes back and the gateway stops mailing that address. The same message sent twice goes out once.
- Webhook reports are verified with Svix signatures
- Idempotency keys stop duplicate sends
35 tests hold it together
The client, the origin check, the quota and the email templates are covered by 35 automated tests, so one change cannot quietly break mail for every project at once.
- One door: POST /api/v1/send; POST /api/v1/send/bulk; POST /api/v1/notify; GET /api/v1/quota; GET /api/v1/messages/:id
- Quota: QUOTA Splits Resend's 100 a day across projects; FALLBACK Gmail SMTP when the day's quota is spent
- Bounces: HOOK POST /api/webhooks/resend; SIG Signed by Svix; RULE Bounces and complaints suppress the address; SAFE Idempotency keys stop duplicate sends; UNSUB GET /u/:token
- Tests: TEST client.test.ts, 8; TEST origin.test.ts, 6; TEST quota.test.ts, 11; TEST render.test.ts, 10
Next.js, TypeScript, PostgreSQL, Resend, Nodemailer, Svix
Internal infrastructure. Its admin panel shows real users' data, so there is no public link.
Client sites
Al-Haysoni Law Firm: Website for a law firm
A law firm's site with an interactive 3D scene, a contact form that reaches the office, and structured data for search.
- React 19 and Three.js.
- Schema.org structured data, sitemap and robots.txt.
React 19, Three.js, Vite, Express
https://www.alhisony.com
Erth Environmental Services: Website for an environmental consultancy
An Arabic site for an environmental consultancy, opening on an interactive 3D globe, with dark and light themes and a consultation form.
- Interactive Three.js globe with Saudi Arabia marked out.
- A fingerprint drawn from a mathematical field, not by hand.
Three.js, Vite, JavaScript, Google Maps
https://erthfc.com
Experience
Full-stack software engineer, Ultimate Care Rehabilitation Co.
Oct 2025 to now
- Designed and built an HR system from scratch for a company with 25+ branches: lifecycle, payroll, absence, documents and student transport.
- Automated document-expiry alerts and PDF and Excel reports with Hijri dates.
Full-stack developer, SQB
Feb 2025 to now
- Built the platform, API, database, mobile app and Telegram bot, and run them in production.
- Prerendered public content for crawlers: Google-indexed pages rose from 5 to 468.
Web developer (freelance), Al-Haysoni Law Firm
Jul to Aug 2025
- Delivered a site with an interactive Three.js scene and Schema.org structured data.
Web developer (freelance), Erth Environmental Services
2025, redesigned Sep 2026
- Built the site, then redesigned it around an interactive Three.js globe.
About
I build the whole system, from the database to the screen, and I stay on after launch.
Contact
alshraky3@gmail.com, GitHub, LinkedIn