Show HN: Biff – Self-hosted Firebase alternative for Clojure
findka.com
findka.com
I've been doing Clojure web dev for a while but hadn't found a setup that I really liked yet. My latest approach was using Firebase + ClojureScript (for my startup[1]) which was really nice but had some significant limitations for my use case (mostly related to the ephemeral Node backend). At one point I realized that I could provide Firebase's query subscription feature using Crux[2] instead without too much effort. So I started working on Biff, and I've been using it in production for 2.5 months now. It's been a joy to use.
Also, I do think that web dev in Clojure can be a little hard to get into, due to the (appropriate) community preference for libraries over frameworks. I think there is a need for more frameworks that curate a set of libraries for you. I'd love it if Biff eventually became a sort of "Rails for Clojure" that would let anyone get up to speed quickly but would allow you to switch out the components easily as you go on.
(About the name: I named it after Biff Tannen from Back to the Future).
That's my only question about it. Thank you for sharing it. I found the project very enjoyable to browse and really appreciate that you shared several of your key design decisions.
Good Work!
- an atom, the contents of which describe what data you're subscribing to
- another atom, which is populated with the results of your subscriptions
- a function for handling server-sent websocket events (if your app uses any)
and it returns a function for sending websocket events to the server (which is used for sending transactions + any custom events you define).
[1] https://github.com/jacobobryant/biff/blob/98ad188924bee9121d...
I think the biggest weak points of firebase are a. a weak query engine (as soon as you reach non-trivial complexity you end up having to de-normalize data) and b. a weak rule engine (relying on a bunch of boolean logic is not going to scale as your app grows. you need more abstraction power)
Looks like your library addresses both -- crux has datalog queryability, and the rule dsl you wrote seems pretty powerful.
Good work Jacob!
1. Would love to learn a bit more about how the rule engine works.
From what I understand crux in essence only gives you key->blob semantics. How do you know that one `doc` is a `user`, and another doc is an `game`, etc
2. re: datalog subscriptions -- do you know if this is something inherently very difficult, or is it that crux hasn't implemented this yet?
2. It is inherently difficult. The closest thing like this that exists for datalog is I believe clj-3df[1]. Hence I was very excited when Materialize was launched publicly a few months ago. A version for datalog would be cool too, but SQL is good enough I think.
1) Crux is schemaless at its core, but you can build your own ~class hierarchy model using regular attributes and Datalog rules to differentiate between types of entities.
2) Subscribing to arbitrary Datalog without any form of polling requires a fundamentally different form of query algorithm (i.e. incremental view maintenance, as seen in Materialize), however there's a whole spectrum of possibilities available if you are willing to accept polling at some level. Hasura has quite an inspiring take on subscriptions that we may yet try to recreate on top of Crux: https://hasura.io/blog/1-million-active-graphql-subscription...
{[:users #uuid "some-user-uuid"] {:db/merge true
:username "foo"}}
gets converted to [[:crux.tx/put (merge (crux/entity db #uuid "some-user-uuid")
{:username "foo"})]]
There is a race condition though; if another tx updated that document after the crux/entity call but before `:username "foo"` was written, then that tx would get clobbered. I'm planning to add `:crux.tx/match` operations automatically to prevent that from happening. There was talk on #crux in Clojurians slack today of using the newly-added transaction function feature to do merges. Haven't looked into that myself.(crux/submit-tx [[:crux.tx/fn :crux.fn/assoc doc-id k v]]) ```