{"id":30376589,"url":"https://github.com/juliolinarez/ex_cuid2","last_synced_at":"2025-10-11T11:17:48.435Z","repository":{"id":304129987,"uuid":"1017283188","full_name":"juliolinarez/ex_cuid2","owner":"juliolinarez","description":"An Elixir implementation of CUID2 (Collision-Resistant Unique Identifiers).","archived":false,"fork":false,"pushed_at":"2025-08-01T01:20:25.000Z","size":24,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-09-22T03:44:23.455Z","etag":null,"topics":["cuid2","elixir","phoenix-framework"],"latest_commit_sha":null,"homepage":"","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/juliolinarez.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-07-10T09:48:20.000Z","updated_at":"2025-08-01T01:20:29.000Z","dependencies_parsed_at":"2025-07-11T11:34:06.849Z","dependency_job_id":"9abf6ecc-7667-42dd-a041-b0a4050736d8","html_url":"https://github.com/juliolinarez/ex_cuid2","commit_stats":null,"previous_names":["juliolinarez/ex_cuid2"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/juliolinarez/ex_cuid2","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/juliolinarez%2Fex_cuid2","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/juliolinarez%2Fex_cuid2/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/juliolinarez%2Fex_cuid2/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/juliolinarez%2Fex_cuid2/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/juliolinarez","download_url":"https://codeload.github.com/juliolinarez/ex_cuid2/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/juliolinarez%2Fex_cuid2/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279007034,"owners_count":26084227,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-11T02:00:06.511Z","response_time":55,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cuid2","elixir","phoenix-framework"],"created_at":"2025-08-20T15:01:09.486Z","updated_at":"2025-10-11T11:17:48.430Z","avatar_url":"https://github.com/juliolinarez.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n# ExCuid2\n\n[![Hex.pm](https://img.shields.io/hexpm/v/ex_cuid2.svg)](https://hex.pm/packages/ex_cuid2)\n[![Hex Docs](https://img.shields.io/badge/hex-docs-lightgreen.svg)](https://hexdocs.pm/ex_cuid2/)\n\n`ExCuid2` is a robust, thread-safe, and testable CUID2 (Collision-Resistant Unique Identifiers) generator for Elixir.\n\nThis implementation follows the CUID2 standard and generates safe, horizontally scalable IDs,\nideal for use as primary keys in databases.\n\nFeatures:\n- Prefix with a random letter.\n- Timestamp in milliseconds.\n- Local Atomic counter to prevent collisions in the same millisecond.\n- Cryptographically secure entropy.\n- Process fingerprint to ensure uniqueness across nodes.\n## Installation\n\nThe package is available in [Hex](https://hex.pm/packages/ex_cuid2) and can be installed by adding `ex_cuid2` to your list of dependencies in `mix.exs`:\n\n```elixir\ndef deps do\n  [\n    {:ex_cuid2, \"~\u003e 0.10.1\"}\n  ]\nend\n```\n\n## Usage\n\n### Generating IDs\n\nYou can generate a CUID2 with the default length (24) or specify a custom length.\n\n```elixir\n# Generate a default CUID2\niex\u003e ExCuid2.generate()\n\"v8p7k3f9z1m0c2x4b6n5j7h8\"\n\n# Generate a CUID2 with a custom length (e.g., 30)\niex\u003e ExCuid2.generate(30)\n\"b5n6m4j3h2g1f0d9s8a7q6w5e4r3t2\"\n```\n\n### Validating a CUID2\n\nYou can check if a given string conforms to the CUID2 format using `is_valid?/1`. It performs a check based on length and character set and returns `false` for any non-binary input without raising an error.\n\n```elixir\niex\u003e id = ExCuid2.generate()\n\"t9p7k3f9z1m0c2x4b6n5j7h8\"\n\niex\u003e ExCuid2.is_valid?(id)\ntrue\n\n# --- Invalid Cases ---\n\n# Too short\niex\u003e ExCuid2.is_valid?(\"a123\")\nfalse\n\n# Starts with a number\niex\u003e ExCuid2.is_valid?(\"1abcdefghijklmnopqrstuvw\")\nfalse\n\n# Contains invalid characters (uppercase)\niex\u003e ExCuid2.is_valid?(\"aBcdefghijklmnopqrstuvwX\")\nfalse\n\n# Wrong data type\niex\u003e ExCuid2.is_valid?(12345)\nfalse\n```\n\n## Ecto Integration\n\n`ExCuid2` provides an optional `Ecto.Type` module for seamless integration with your Ecto schemas.\n\n### 1. Configure Your Repo\n\nFirst, register `ExCuid2.Ecto.Type` as a custom type in your application's configuration (`config/config.exs`).\n\n```elixir\n# in config/config.exs\nconfig :my_app, MyApp.Repo,\n  ecto_types: [cuid2: ExCuid2.Ecto.Type]\n```\n*(Replace `:my_app` and `MyApp.Repo` with your application's name and Repo module.)*\n\n### 2. Use in Your Schema\n\nIt's recommended to use it for your primary key with `autogenerate: true` and to set the `@foreign_key_type`.\n\n```elixir\ndefmodule MyApp.Accounts.User do\n  use Ecto.Schema\n  import Ecto.Changeset\n\n  # Define the primary key as a CUID2\n  @primary_key {:id, ExCuid2.Ecto.Type, autogenerate: true}\n  @foreign_key_type ExCuid2.Ecto.Type\n  schema \"users\" do\n    field :name, :string\n    field :email, :string\n\n    timestamps()\n  end\n\n  def changeset(user, attrs) do\n    user\n    |\u003e cast(attrs, [:name, :email])\n    |\u003e validate_required([:name, :email])\n  end\nend\n```\nYou could use this : `@primary_key {:id, ExCuid2.Ecto.Type, autogenerate: 32}` if you use a cuid2 longer.\n\n\nWith this setup, Ecto will automatically generate a new CUID2 for the `:id` field whenever you insert a new record, giving you secure and scalable primary keys out of the box.\n\n```elixir\ndefmodule MyApp.Accounts.Migrations.CreateAccount do\n  use Ecto.Migration\n\n  def change do\n    # primary_key: false is not needed here since we are defining a custom primary key\n    create table(:record, primary_key: false) do\n      # we use char beacuse cuid2 has a fixed length.\n      add :id, :char, primary_key: true, size: 24 # check the cuid2 length\n      add :title, :string\n      add :body, :string\n\n      timestamps(type: :utc_datetime)\n    end\n    \n    # Use a hash index for random values as cuid2.\n    create index(:record, [:id], using: :hash)\n  end\nend\n\n```\n\n### Optional Custom Postgres Domain\n\n``` elixir\n  defmodule MyApp.Repo.Migrations.CreateCuid2DomainType do\n    use Ecto.Migration\n    # Create a custom domain type for cuid2\n    # This is a PostgreSQL specific feature, so ensure your database supports it.\n    # The cuid2 format is a 24-32 character string starting with a letter followed by lowercase alphanumeric characters.\n    # The regex checks that the value starts with a letter and is followed by 23 lowercase alphanumeric characters.\n    # Adjust the regex as necessary to fit your specific requirements.\n    # Note: The size of 24 is based on the standard CUID2 length.\n    # If you are using a different length, adjust the size accordingly.\n    def up do\n      execute(\"\"\"\n      CREATE DOMAIN cuid2 AS character(24)\n        CONSTRAINT cuid2_check CHECK (VALUE ~ '^[a-z][a-z0-9]{23}$');\n      \"\"\")\n    end\n\n    def down do\n      execute(\"DROP DOMAIN cuid2;\")\n    end\n  end\n```\n\nAfter this migration you can use this :\n\n```elixir\ndefmodule MyApp.Repo.Migrations.CreateRecord do\n  use Ecto.Migration\n\n  def change do\n    create table(:record, primary_key: false) do\n      # add :id, :char, primary_key: true, size: 24\n      add :id, :cuid2, primary_key: true\n      add :title, :string\n      add :body, :string\n\n      timestamps(type: :utc_datetime)\n    end\n\n    # Use a hash index for random values as cuid2.\n    create index(:record, [:id], using: :hash)\n  end\nend\n\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjuliolinarez%2Fex_cuid2","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjuliolinarez%2Fex_cuid2","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjuliolinarez%2Fex_cuid2/lists"}