12 Comments

How to use UUIDs in URLs in Laravel

In my previous post, I told why you shouldn't expose your default incrementing IDs, because of security and other issues. Luckily there are some solutions for this problem: slugs and UUIDs.

If you want to read more about slugs, you can read more about it here.

In this post, I'm going to show you how you can configure UUIDs with Laravel. Then you'll get URLs like these:

Migrations

First, we'll have to add a uuid column in the migration. Laravel has a UUID column available that we can use.

// database/migrations/create_posts_table.php

Schema::create('posts',  function  (Blueprint  $table)  {
    $table->id();
    $table->uuid('uuid')->unique(); // <-- Add the UUID column in your migration
    $table->string('title');
    $table->text('body');
    $table->timestamps();
});

Models

Next, every time a model (for example a post) is created, we want to automatically assign a UUID to it. We can do that in the boot model of our model.

// app/Models/Post.php

use Illuminate\Support\Str;

protected  static  function  boot()
{
    parent::boot();
    
    static::creating(function  ($model)  {
        $model->uuid = (string) Str::uuid();
    });
}

Factories

If you are using model factories to quickly create test models, you don't have to add anything to them because the boot method will run when using factories too.

Routing

The last step is to update the routes file. Every time you use Route Model Binding in your routes/web.php file add :uuid at the end:

// routes/web.php

Route::get('/posts/{post:uuid}', [PostController::class, 'show'])
    ->name('posts.show');

Now Laravel will automatically use the UUID column for routing.

URLs

If you use URLs, you'll have to update your routes everywhere. For example:

// resources/views/posts/index.blade.php

<ul>
    @foreach($posts as $post)
        <li>
            <a href="{{ url('/posts/' . $post->uuid) }}">
                {{ $post->title }}
            </a>
        </li>
    @endforeach
</ul>

route helper

If you're using the route helper method, you don't have to change anything! For example:

// resources/views/posts/index.blade.php

<ul>
    @foreach($posts as $post)
        <li>
            <a href="{{ route('posts.show', $post) }}">
                {{ $post->title }}
            </a>
        </li>
    @endforeach
</ul>

Conclusion

As you can see, using Laravel it's very easy to use UUIDs and hide your incrementing IDs. I hope you liked this post, and if you did, you can subscribe to my newsletter below.

Share this article:

Subscribe to my newsletter

Continue reading:

Why you shouldn't expose your incrementing IDs

By default, every Laravel model has an incrementing ID. Although this makes things very easy, there are some security reasons why you shouldn't be exposing...

Leave a comment

Comments (12)

    Merel Deblauwe's avatar
    Merel Deblauwe Reply
    7 months ago

    I would suggest that you also add an index to the uuid column in your migration, for better performance

      Jeroen van Rensen's avatar
      Jeroen van Rensen
      7 months ago

      Thanks Merel Deblauwe!

      Peter's avatar
      Peter
      6 months ago

      Unnecessary if you're using unique(), it will create an index for you.

      Jeroen van Rensen's avatar
      Jeroen van Rensen
      6 months ago

      Hi Peter, yes you're right! Thanks for letting me know!

    Ali's avatar
    Ali Reply
    7 months ago

    Hi. If UUIDs are going to be unique, what is the purpose of using unique() in the migration?

      Jeroen van Rensen's avatar
      Jeroen van Rensen
      7 months ago

      Hi Ali, it’s just a personal preference to add it there explicitly, so you can remove it if you want to. Jeroen

    mikec's avatar
    mikec Reply
    7 months ago

    how do you do the same thing, but with a resource rather than the get or put inside the routes file? Most of my routes are using a using a resource like this: Route::middleware(['auth:sanctum', 'verified'])->resource('widget', WidgetController::class);

      Jeroen van Rensen's avatar
      Jeroen van Rensen
      7 months ago

      Hi mikec, I think you can change the route key name in your model to uuid. You can read more at the Laravel docs.

    Patrick Curl's avatar
    Patrick Curl Reply
    7 months ago

    I'm not a fan of uuid's in links. They're ugly for starters. Hashids are much better imho. There's considerations whether to keep a uuid pattern of primary keys or go with incrementing but use hashids for all public facing identification in apis/urls.

    You can still use uuids in api's, just urls I'd recommend against. I like mediums' slug method that's basically hashid-sluggified-title-of-resource.

      Jeroen van Rensen's avatar
      Jeroen van Rensen
      7 months ago

      Hi Patrick Curl,

      I did a quick search and I think you mean to use this package. It will generate IDs like y42LW46J9luq3Xq9XMly.

      Personally, I don't think that's cleaner than something like 0597f0a8-b718-4e19-b193-891adbd2daf7. So I guess it's a preference thing.

      Thanks! Jeroen

    Mahdi 's avatar
    Mahdi Reply
    6 months ago

    So, how we can make relationships between models?

      Jeroen van Rensen's avatar
      Jeroen van Rensen
      5 months ago

      Hi Mahdi, in this article I explained how to use UUIDs in your Laravel routes, but I still think it's a good idea to use "classic" IDs as the primary key, and use them for defining relationships.