API Reference

This document provides complete API documentation for OpaqueId, including all methods, classes, and configuration options.

  • TOC

Core Module: OpaqueId

The main module for generating opaque IDs.

Constants

SLUG_LIKE_ALPHABET

Default alphabet for ID generation.

OpaqueId::SLUG_LIKE_ALPHABET
# => "0123456789abcdefghijklmnopqrstuvwxyz"

Characteristics:

  • Length: 36 characters
  • Characters: 0-9, a-z
  • Use case: URL-friendly, double-click selectable
  • Performance: Good

STANDARD_ALPHABET

Standard alphabet for high-performance generation.

OpaqueId::STANDARD_ALPHABET
# => "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"

Characteristics:

  • Length: 64 characters
  • Characters: A-Z, a-z, 0-9, -, _
  • Use case: High performance, URL-safe
  • Performance: Best (optimized path)

Methods

generate

Generates a cryptographically secure opaque ID.

OpaqueId.generate(size: 21, alphabet: ALPHANUMERIC_ALPHABET)

Parameters:

Parameter Type Default Description
size Integer 21 Length of the generated ID
alphabet String ALPHANUMERIC_ALPHABET Character set for generation

Returns: String - The generated opaque ID

Raises:

  • ConfigurationError - If size is not positive or alphabet is empty
  • GenerationError - If ID generation fails

Examples:

# Generate with default parameters
id = OpaqueId.generate
# => "V1StGXR8_Z5jdHi6B-myT"

# Generate with custom length
id = OpaqueId.generate(size: 10)
# => "V1StGXR8_Z5"

# Generate with custom alphabet
id = OpaqueId.generate(alphabet: OpaqueId::STANDARD_ALPHABET)
# => "V1StGXR8_Z5jdHi6B-myT"

# Generate with both custom parameters
id = OpaqueId.generate(size: 15, alphabet: "0123456789")
# => "123456789012345"

Algorithm Selection:

  • Fast Path: Used for 64-character alphabets (bitwise optimization)
  • Unbiased Path: Used for other alphabets (rejection sampling)
  • Direct Repetition: Used for single-character alphabets

ActiveRecord Concern: OpaqueId::Model

Provides ActiveRecord integration for automatic opaque ID generation.

Class Methods

opaque_id_column

Gets or sets the column name for storing opaque IDs.

self.opaque_id_column = :public_id
opaque_id_column
# => :public_id

Default: :opaque_id

Returns: Symbol - The column name

opaque_id_length

Gets or sets the length of generated opaque IDs.

self.opaque_id_length = 15
opaque_id_length
# => 15

Default: 21

Returns: Integer - The ID length

opaque_id_alphabet

Gets or sets the alphabet for ID generation.

self.opaque_id_alphabet = OpaqueId::STANDARD_ALPHABET
opaque_id_alphabet
# => "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"

Default: OpaqueId::ALPHANUMERIC_ALPHABET

Returns: String - The alphabet

opaque_id_require_letter_start

Gets or sets whether IDs must start with a letter.

self.opaque_id_require_letter_start = true
opaque_id_require_letter_start
# => true

Default: false

Returns: Boolean - Whether letter start is required

opaque_id_max_retry

Gets or sets the maximum retry attempts for collision handling.

self.opaque_id_max_retry = 5
opaque_id_max_retry
# => 5

Default: 3

Returns: Integer - Maximum retry attempts

opaque_id_purge_chars

Gets or sets characters to exclude from generated IDs.

self.opaque_id_purge_chars = ['0', 'O', 'l', 'I']
opaque_id_purge_chars
# => ['0', 'O', 'l', 'I']

Default: []

Returns: Array<String> - Characters to exclude

find_by_opaque_id

Finds a record by its opaque ID.

User.find_by_opaque_id("V1StGXR8_Z5jdHi6B-myT")

Parameters:

Parameter Type Description
opaque_id String The opaque ID to search for

Returns: ActiveRecord::Base or nil - The found record or nil

