Exid 
A Ruby gem for implementing human-friendly, prefixed identifiers for records using Base62-encoded UUIDs.
Overview
Exid provides helper methods to create external, prefixed identifiers for your records, following the pattern popularized by Stripe's API.
Similar to Stripe's IDs (like cus_12345 for customers or prod_67890 for products), Exid generates readable, prefixed identifiers that:
- Are human-friendly and can be safely exposed in URLs
- Contain semantic prefixes that indicate resource type
- Hide internal database IDs
- Maintain global uniqueness
- Are collision-resistant
- Have constant length for a consistent user experience
The core Exid::Coder.encode method accepts a string prefix with a UUID and returns an "external ID" composed of the prefix and a zero-padded Base62-encoded UUID.
For example:
prg, 018977bb-02f0-729c-8c00-2f384eccb763 => prg_02TOxMzOS0VaLzYiS3NPd9
Resources
For more information on this approach:
- Designing APIs for Humans: Object IDs
- Prefixed Base62 UUIDv7 Object IDs with Ecto
- Friendly IDs for Ruby on Rails
- Prefixed IDs
- UUID7 Ruby
- Base62 Ruby
Installation
Add this line to your application's Gemfile:
gem "exid"
Then execute:
$ bundle install
Usage
Basic Setup
Add a UUID or (preferably) UUIDv7 field to your model and include the helper module. Pass a prefix (String) and a field name (Symbol) to the Exid::Record.new method:
class User < ApplicationRecord
include Exid::Record.new("user", :uuid)
# Optional, but recommended: Use the external ID as the primary object identifier
def to_param = exid_value
end
That's all! This adds several helper methods to your model.
Example
# Create a record with a UUID
user = User.create!(uuid: "018977bb-02f0-729c-8c00-2f384eccb763")
# Access the methods
user.exid_value # => "user_02TOxMzOS0VaLzYiS3NPd9"
user.exid_prefix_name # => "user"
user.exid_field # => :uuid
The exid_handle instance method returns the last 10 characters of the identifier. This is useful for displaying a distinguishing identifier in the UI. When using UUID7, the first few characters derive from timestamps and will be similar for objects created at the same time. You can pass an integer argument to get a specific number of trailing characters.
user.exid_handle # => "OBtqZqRhLm"
user.exid_handle(6) # => "ZqRhLm"
Configuration
You can configure Exid's prefix validation at the application level. Create an initializer (e.g., config/initializers/exid.rb in Rails):
By default, prefixes are limited to 4 characters. You can provide a custom validator proc that returns true if the prefix is valid, false otherwise:
Exid.configure do |config|
config.prefix_validator = proc { it.match?(/\A[a-z]{2,6}\z/) }
end
Note: When you set a custom prefix_validator, it replaces the default 4-character length validation. Your validator has full control over validation logic.
Loading Records by External ID
Use the class method exid_loader to load a record using its external ID:
User.exid_loader("user_02TOxMzOS0VaLzYiS3NPd9")
# Raises ActiveRecord::RecordNotFound if not found
# Raises NoMatchingPatternError if ID format is invalid
The Exid::Record module also provides global loading methods that mimic Rails GlobalID functionality:
Exid::Record.fetch!("pref_02WoeojY8dqVYcAhs321rm") # Raises exception if not found
Exid::Record.fetch("pref_02WoeojY8dqVYcAhs321rm") # Returns `nil` if not found
⚠️ Security Warning: Exercise caution when using global loading methods with user-supplied identifiers, as this could lead to unexpected results or security issues if users substitute identifiers.
Note: When using this gem in Rails development mode (with eager_load set to false), ensure the model class is referenced before calling .fetch or .fetch!. This ensures the prefix is added to the global registry so the loader can identify which class is associated with the prefix.
Contributing
- Fork the repository
- Create your feature branch (
git checkout -b my-new-feature) - Commit your changes (
git commit -am 'Add some feature') - Push to the branch (
git push origin my-new-feature) - Create a new Pull Request
License
The gem is available as open source under the terms of the MIT License.