Laravel + Inertia as a Headless CMS
Running an Inertia admin panel and a JSON API off the same Laravel app — where the seams are, and how to keep the two consumers from fighting each other.

The setup
One Laravel app, two front doors. Editors get an Inertia + Vue admin panel with full server-side routing. The public portfolio gets plain JSON over HTTP. Same models, same validation, same authorization.
The failure mode people hit is letting one consumer's needs leak into the other's response shape.
Separate the resource layer
Inertia pages want everything eagerly: relations, computed labels, form option lists. The public API wants a lean, stable contract.
Give them different resources.
// Admin — verbose, shaped for the form
class ProjectAdminResource extends JsonResource
{
public function toArray($request): array
{
return [
...$this->resource->toArray(),
'tags' => TagResource::collection($this->whenLoaded('tags')),
'available_tags' => Tag::pluck('name'),
];
}
}
// Public — narrow and versioned
class ProjectResource extends JsonResource
{
public function toArray($request): array
{
return [
'slug' => $this->slug,
'title' => $this->title,
'description' => $this->description,
'tags' => $this->tags->pluck('name'),
];
}
}
The shared toArray() is a trap — the moment an editor needs a new field, the
public contract silently grows.
Route groups do the rest
Route::middleware(['auth', 'inertia'])->prefix('admin')->group(...);
Route::middleware(['throttle:api'])->prefix('api/v1')->group(...);
Different middleware stacks, different rate limits, different failure formats — Inertia wants a redirect on validation failure, the API wants a 422 with a JSON body. Laravel handles both if you keep the groups honest.
When to skip the API entirely
For content that changes a few times a month — project write-ups, articles — I stopped hitting the API at build time and moved it into flat markdown in the frontend repo. No network call, no cache invalidation, no cold-start latency.
The CMS still owns the things that genuinely need a database: contact messages, draft state, anything with an editorial workflow. Everything else is a file.