Unifying Web and Mobile Under One API
The web frontend had its own AJAX routes and controllers while mobile used a REST API. I eliminated 50% of controller code by making the web consume the same API.
PlanPal had two sets of controllers: web controllers returning Blade views with custom AJAX routes, and API controllers returning JSON for the mobile app. Same business logic, duplicated across both. When I fixed a bug in one, I had to remember to fix the other.
The Migration
I migrated the web frontend from custom AJAX routes to the same REST API the mobile app uses. The key was a feature flag for zero-downtime rollout:
// config/api.php
'unified_api' => env('USE_UNIFIED_API', false),
// Alpine.js component
const baseUrl = window.appConfig.useUnifiedApi ? '/api/v1' : '/planner';
async function fetchAppointments(date) {
const response = await fetch(`${baseUrl}/planner/${date}/appointments`);
return await response.json();
}
I migrated one feature at a time -- appointments first, then tasks, then contacts -- testing each before flipping the flag for the next. The old web routes stayed in place until all features were migrated.
The Result
| Metric | Before | After |
|---|---|---|
| Controller files | 45 web + 45 API | 45 API only |
| Route definitions | ~180 total | ~90 total |
| Bug fix locations | 2 (web + API) | 1 |
The standardized JSON response format ({success: true, data: {...}}) works for both Alpine.js fetch() calls and React Native API clients. One set of tests covers both platforms.
The Lesson
If your web frontend makes AJAX calls that return JSON, it's already an API consumer -- just an unofficial one. Formalizing the API and having both platforms consume it eliminates an entire class of "fixed on web but not mobile" bugs.
Built while working on PlanPal.
Back to danielships ->