snortlin/nano-id
Laravel-friendly NanoID generator for PHP: create short, URL-safe, collision-resistant IDs with configurable length and alphabet. Simple API, lightweight and fast—ideal for public identifiers, tokens, and model keys without exposing sequential IDs.
Installation
composer require snortlin/nano-id
Publish the config (optional):
php artisan vendor:publish --provider="Snortlin\NanoId\NanoIdServiceProvider" --tag="nano-id-config"
First Usage Generate a default ID (21 chars, URL-safe):
use Snortlin\NanoId\NanoId;
$id = NanoId::generate(); // e.g., "V1StGXvQZ5RgxwKP7Yk3"
Laravel Integration Bind to a model trait or service:
use Snortlin\NanoId\HasNanoId;
class Post extends Model
{
use HasNanoId;
}
Auto-generates nano_id column on create() if configured.
Basic Generation
// Default (21 chars)
$id = NanoId::generate();
// Custom length
$shortId = NanoId::generate(10); // e.g., "aBc12xYz"
Custom Alphabets
// URL-safe (default)
$urlSafe = NanoId::generate();
// Custom alphabet (e.g., alphanumeric + symbols)
$customAlphabet = NanoId::generate(16, '0123456789ABCDEF');
Model Integration
// Auto-generate on create
class Invoice extends Model
{
use HasNanoId;
protected $nanoIdColumn = 'invoice_code';
protected $nanoIdLength = 12;
}
Batch Generation
$batch = NanoId::generateBatch(5); // ['id1', 'id2', ...]
$id = NanoId::generate(10, null, 'test-seed');
NanoId::generate(); // Thread-safe by design
use Snortlin\NanoId\NanoIdValidator;
$isValid = NanoIdValidator::validate($id, 21);
Database Schema
Use string type with length matching your config (e.g., string(21) for default).
Add a unique index for collision safety:
Schema::table('posts', function (Blueprint $table) {
$table->string('nano_id', 21)->unique();
});
Migration Hooks
Override boot() to auto-generate IDs:
class Post extends Model
{
protected static function boot()
{
parent::boot();
static::creating(function ($model) {
if (empty($model->nano_id)) {
$model->nano_id = NanoId::generate(config('nano-id.length'));
}
});
}
}
API Responses Use the IDs in routes/slugs:
Route::get('/posts/{nano_id}', [PostController::class, 'show']);
Collision Risk
do {
$id = NanoId::generate();
} while (Model::where('nano_id', $id)->exists());
Alphabet Customization
l1O0) in custom alphabets.Performance
generateBatch()) is not parallelized by default. For high throughput, use multiple processes.Config Overrides
config/nano-id.php) takes precedence over defaults.'length' => 16,
'alphabet' => '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
'seed' => env('NANO_ID_SEED'),
Validate IDs Use the validator to check malformed IDs:
NanoIdValidator::validate($id); // Returns bool
Log Collisions Add a middleware to log duplicate IDs:
public function handle($request, Closure $next)
{
if (Model::where('nano_id', $request->nano_id)->exists()) {
Log::warning("NanoID collision detected: {$request->nano_id}");
}
return $next($request);
}
Seed Consistency
Custom Storage
Extend the NanoId facade to persist IDs:
class CustomNanoId extends NanoId
{
public static function generateAndStore()
{
$id = parent::generate();
Model::create(['nano_id' => $id]);
return $id;
}
}
Event Triggers Dispatch events on ID generation:
NanoId::generating(function ($length, $alphabet, $seed) {
event(new NanoIdGenerating($length, $alphabet, $seed));
});
Fallback for DB Conflicts Implement a retry logic:
$attempts = 0;
$maxAttempts = 3;
do {
$id = NanoId::generate();
$attempts++;
} while ($attempts < $maxAttempts && Model::where('nano_id', $id)->exists());
How can I help you explore Laravel packages today?