Current section
Files
Jump to
Current section
Files
README.md
<img src="assets/uxid-wordmark.svg" alt="UXID" width="300">
# UXID
[![MIT License][badge_license_url]](LICENSE)
[![CI][badge_ci_url]](https://github.com/riddler/uxid-ex/actions/workflows/ci.yml)
[![Hex Version][badge_version_url]](https://hex.pm/packages/uxid)
[![Hex Downloads][badge_downloads_url]](https://hex.pm/packages/uxid)
[![Hex Docs][badge_docs_url]](https://hexdocs.pm/uxid/)
User eXperience focused IDentifiers for Elixir: prefixed, K-sortable,
Stripe-style IDs a person can read, copy and route back to their resource, with
optional Ecto types and a prefix registry. An ID looks like
`usr_01epey2p06tr1rtv07xa82zgjj`: the prefix names the resource, and the body
carries a timestamp and randomness in lowercase Crockford Base32.
## Why UXID
An auto-increment key leaks how many rows a table holds and invites
enumeration; a random UUID fixes both but is long, unordered and anonymous, so
in a log line, a URL or a support thread nobody can tell what it points at, and
a double-click selects only part of it. A UXID names its resource in its prefix,
selects whole on a double-click, reads aloud without ambiguity, sorts by
creation time so it indexes well, and is generated in the application with no
coordination between nodes. Its size is tunable for low-cardinality resources,
a monotonic mode keeps a burst within one millisecond unique and ordered, a
deterministic mode maps the same input to the same ID, and a registry keeps
every prefix in an app unique and routes an ID back to its resource.
[Why a UXID has a prefix, a time and randomness][guide_why_url] explains the
trade-offs behind these properties.
## Install
Add `uxid` to your dependencies in `mix.exs`:
```elixir
def deps do
[
{:uxid, "~> 2.9"}
]
end
```
Ecto is an optional dependency: UXID only uses it if your app already does.
## Basic usage
```elixir
# No options generates a plain ULID
UXID.generate!() # "01emdgjf0dqxqj8fm78xe97y3h"
# Add a prefix to name the resource
UXID.generate!(prefix: "cus") # "cus_01emdgjf0dqxqj8fm78xe97y3h"
# Shrink the random part for low-cardinality resources
# T-shirt sizes: :xs :s :m :l :xl (or :xsmall :small :medium :large :xlarge)
UXID.generate!(prefix: "cus", size: :small) # "cus_01eqrh884aqyy1"
# Deterministic: same input -> same id, forever (prefix is the namespace)
UXID.generate!(prefix: "usr", from: "alice@example.com")
# => "usr_zcvt7epac0t1ebcsjfyf7cwz25"
# As an Ecto field type, primary keys included, with the same options
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
end
end
```
## Documentation
- Do
- [How to use UXIDs in Ecto schemas][guide_ecto_url]: primary and foreign keys, strict `validate:` casting, `allow_uuid` coexistence, `UXID.valid?/2`, and migrating a `uuid` column.
- [How to govern prefixes with a registry][guide_registry_url]: the compile-time DSL that keeps every prefix unique, routes an ID back to its schema, works in layered and umbrella apps, and exports a JSON manifest.
- [How to tune ID generation][guide_tuning_url]: turn on monotonic generation per call, per field, per registry key or globally, and set a size floor for test suites.
- Look up
- [The API reference][hexdocs_api_url]: every public module and function.
- [Sizes & Encoding][guide_sizes_url]: the t-shirt sizes, how much randomness each carries, and compact-time mode.
- [Configuration][guide_configuration_url]: every `config :uxid` key in one place, with per-call and global precedence.
- [Registry reference][guide_registry_reference_url]: the functions a registry module defines, the options a key accepts, and the routing-table lookups.
- [Deterministic IDs reference][guide_deterministic_reference_url]: the `from:` option and the body length and hash width at each size.
- [The changelog][hexdocs_changelog_url]: what changed in each version.
- Understand
- [Why a UXID has a prefix, a time and randomness][guide_why_url]: what each part of an ID is for, and what you trade when you tune it.
- [Why monotonic mode exists and what it costs][guide_monotonic_url]: same-millisecond uniqueness and ordering, why the small sizes need it, and the security tradeoff.
- [Deterministic IDs][guide_deterministic_url]: name-based (UUIDv5-style) IDs, with the prefix as the namespace.
- [_Designing APIs for humans: object IDs_][stripe_ids_url]: the Stripe ID design many of UXID's choices follow.
- [_UXIDs in Elixir/Ecto_][uxid_talk_url]: Adam Kirk's ElixirConf US 2025 talk, the source of the registry and routing patterns.
## Compatibility
`mix.exs` declares `elixir: "~> 1.8"`. CI runs the full quality gate on the
toolchain `mise.toml` pins and the test suite alone on Elixir 1.16 / OTP 25,
the oldest pair it is tested on. There is no required runtime dependency. Ecto
is optional (`{:ecto, "~> 3.12"}`): when it is loaded, `UXID` is also an
`Ecto.ParameterizedType`; without it, everything but the Ecto type works the
same.
## License
UXID is released under the [MIT License](LICENSE).
<!-- LINKS -->
[hex_project_url]: https://hex.pm/packages/uxid
[hexdocs_api_url]: https://hexdocs.pm/uxid/api-reference.html
[hexdocs_changelog_url]: https://hexdocs.pm/uxid/changelog.html
<!-- Guide links are absolute HexDocs URLs on purpose. A relative link like
`guides/registry.md` renders correctly on GitHub and is rewritten to
`registry.html` by ExDoc, but hex.pm rewrites it to the raw-file preview
(repo.hex.pm/preview/.../registry.md), which serves plain-text Markdown. -->
[guide_sizes_url]: https://hexdocs.pm/uxid/sizes.html
[guide_ecto_url]: https://hexdocs.pm/uxid/ecto.html
[guide_monotonic_url]: https://hexdocs.pm/uxid/monotonic.html
[guide_deterministic_url]: https://hexdocs.pm/uxid/deterministic.html
[guide_registry_url]: https://hexdocs.pm/uxid/registry.html
[guide_configuration_url]: https://hexdocs.pm/uxid/configuration.html
[guide_why_url]: https://hexdocs.pm/uxid/why-prefix-time-randomness.html
[guide_tuning_url]: https://hexdocs.pm/uxid/tuning.html
[guide_registry_reference_url]: https://hexdocs.pm/uxid/registry-reference.html
[guide_deterministic_reference_url]: https://hexdocs.pm/uxid/deterministic-reference.html
[mit_license_url]: http://opensource.org/licenses/MIT
[uxid_talk_url]: https://www.youtube.com/watch?v=YIIJClhjxOA
[stripe_ids_url]: https://dev.to/stripe/designing-apis-for-humans-object-ids-3o5a
<!-- BADGES -->
[badge_license_url]: https://img.shields.io/badge/license-MIT-brightgreen.svg?cacheSeconds=3600?style=flat-square
[badge_ci_url]: https://github.com/riddler/uxid-ex/actions/workflows/ci.yml/badge.svg
[badge_downloads_url]: https://img.shields.io/hexpm/dt/uxid?style=flat&logo=elixir
[badge_version_url]: https://img.shields.io/hexpm/v/uxid?style=flat&logo=elixir
[badge_docs_url]: https://img.shields.io/badge/hex-docs-lightgreen.svg