Examples:

# Find existing user
user = User.find_by_opaque_id("V1StGXR8_Z5jdHi6B-myT")
# => #<User id: 1, opaque_id: "V1StGXR8_Z5jdHi6B-myT", ...>

# Find non-existent user
user = User.find_by_opaque_id("nonexistent")
# => nil

find_by_opaque_id!

Finds a record by its opaque ID, raising an exception if not found.

User.find_by_opaque_id!("V1StGXR8_Z5jdHi6B-myT")

Parameters:

Parameter Type Description
opaque_id String The opaque ID to search for

Returns: ActiveRecord::Base - The found record

Raises:

  • ActiveRecord::RecordNotFound - If no record is found

Examples:

# Find existing user
user = User.find_by_opaque_id!("V1StGXR8_Z5jdHi6B-myT")
# => #<User id: 1, opaque_id: "V1StGXR8_Z5jdHi6B-myT", ...>

# Find non-existent user
user = User.find_by_opaque_id!("nonexistent")
# => ActiveRecord::RecordNotFound: Couldn't find User with opaque_id 'nonexistent'

Instance Methods

opaque_id

Gets the opaque ID for the record.

user.opaque_id
# => "V1StGXR8_Z5jdHi6B-myT"

Returns: String - The opaque ID

Callbacks

before_create :set_opaque_id

Automatically generates an opaque ID before creating a record.

class User < ApplicationRecord
  include OpaqueId::Model
end

user = User.new(name: "John Doe")
user.save!
puts user.opaque_id
# => "V1StGXR8_Z5jdHi6B-myT"

Rails Generator: OpaqueId::Generators::InstallGenerator

Rails generator for setting up OpaqueId in your application.

Usage

rails generate opaque_id:install ModelName [options]

Arguments

Argument Type Required Description
model_name String Yes Name of the model to set up

Options

Option Type Default Description
--column-name String opaque_id Column name for storing the opaque ID

Examples

# Basic usage
rails generate opaque_id:install User

# Custom column name
rails generate opaque_id:install User --column-name=public_id

# Multiple models
rails generate opaque_id:install User
rails generate opaque_id:install Post --column-name=slug
rails generate opaque_id:install Comment

Generated Files

Migration

Creates a migration to add the opaque ID column:

class AddOpaqueIdToUsers < ActiveRecord::Migration[8.0]
  def change
    add_column :users, :opaque_id, :string
    add_index :users, :opaque_id, unique: true
  end
end

Model Update

Updates the model file to include the concern:

class User < ApplicationRecord
  include OpaqueId::Model
end

With custom column name:

class User < ApplicationRecord
  include OpaqueId::Model
  self.opaque_id_column = :public_id
end

Error Classes

OpaqueId::Error

Base error class for all OpaqueId errors.

OpaqueId::Error
# => StandardError

OpaqueId::ConfigurationError

Raised when configuration parameters are invalid.

OpaqueId::ConfigurationError
# => OpaqueId::Error

Examples:

# Invalid size
OpaqueId.generate(size: 0)
# => OpaqueId::ConfigurationError: Size must be positive

# Empty alphabet
OpaqueId.generate(alphabet: "")
# => OpaqueId::ConfigurationError: Alphabet cannot be empty

OpaqueId::GenerationError

Raised when ID generation fails.

OpaqueId::GenerationError
# => OpaqueId::Error

Examples:

# Collision handling failure
class User < ApplicationRecord
  include OpaqueId::Model
  self.opaque_id_max_retry = 0
end

user = User.create!(name: "John Doe")
# => OpaqueId::GenerationError: Failed to generate opaque ID after 0 retries

Configuration Examples

Model Configuration

