Central API Platform.
Decided and directed a three-year move of BMI Global Ed's systems onto one shared API platform: seven modules, 200+ REST endpoints, and the legacy PHP API retired.
One backend instead of seven
Each product line risked rebuilding its own backend, data access and integrations. The platform gives all of them one governed integration boundary instead.
Seven business modules
Business logic, data access and authentication for every product line, from the event booking CRM and reporting to the mobile app and the B2C platform, in one Node.js and Express codebase. A shared-services module absorbs the legacy API's scripts, webhooks and small utilities that belong to no single product.
REST and GraphQL
Over 200 REST endpoints, with a Hasura GraphQL layer on PostgreSQL alongside them. Callers authenticate with JWT bearer tokens, keyed to one internal identity per product domain.
Background jobs
BullMQ queues on Redis take the work that should not hold up a request and run it in the background, outside the request cycle.
Payments and integrations
Adyen payment webhooks, their HMAC signatures checked with Adyen’s own validator against a key held in the environment, ActiveCampaign marketing webhooks, and file storage on AWS S3, handled once for every product.
Push notifications
Web push notifications to users' browsers, identified to the browser with VAPID keys.
A door for AI agents
A read-only Model Context Protocol (MCP) server lets AI agents query the platform's GraphQL data in natural language, with no way to change it.
Every module has the same shape
One layering convention runs through all seven modules, so anyone who knows one domain can find their way around the rest. Two data stores sit under it while the move to PostgreSQL continues.
- ROUTER
- Express routes for the module
- CONTROLLER
- Reads the request, shapes the response
- BUSINESS LOGIC
- The rules of each product
- DATA ACCESS
- MySQL pools or Hasura GraphQL
- RUNTIME
- Node.js 24, Express 4
- DATA
- MySQL moving to PostgreSQL via Hasura
- QUEUES
- BullMQ on Redis
- HOSTING
- AWS EC2 under PM2, files on S3
- ERRORS
- Sentry
Moved one system at a time
Systems moved off the legacy PHP API one at a time, while the old API kept running for the ones still waiting. After the move, every change to the platform passes checks that block a release until they pass.
The standards it runs on
The team was offshore, so the standards had to live in the process rather than in anyone's head: written specs, tests tied to them, and checks that block a merge rather than warn about it.
A spec for every behaviour
Versioned Markdown specifications, SPEC-000 to SPEC-038, each linked one to one to a test-case document, a unit test and an integration test. A separate governance repository holds 96 specifications in seven folders that mirror the seven modules.
Two layers of tests
Jest unit tests with their dependencies mocked, and Supertest integration tests against the real application: two independent layers of confidence rather than one.
Security scanning that blocks
Semgrep static analysis runs on every pull request, every push and a daily schedule. A failure blocks the merge, replacing reliance on periodic external audits.
Risk-tiered dependency updates
Renovate raises dependency upgrades automatically. Low-risk patches go through on their own; payment, authentication and cloud-SDK upgrades wait for a person to review them.
Build, deploy, observe
AWS CodeBuild builds each release and CodeDeploy rolls it out to EC2, where PM2 runs the process. Sentry reports errors from production as they happen.
Migration signed off on evidence
The MySQL to PostgreSQL move ran as its own changelog-documented programme beside feature work, with no delivery freeze. Core specs were re-verified against the migrated system and API contracts checked against live platform metadata before sign-off.
What I led
Led a team of three on this platform (the wider offshore team peaked at five), who designed, built and run it, day to day through verbal direction, written specifications and wireframes. The calls below were mine.
- Decided and drove the migration of five consumer systems off a legacy PHP API onto the platform, backed by a 96-specification quality-governance programme, sequencing the systems and keeping the legacy contract alive throughout.
- Managed the offshore team that built and runs the platform, through daily stand-ups, task assignment, written specifications and wireframes.
- Oversaw a platform-wide database migration from MySQL to PostgreSQL, with a Hasura GraphQL layer alongside the REST API.
- Architected the platform’s AI tooling, which the team built: a read-only MCP server giving AI agents natural-language queries against the GraphQL data layer, and custom agents that automate the release pipeline up to QA-gated production cherry-picks.
- Authored a cross-repository tooling recommendations document covering CI automation, contract testing, OpenAPI conversion, and dependency and security scanning.
- Contributed targeted fixes, including retry-with-fallback logic that resolved intermittent QR-badge generation failures.
Tech stack
Say hello.
Message sent.
Thanks for getting in touch.