Here is the full UserResource in my starter kit:
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'role' => $this->role,
'email_verified_at' => $this->email_verified_at,
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
];
}
Seven fields. One of them is a lie, or at least an IOU: email_verified_at. My kit has no email verification. No verification routes, no signed URLs, no "check your inbox" screen. For every real user of this boilerplate, that field is null, forever, until the developer adds verification themselves.
So why ship it?
Because API payload shapes are contracts, and this field is forward compatibility bought at the cheapest possible price.
The day you add verification, nothing downstream changes. The resource already emits the field. The React side already receives it. The demo mock mirrors it. Every component that reads a user object already handles a key that happens to be null today and a timestamp tomorrow. The feature lands as backend-only work: routes, mail, a controller. No frontend migration, no mock update, no "why did the user object change shape" debugging across a codebase you did not write.
Removing a field later is the expensive direction. I have seen the reverse too many times: a minimal payload ships, six months later someone adds verification, and the new field ripples through types, mocks, tests, and three components that destructure the user object. Adding a field to an API response is a breaking change for every consumer you cannot see, including consumers of a boilerplate who built on your shape. Shipping the field early means the shape was always the final shape.
Null is honest. This only works because the field is truthfully null, not faked. The resource reads it straight from the model; the model reads it from the column; the column is empty because no flow writes to it. A frontend that shows "unverified" for a null timestamp is correct on day one and stays correct. What I would never do is invent a value, like defaulting it to created_at to make the payload look complete. A placeholder value is a lie that downstream code will eventually trust.
There is a boundary to this habit, and it is worth naming: I ship the field, not the feature. The kit still has no verification flow, no verification email, no UI for it. Shipping the field says "this is where the data will live". Shipping a half-built feature says "this works", and then it does not. One of those is forward compatibility. The other is a bug report waiting to happen.
My rule of thumb for boilerplates: if a field is on the model's natural trajectory and costs nothing to emit, emit it now. Your future self, adding the feature properly, will change one backend file instead of ten files across the stack.
I deliberately left verification (and password reset, and subscriptions) out of my starter kit to keep it shippable, and I wrote about those cuts before. The payload, though, is already ready for the day verification arrives. That is the whole kit's philosophy in one field: cut the feature, keep the shape. The full starter kit is here: Laravel + React SaaS Starter Kit
Top comments (0)