README.md

# UXID

[![MIT License][badge_license_url]](LICENSE)
[![Hex Version][badge_version_url]](https://hex.pm/packages/uxid)
[![Hex Downloads][badge_downloads_url]](https://hex.pm/packages/uxid)

**U**ser e**X**perience focused **ID**entifiers (UXIDs) are identifiers which:

* Describe the resource (aid in debugging and investigation)
* Work well with copy and paste (double clicking selects the entire ID)
* Can be shortened for low cardinality resources
* Are secure against enumeration attacks
* Can be generated by application code (not tied to the datastore)
* Are K-sortable (lexicographically sortable by time - works well with datastore indexing)
* Do not require any coordination (human or automated) at startup, or generation
* Are very unlikely to collide (more likely with less randomness)
* Are easily and accurately transmitted to another human using a telephone

Many of the concepts of Stripe IDs have been used in this library.

## Usage

### Generating UXIDs

```elixir
# No options generates a basic ULID
UXID.generate! # "01emdgjf0dqxqj8fm78xe97y3h"

# A prefix can be provided
UXID.generate! prefix: "cus" # "cus_01emdgjf0dqxqj8fm78xe97y3h"

# The amount of randomness can be decreased for smaller cardinality resources
# T-Shirt sizes can be used (xs, s, m, l, xl) or (xsmall, small, medium, large, xlarge)
UXID.generate! prefix: "cus", size: :small # "cus_01eqrh884aqyy1"

# Compact time mode trades timestamp precision for more randomness (good for collision resistance)
UXID.generate! prefix: "sess", size: :small, compact_time: true # "sess_kf3ng7s1mf41b"

# Uppercase can be used to match previous UXID versions
UXID.generate! case: :upper # "01EMDGJF0DQXQJ8FM78XE97Y3H"
```

### Ecto

UXIDs can be used as Ecto fields including primary keys.

```elixir
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
end
```

### Configuration

#### Case

The `:case` config option controls the default case for generated UXIDs. By default, UXIDs are lowercase (`:lower`), but you can configure uppercase (`:upper`) globally or per-call.

```elixir
# config/config.exs
config :uxid, case: :upper

# All generated UXIDs will be uppercase by default
UXID.generate!()
# => "01EMDGJF0DQXQJ8FM78XE97Y3H"

# Override per-call if needed
UXID.generate!(case: :lower)
# => "01emdgjf0dqxqj8fm78xe97y3h"
```

#### Minimum Size

The `:min_size` config option enforces a minimum UXID size regardless of what size is requested. This is useful in test environments where many IDs are generated rapidly, as smaller sizes have limited randomness that can cause duplicate key violations.

```elixir
# config/test.exs
config :uxid, min_size: :medium

# In application code - requests :small but gets :medium in test env
UXID.generate!(prefix: "usr", size: :small)
# => Returns 18 character UXID instead of 14
```

When configured, any requested size smaller than `:min_size` will be automatically upgraded. Larger sizes are not affected.

#### Compact Time

The `:compact_small_times` config option and per-call `compact_time` option provide improved collision resistance for small UXIDs by using shorter timestamps and more randomness.

**Global Policy:**

```elixir
# config/test.exs
config :uxid, compact_small_times: true

# Automatically compacts :xs/:xsmall and :s/:small sizes
UXID.generate!(size: :small)
# => 13 chars (8 time + 5 rand = 24 bits random vs 16 bits standard)
```

**Per-Call Override:**

```elixir
# Override for any size - works even when global policy is off
UXID.generate!(size: :large, compact_time: true)
# => 21 chars with extra randomness

# Opt out of global policy for specific calls
UXID.generate!(size: :small, compact_time: false)
# => 14 chars with standard randomness
```

**In Ecto Schemas:**

```elixir
defmodule YourApp.Session do
  use Ecto.Schema

  @primary_key {:id, UXID, autogenerate: true, prefix: "sess", size: :small, compact_time: true}
  schema "sessions" do
    # This session ID will always use compact mode for better collision resistance
  end
end
```

**How it works:**

- Reduces timestamp from 48 bits (10 chars) to 40 bits (8 chars)
- Frees 8 bits for additional randomness (e.g., :small gets 24 bits vs 16 bits)
- Perfect 5-bit Crockford Base32 alignment
- K-sortability maintained until ~September 2039
- Decoder automatically detects compact format and reconstructs full timestamp using epoch inference

**When to use:**

- Test environments with rapid ID generation
- Resources with small cardinality that need better collision resistance
- Any scenario where you want to maximize randomness within a given length constraint

## Installation

The package can be installed by adding `uxid` to your list of dependencies in `mix.exs`:

```elixir
def deps do
  [
    {:uxid, "~> 2.3"}
  ]
end
```

Online documenttion can be found at [https://hexdocs.pm/uxid][hexdocs_project_url].


<!-- LINKS -->
[hex_project_url]: https://hex.pm/packages/uxid
[hexdocs_project_url]: https://hexdocs.pm/uxid
[mit_license_url]: http://opensource.org/licenses/MIT

<!-- BADGES -->
[badge_license_url]: https://img.shields.io/badge/license-MIT-brightgreen.svg?cacheSeconds=3600?style=flat-square
[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