EctoHashids
IDs are hard.
Ecto, out of the box, works really well with sequential ids. But then, you release your fancy new service and your users see a url like /purchases/4 and the illusion that you have any sort of traction is gone.
How do others solve this?
Some solve this w/ UUIDs, meaning your users instead see /purchase/051c022e-8b9d-4d32-991d-58ad00a92d59 and that might suit you very well, but there are some annoyances that come up w/ UUIDs
- they are really ugly
- it isn't "memorable" (think "here's your confirmation number")
- they take up a more space in the DB
- ecto, out of the box, assumes sequential ids so you have to do some (easy) customization
What does this library do about it?
EctoHashids is essentially a Ecto Type generator where you configure which schemas you want to expose hashids instead of sequential ids, and w/ 1 line in yuor schema, you can now interact w/ that model w/ the hashid instead of the sequential id.
In the above example, your user could now see: /purchases/o_5zabk
IMPORTANT:
Some folks (rightly) want to avoid sequential ids for security reasons
I.e if I am shown /purchases/1, I could just change the 1 to a 2 and see information about some other purchase.
This library DOESN'T HELP w/ the above security hole. A user can just as easily guess a random hashid (since they are short and wonderful) as they can a sequential id.
Any solution to solving the security hole to sequential ids can and should be added onto of these Hashids if it's devastating for a user to find their way to a url you were expecting. (ex: /purchases/o_5zabk/some-token-for-this-id)
Installation
If available in Hex, the package can be installed
by adding ecto_hashids to your list of dependencies in mix.exs:
def deps do
[
{:ecto_hashids, "~> 0.0.1"}
]
endConfiguration
The very minimal configuration you'll need is to define which schemas you want to generate these types for. Let's say you have a single Schema called Purchase and hou want to have p_abc123-looking hashids, you can do this in two steps:
config :ecto_hashids,
prefix_descriptions: %{
p: Purchase
}
This will auto-generate a module for you: EctoHashids.Types.P.
All you have to do is use that as your primary key inside your Purchase schema:
@primary_key {:id, EctoHashids.Types.P, read_after_writes: true}
````
If you have any relationships that `belong_to :purchase`, you'll need to tell ecto how to handle that as well:belongs_to :purchase, Purchase, type: EctoHashids.Types.P
And you're done!
### Recommended Configuration:config :ectohashids, prefix_separator: "", # What goes after the prefix? characters: "023456789abcdefghjkmnpqrstvwxyz", # Which characters should be valid for hashid salt: "fef02203-0e9c-45d4-89f2-f2ac7d154f36", # What do you want to use for a salt for creating hashids prefix_descriptions: %{
p: Purchase, # Include all of your modules}
Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
be found at [https://hexdocs.pm/ecto_hashids](https://hexdocs.pm/ecto_hashids).