Using Twitter Slowflake IDs in Laravel

By default Laravel simply uses auto incrementing integers as model IDs. So your first user will always have the ID of 1, the next will get the ID of 2 and so forth. There is nothing inherently wrong with this approach, it works well and is great for database performance.

But there are some cases where this might lead to problems, because it's fairly easy to guess these IDs. This could lead to data getting into the wrong hands, especially if you made any mistake securing your routes.

In my case the issue was different: I'm building a Saas application for a client who wants the scale of their business to be a secret. But with incremental IDs, one could simply refer to new user or request IDs and get a pretty good idea of how active the app really is.

Snowflake vs UUIDs and ULIDs

There are a couple of alternatives available natively within Laravel, namely UUID (Universal Unique Identifier) and ULID (Universally Unique Lexicographically Sortable Identifier). Both are alphanumeric strings (Example UUID: 123e4567-e89b-12d3-a456-426655440000, Example ULID: 01H0WXWP3XGAX0ZJVG7Q2E70FC) instead of integers which are very hard to guess and don't let the user know how many other entries exist in the database.

The main difference between the two is that ULID is lexicographically sortable, meaning we can determine the order that the objects where created in from that ID (similar as to how newer object have a bigger integer ID in the default system).

One disadvantage of both is that alphanumeric database fields have worse index performance compared to integers. This will affect the overall app performance given enough data and traffic, especially when using many relations and joins.

So I instead opted for a third-party solution originally created by Twitter: Slowflake IDs. These use integers instead of strings while still being sortable and not giving away any potentially sensitive information to the user, combining the best of all of the above.

Setting up Snowflake in Your Laravel Project

Now it's time to tell Laravel to use Slowflake IDs. I would recommend doing this at the start of a project when you can still wipe the database without losing actual production data. You could probably also work it into an existing project, but that would require a lot of data wrangling if you want to update existing models to new IDs.

As usual, there is already a Laravel Package we can use to do most of the work for us. After pulling it in via composer and publishing the config file to /config/snowflake.php, we can edit it and add the correct start date for our project. There is no need to edit the other available settings, unless you're planning on building a distributed system.

The package comes with a HasSnowflakePrimary trait that we can simply apply to all models that should use the new ID system.

/app/Models/User.php
<?php namespace App\Models; use Kra8\Snowflake\HasSnowflakePrimary; class User extends Authenticatable { use HasSnowflakePrimary; ... }
My simplified user model with the new trait applied.

As the documentation mentions: if you're planning on using these IDs in JavaScript, you'll be better off using the HasShortflakePrimary instead, which will create slightly smaller numbers to deal with some language limitations.

That's it already, there is no need to make any changes to the database as the Slowflake IDs are compatible with the default ID field created in standard Laravel migrations.

Working With the New Snowflake IDs

While this application is still in development, I can say that so far I have not noticed any downside to this change. After re-seeding the database everything works just as before, except that my models now have longer IDs.

(php artisan tinker)
> App\Models\User::first()->id; = 4376643753440
Checking out the ID of the first user seeded into the database with Snowflake.

The main goal is achieved: nobody will be able to guess how many users have registered with the app in total or within the recent days. And by using an integer based solution the performance degradation should be minimal.

More Posts