Ecto Integration
View SourceUXID implements Ecto.ParameterizedType, so a UXID column is just a :string
(text) column with UXID as the field type. Ecto is an optional dependency —
UXID only uses it when your app already depends on it.
Fields and primary keys
Set the field type to UXID and pass the same options you'd give
UXID.generate!/1 (:prefix, :size, :compact_time, :monotonic, …).
autogenerate: true mints a value on insert when the field is blank.
defmodule YourApp.User do
use Ecto.Schema
@primary_key {:id, UXID, autogenerate: true, prefix: "usr", size: :medium}
schema "users" do
field :api_key, UXID, autogenerate: true, prefix: "apikey", size: :small, compact_time: true
field :api_secret, UXID, autogenerate: true, prefix: "apisecret", size: :xlarge
end
endBecause UXIDs are strings, use a text primary-key column and matching foreign keys in your migrations:
create table(:users, primary_key: false) do
add :id, :string, primary_key: true
timestamps()
end
create table(:posts, primary_key: false) do
add :id, :string, primary_key: true
add :user_id, :string
endSet @foreign_key_type UXID (or :string) on schemas that reference UXID-keyed
tables so associations cast correctly.
Strict validation
By default cast/2 accepts any binary unchanged, which is ideal while a schema
still holds a mix of legacy identifiers. Opt a field into strict validation with
validate: true. A value must then be either:
- a structurally valid UXID carrying the field's configured
:prefix, or - a legacy bare UUID string (canonical 36-character form).
Anything else — an empty string, the wrong prefix, non–Base32 characters — casts
to :error.
@primary_key {:id, UXID, autogenerate: true, prefix: "org", size: :medium, validate: true}
schema "organizations" do
field :owner_org_id, UXID, prefix: "org", validate: true
endUUID coexistence and migrating a column
UUID coexistence is on by default so a table can migrate its column type from
uuid to text without rewriting existing rows: old UUID values keep casting
while new rows get UXIDs. Once a table holds only UXIDs, turn it off with
allow_uuid: false to reject stray UUIDs:
field :id, UXID, prefix: "org", validate: true, allow_uuid: falseValidating a string without Ecto
UXID.valid?/2 checks a string's structure (prefix + Crockford Base32 body),
independent of Ecto:
UXID.valid?("org_01emdgjf0dqxqj8fm78xe97y3h") # => true
UXID.valid?("org_01emdgjf0dqxqj8fm78xe97y3h", prefix: "usr") # => false
UXID.valid?("not-a-uxid") # => falseIt validates structure, not authenticity, and deliberately does not accept
bare UUIDs — that coexistence lives only in cast/2. To centralize the
prefix/size/validate/allow_uuid options for a field so they live in one
place, see the Prefix Registry guide and its field_opts/1 hook.