All work

Case study · 2026

Nexus

ApexMedix — multi-tenant operations platform for private medical event companies

Role

End-to-end product & engineering

Stack

Nuxt 3 · Vue · Fastify · PostgreSQL · Stripe · Docker

Nexus staff operations platform preview

Overview

It started while I was working in the private medical sector. Most companies I saw were running events, staff, compliance, and hiring across spreadsheets, WhatsApp, and a handful of disconnected tools. There was no single system for day-to-day operations.

I built Nexus to be that system: one branded staff portal per organisation, with rostering, HR, compliance, recruitment, and training in one place — and extra modules they can turn on as they grow.

Role

I designed and built the product end to end: product direction, multi-tenant architecture, the Fastify API, the Nuxt staff portal, permissions, billing/modules, and production deployment.

The problem

Event medical companies typically juggle:

  • Staffing events and tracking who is on which shift
  • Compliance (licences, DVLA, medicines, equipment)
  • HR, documents, and onboarding
  • Hiring into the same operational pool they roster from

Without a central system, data is duplicated, access is messy, and nothing looks like their company. Nexus needed to feel like each tenant’s own product, not a generic SaaS dashboard.

What I built

A multi-tenant staff portal. Each organisation gets its own branding, login, roles, and data. Core operations sit in one app; optional modules are purchased and gated.

Core

  • Event rostering and staff directory
  • Staff onboarding (accounts, temp passwords, first-login / 2FA)
  • Roles, grades, and permission-based navigation
  • HR: people, leave, finance, documents
  • Compliance: DVLA, medicines, equipment
  • Company settings, audit log, custom portal domains

Modules

  • Recruitment — jobs, forms, applications, GDPR retention/anonymise
  • Interview scheduling — shared availability, booking/amend links, confirmations
  • Training bookings — courses, learners, timetable
  • WhatsApp Business — shift notifications via Meta Cloud API

Hire → account

When an application is marked hired, staff can import the applicant and create a portal account (name, email, phone, address from the form; role and grade chosen in a dialog). The new user is linked back to the application.

Architecture

LayerChoice
FrontendNuxt (App Router-style pages), Vue, Tailwind, shadcn/Reka UI
APIFastify, file-based /api routes, PostgreSQL (pg)
AuthHttpOnly cookies, JWT, tenant resolution by host / custom domain / slug
TenancyRow-level tenantId on every resource; CORS and cookies for custom domains
AccessRBAC + purchasable modules (requireTenantModule)
FilesPrivate S3/Spaces; served only through the API
BillingStripe; module catalogue with monthly pricing
DeployFrontend on Vercel; API as a long-running Node service (Docker / Coolify), auto-migrate on boot

The frontend never talks to the database. SSR and the browser call the public API with tenant headers and credentials. TRUST_PROXY and explicit CORS origins matter because tenants can sit on *.apexmedix.net or a custom host like ops.example.co.uk.

Hard problems

Tenancy and custom domains

Resolve the tenant from hostname, forwarded host, or slug, then scope every query. Cookies and CORS have to work across the marketing/admin hosts, tenant subdomains, and custom CNAMEs.

Session performance

Owner permission repair used to run on every authenticated request and could lock the database. That work was moved off the session path so logins and API calls stay fast.

Recruitment → staff

Applications and users were separate. Hire now optionally creates a real staff user, maps form fields, requires role/grade, and records hiredUserId so the same person isn’t imported twice.

Interview pool

Availability is shared, not per-applicant. Published slots have to round-trip as real ISO timestamps (Postgres timestamptz vs the browser) or the calendar shows “Invalid Date”.

Auth UX

First login, forgot/reset, and forced 2FA had to match the branded login shell and actually complete (middleware was bouncing “change password” back to the dashboard).

Outcome

Nexus is a production operations platform, not a demo: live tenants, module billing, branded portals, and a path from applicant to rostered staff. The idea was simple — one place to run the company — and the work was making tenancy, permissions, and those operational workflows hold up in production.

Stack

Nuxt 3 · Vue · Tailwind · Fastify · PostgreSQL · JWT cookies · Stripe · AWS S3 / DigitalOcean Spaces · Meta WhatsApp Cloud API · Vercel · Docker / Coolify