Laravel: UUID for public, ID for the application

A nice way to obfuscate, without taking away the default ID.

Italo Baeza
Apr 1 · 3 min read
Photo by Kyle Glenn on Unsplash

In some applications, hiding the ID of your Model from the public is a good way to also hide how many of that model you have stored.

Okay, look. Imagine that in my Podcast application I have a set the Podcast model with the default ID of Laravel, which is just a integer that grows by one each time a row is inserted, so the 47th podcast in the table has the ID of — you guessed it — 47. Then I claim inside my website: “This Podcast App holds millions of podcasts, so don’t miss out!”, something that can be easily debunked when seeing the latest Podcast ID:

https://podcast.app/podcasts/47

Wouldn’t be nice to hide the ID without having to practically re-wire everything in our application, and hopefully keep our lie incomprobable? Okay, there is one way.

Setting up the Database

Some databases can be configured to set an UUID into the primary key when inserting a new row. You should check this on whatever RDBMS are you using, since implementation vary between each of them.

You can also tell your application to set a default UUID when creating a new record using the creating Eloquent event, but that will leave you using Eloquent for everything. If you insert a record directly to the database, you may get an error — one of the reasons why I prefer to set automatic UUIDs inside the Database instead of the application; it shouldn’t depend on anything since is a Database behaviour.

Anyway, you should set your database using Laravel like this:

As you can see, we have added a $table->uuid('uuid')->index(). This code tells Laravel to use an UUID column (if supported, or use a string column), and create an index on it so the row can be quickly retrieved by its UUID, like you would when somebody access to this URL:

https://podcast.app/podcast/535c4cdf-70a0-4615-82f2-443e95c86aec

You may argue that having another index would hinder inserting operations, but is a trade off.

Now, the problem lies with the Controller and the Model.

Wiring the Model to the UUID

You don’t have to do anything in the Model, except for two things: hide the ID from serialization, and allow the Model to be “url-routable” using the UUID.

To hide the ID, we can just push the id column into the hidden attributes. When the Model is serialized, like when it transforms to an array or a JSON string, the ID won’t be shown. Of course, you can access the ID getting the attribute directly like a property or array key.

$podcast = Podcast::first();// This will work
echo $podcast->id; // 47
// But this won't
echo $podcast->toArray()['id']; // null
echo json_decode($podcast->toJson())->id; // ERROR

The next step is to tell Laravel that, when the URL contains the UUID of the Model, get that model by the uuid column. This will allow to find the model by setting it inside a route, like

Route::get({podcast}, 'PodcastController@show');

and then using it in our PodcastController class.

/**
* Show the Podcast by its UUID
*
* @param \App\Podcast $podcast
* @return \Illuminate\Http\Response
/*
public function show(Podcast $podcast)
{
return response()->view('response', [
'podcast' => $podcast
]);
}

You can also play with the resolveRouteBinding() method in your Model, were you can programatically set how to retrieve the model by the value it’s given. You could even allow authenticated administrators to pull the record by its ID or UUID.

And that’s all. Nothing more to do. This technique also allows to set an UUID after your application is production, since you only need to add that column with an index, and then set this code.

Italo Baeza

Written by

Graphic Designer. Web Developer Full Stack. Retired Tech & Gaming Editor.