TEMPORARY: Dual ID lookup utilities for UUID migration transition.
Transitional Module
This module exists ONLY for the transition period while PhoenixKit migrates from bigserial to UUID primary keys. It will be removed in PhoenixKit 2.0 when all tables use UUID as the primary key.
- For UUID generation, use
UUIDv7.generate()directly - For lookups, use standard
Repo.get/2with UUIDs after 2.0
DELETE THIS MODULE when PhoenixKit switches to UUID-native PKs.
Purpose
This module provides helper functions for working with dual ID systems (bigserial + UUID column) during the transition period. It enables parent applications to accept either integer IDs or UUIDs in URLs/APIs.
When to Use
Use this module in parent application controllers/LiveViews when you want to accept either ID type in user-facing URLs:
# In your controller - accepts /users/5 OR /users/019b57...
def show(conn, %{"id" => identifier}) do
user = PhoenixKit.UUID.get(User, identifier)
endWhen NOT to Use
- PhoenixKit internal code still uses integer IDs for all operations
- Foreign key relationships remain integer-based
- If you know you have an integer ID, use
Repo.get/2directly
Usage Examples
# Dual lookup (auto-detects type)
PhoenixKit.UUID.get(User, "123") # integer lookup
PhoenixKit.UUID.get(User, "019b57...") # UUID lookup
# With multi-tenant prefix
PhoenixKit.UUID.get(User, id, prefix: "tenant_123")
# UUID generation (prefer UUIDv7.generate() directly)
PhoenixKit.UUID.generate()Migration Timeline
- V40 (current): UUID columns added, this helper available
- Transition: Parent apps can start using UUIDs in URLs
- 2.0: UUID becomes PK, this module is deleted
Summary
Functions
Extracts the integer ID from a record.
Extracts the UUID from a record, returning nil if not present.
Generates a new UUIDv7 string.
Gets a record by either integer ID or UUID string.
Gets a record by either integer ID or UUID string, raising if not found.
Gets a record by its integer ID.
Gets a record by its UUID.
Checks if an identifier is an integer ID (vs UUID).
Parses an identifier and returns its type.
Returns the preferred identifier for a record.
Checks if an identifier is a UUID (vs integer).
Checks if a string is a valid UUID format.
Functions
Extracts the integer ID from a record.
Examples
iex> PhoenixKit.UUID.extract_id(%User{id: 123})
123
Extracts the UUID from a record, returning nil if not present.
Examples
iex> PhoenixKit.UUID.extract_uuid(%User{uuid: "019b5704-..."})
"019b5704-..."
iex> PhoenixKit.UUID.extract_uuid(%User{uuid: nil})
nil
@spec generate() :: String.t()
Generates a new UUIDv7 string.
UUIDv7 is a time-ordered UUID where the first 48 bits are a Unix timestamp in milliseconds, providing natural chronological ordering.
Examples
iex> PhoenixKit.UUID.generate()
"019b5704-3680-7b95-9d82-ef16127f1fd2"
Gets a record by either integer ID or UUID string.
Automatically detects the identifier type and performs the appropriate lookup.
Returns nil if the record is not found.
Parameters
schema- The Ecto schema moduleidentifier- Either an integer ID, string integer, or UUID stringopts- Optional keyword list::prefix- Schema prefix for multi-tenant databases:repo- Override the repository to use
Examples
iex> PhoenixKit.UUID.get(User, 123)
%User{id: 123, uuid: "..."}
iex> PhoenixKit.UUID.get(User, "123")
%User{id: 123, uuid: "..."}
iex> PhoenixKit.UUID.get(User, "019b5704-3680-7b95-9d82-ef16127f1fd2")
%User{id: 123, uuid: "019b5704-3680-7b95-9d82-ef16127f1fd2"}
iex> PhoenixKit.UUID.get(User, "nonexistent")
nil
# With prefix for multi-tenant schemas
iex> PhoenixKit.UUID.get(User, "123", prefix: "tenant_abc")
%User{id: 123, uuid: "..."}
Gets a record by either integer ID or UUID string, raising if not found.
Examples
iex> PhoenixKit.UUID.get!(User, "019b5704-3680-7b95-9d82-ef16127f1fd2")
%User{uuid: "019b5704-3680-7b95-9d82-ef16127f1fd2"}
iex> PhoenixKit.UUID.get!(User, "nonexistent")
** (Ecto.NoResultsError)
Gets a record by its integer ID.
Options
:prefix- Schema prefix for multi-tenant databases:repo- Override the repository to use
Examples
iex> PhoenixKit.UUID.get_by_id(User, 123)
%User{id: 123}
iex> PhoenixKit.UUID.get_by_id(User, 999999)
nil
iex> PhoenixKit.UUID.get_by_id(User, 123, prefix: "tenant_abc")
%User{id: 123}
Gets a record by its UUID.
Options
:prefix- Schema prefix for multi-tenant databases:repo- Override the repository to use
Examples
iex> PhoenixKit.UUID.get_by_uuid(User, "019b5704-3680-7b95-9d82-ef16127f1fd2")
%User{uuid: "019b5704-3680-7b95-9d82-ef16127f1fd2"}
iex> PhoenixKit.UUID.get_by_uuid(User, "nonexistent-uuid")
nil
iex> PhoenixKit.UUID.get_by_uuid(User, "019b5704-...", prefix: "tenant_abc")
%User{uuid: "019b5704-..."}
Checks if an identifier is an integer ID (vs UUID).
Examples
iex> PhoenixKit.UUID.integer_id?(123)
true
iex> PhoenixKit.UUID.integer_id?("123")
true
iex> PhoenixKit.UUID.integer_id?("019b5704-3680-7b95-9d82-ef16127f1fd2")
false
@spec parse_identifier(integer() | String.t()) :: {:integer, integer()} | {:uuid, String.t()} | :invalid
Parses an identifier and returns its type.
Examples
iex> PhoenixKit.UUID.parse_identifier(123)
{:integer, 123}
iex> PhoenixKit.UUID.parse_identifier("123")
{:integer, 123}
iex> PhoenixKit.UUID.parse_identifier("019b5704-3680-7b95-9d82-ef16127f1fd2")
{:uuid, "019b5704-3680-7b95-9d82-ef16127f1fd2"}
iex> PhoenixKit.UUID.parse_identifier("invalid")
:invalid
Returns the preferred identifier for a record.
Prefers UUID if available, falls back to integer ID.
Examples
iex> PhoenixKit.UUID.preferred_identifier(%User{id: 123, uuid: "019b5704-..."})
"019b5704-..."
iex> PhoenixKit.UUID.preferred_identifier(%User{id: 123, uuid: nil})
123
Checks if an identifier is a UUID (vs integer).
Examples
iex> PhoenixKit.UUID.uuid?("019b5704-3680-7b95-9d82-ef16127f1fd2")
true
iex> PhoenixKit.UUID.uuid?("123")
false
iex> PhoenixKit.UUID.uuid?(123)
false
Checks if a string is a valid UUID format.
Works with both UUIDv4 and UUIDv7 formats.
Examples
iex> PhoenixKit.UUID.valid_uuid?("019b5704-3680-7b95-9d82-ef16127f1fd2")
true
iex> PhoenixKit.UUID.valid_uuid?("not-a-uuid")
false