jacobobryant / biff

A Clojure web framework for solo developers.
https://biffweb.com
MIT License
877 stars 41 forks source link

Make uniqueness constraints easy #89

Closed jacobobryant closed 2 years ago

jacobobryant commented 3 years ago

Example problem: right now, the template project has a race condition when creating users (see https://github.com/jacobobryant/biff/blob/master/libs/tasks/resources/biff/template/src/%7B%7Bparent-path%7D%7D/routes/auth.clj#L68-L78). In general, email address shouldn't be used as the primary key for a user document/record, since it can change. But we still want to enforce uniqueness.

This can be done by creating a separate document (call it a "constraint doc") and matching on it, e.g. {:crux.db/id {:user/email "foo@example.com"} :biff/held-by #uuid "some-doc-id"}.

First, we should do this in the project template without helper fns. This will only take a minute, since the project template doesn't let users change their email, and it doesn't delete users. I just don't want to do it right now since it's not urgent and I'm busy writing docs.

Second, we should think about abstracting this into a Biff lib. For example, maybe in the schema we add a key like this:

:unique-keys #{:user/email #{:foo/bar :foo/baz} ...}

And then have biff.crux/submit-tx add matches + create the separate constraint docs based on that.

We could use transaction fns instead of matching. Need to decide which is better. My initial whim is to use matching.

jacobobryant commented 3 years ago

Lately I'm starting to lean towards always using UUIDs for IDs on regular documents, and then relying on something like this for all uniqueness constraints.

jacobobryant commented 2 years ago

New version of biff includes a :db/lookup special value for this.