PhoenixKit.UUID (phoenix_kit v1.7.43)

Copy Markdown View Source

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/2 with 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)
end

When 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/2 directly

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

  1. V40 (current): UUID columns added, this helper available
  2. Transition: Parent apps can start using UUIDs in URLs
  3. 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

extract_id(arg1)

@spec extract_id(struct()) :: integer() | nil

Extracts the integer ID from a record.

Examples

iex> PhoenixKit.UUID.extract_id(%User{id: 123})
123

extract_uuid(arg1)

@spec extract_uuid(struct()) :: String.t() | nil

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

generate()

@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"

get(schema, identifier, opts \\ [])

@spec get(module(), integer() | String.t(), keyword()) :: struct() | nil

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 module
  • identifier - Either an integer ID, string integer, or UUID string
  • opts - 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: "..."}

get!(schema, identifier, opts \\ [])

@spec get!(module(), integer() | String.t(), keyword()) :: struct()

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)

get_by_id(schema, id, opts \\ [])

@spec get_by_id(module(), integer() | String.t(), keyword()) :: struct() | nil

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}

get_by_uuid(schema, uuid, opts \\ [])

@spec get_by_uuid(module(), String.t(), keyword()) :: struct() | nil

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-..."}

integer_id?(identifier)

@spec integer_id?(any()) :: boolean()

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

parse_identifier(id)

@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

preferred_identifier(arg1)

@spec preferred_identifier(struct()) :: String.t() | integer() | nil

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

uuid?(identifier)

@spec uuid?(any()) :: boolean()

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

valid_uuid?(string)

@spec valid_uuid?(String.t()) :: boolean()

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