Best practices for validation of UUIDs in GET requests

Here is the setting:

  • Say we have a ressource User which has a Ecto.UUID as primary key.
  @primary_key {:id, Ecto.UUID, autogenerate: true}
  • When GETting the resource a UUID must be provided (GET /users/c8588b10-5c0a-4f74-9f43-f2df7ddc7342). But if an invalid parameter instead of a UUID is sent, postgres is raising the exception:
** (exit) an exception was raised:
    ** (Ecto.Query.CastError) deps/ecto/lib/ecto/repo/queryable.ex:322: value `"1"` in `where` cannot be cast to type Ecto.UUID in query:

So now we have two choices:

  1. Catch the Error with Plug.Exception and raise a 404
  2. Validate that the given parameter is a valid UUID before accessing the database

The first approach doesn’t feel right, because we have to hope, that the Ecto.Query.CastError is only ever raised when there is a wrong UUID-format given. Every other CastError would lead to the same 404 without knowing the difference.

So for the second approach: What is the best way to do that? Create a specific changeset which checks the format of the UUID and rejects the Request with a 422 or 404 on failure? Or is there a better (built-in) way?

Just an idea, but you can use Plug that’d be executed before the requests are being handled in controller that validates the format of IDs. You would write and then configure the plug on a controller level this way:

plug RequireUUIDs, param_keys: [:id, :organization_id]

as an example. So if you have nested routes it’d validate :id and :organization_id, and if these are present, and are not UUIDs, it would raise 404.

Thanks! That sounds promising, I will try that.