class User < ApplicationRecord
  include OpaqueId::Model

  # Custom column name
  self.opaque_id_column = :public_id

  # Custom length
  self.opaque_id_length = 15

  # Custom alphabet
  self.opaque_id_alphabet = OpaqueId::STANDARD_ALPHABET

  # Require letter start
  self.opaque_id_require_letter_start = true

  # Max retry attempts
  self.opaque_id_max_retry = 5

  # Purge characters
  self.opaque_id_purge_chars = ['0', 'O', 'l', 'I']
end

Global Configuration

# config/initializers/opaque_id.rb
OpaqueId.configure do |config|
  config.default_length = 15
  config.default_alphabet = OpaqueId::STANDARD_ALPHABET
end

Usage Examples

Basic Usage

# Generate standalone ID
id = OpaqueId.generate
# => "V1StGXR8_Z5jdHi6B-myT"

# Generate with custom parameters
id = OpaqueId.generate(size: 10, alphabet: "0123456789")
# => "1234567890"

ActiveRecord Integration

class User < ApplicationRecord
  include OpaqueId::Model
end

# Create user with automatic ID generation
user = User.create!(name: "John Doe")
puts user.opaque_id
# => "V1StGXR8_Z5jdHi6B-myT"

# Find by opaque ID
found_user = User.find_by_opaque_id(user.opaque_id)
# => #<User id: 1, opaque_id: "V1StGXR8_Z5jdHi6B-myT", ...>

Custom Configuration

class Order < ApplicationRecord
  include OpaqueId::Model

  # Numeric order numbers
  self.opaque_id_column = :order_number
  self.opaque_id_length = 8
  self.opaque_id_alphabet = "0123456789"
end

order = Order.create!(total: 99.99)
puts order.order_number
# => "12345678"

Error Handling

begin
  user = User.create!(name: "John Doe")
rescue OpaqueId::GenerationError => e
  Rails.logger.error "Failed to generate opaque ID: #{e.message}"
  # Handle error appropriately
end

Performance Considerations

Algorithm Selection

  • 64-character alphabets: Use fast path (bitwise operations)
  • Other alphabets: Use unbiased path (rejection sampling)
  • Single-character alphabets: Use direct repetition

Memory Usage

  • Per ID: ~50-70 bytes depending on length
  • Batch generation: Linear scaling with count
  • Garbage collection: Minimal impact

Generation Speed

  • Fast Path: Optimized for 64-character alphabets
  • Unbiased Path: Slightly slower but ensures uniform distribution
  • Scaling: Linear with ID length

Security Considerations

Entropy

  • 21-character alphanumeric: 125 bits entropy
  • 21-character standard: 126 bits entropy
  • 15-character hexadecimal: 60 bits entropy

Collision Probability

  • 1M IDs: 2.3×10⁻¹⁵ probability
  • 1B IDs: 2.3×10⁻⁹ probability
  • 1T IDs: 2.3×10⁻³ probability

Attack Resistance

  • Brute Force: Extremely long time (exponential with ID length)
  • Timing Attacks: Constant-time operations
  • Statistical Attacks: Uniform distribution

Best Practices

1. Choose Appropriate Configuration

# High security
self.opaque_id_length = 21
self.opaque_id_alphabet = OpaqueId::ALPHANUMERIC_ALPHABET

# High performance
self.opaque_id_alphabet = OpaqueId::STANDARD_ALPHABET

# Human readable
self.opaque_id_purge_chars = ['0', 'O', 'l', 'I']

2. Handle Errors Gracefully

begin
  user = User.create!(name: "John Doe")
rescue OpaqueId::GenerationError => e
  # Log error and handle appropriately
  Rails.logger.error "ID generation failed: #{e.message}"
end

3. Use Appropriate Finder Methods

# Optional lookup
user = User.find_by_opaque_id(params[:id])

# Required lookup
user = User.find_by_opaque_id!(params[:id])

4. Implement Proper Indexing

# Ensure database indexes
add_index :users, :opaque_id, unique: true

Next Steps

Now that you understand the API:

  1. Explore Getting Started for quick setup
  2. Check out Usage for practical examples
  3. Review Configuration for advanced setup
  4. Read Security for security considerations