I've been on a curious arc of backend, server-rendered apps that has led me to asking some questions about Fulcro on the server. it's odd a priori to get to Fulcro from there, but the latest steps of the arc led to wanting RAD-like attributes as data, with automatic form + report generation.
my app
the app consists of some chunky forms that build a request map, which feeds a long-running operation (5-90s). the results of those long ops are cached for O(days) unless evicted by the user. storage in Datalevin, using Datalog for the proper database parts and the KV store holding EDN blobs for user-specific state like the requests/responses to those long ops.
one of the long ops results is treated like a checklist, with the user (durably) marking steps as complete, and being able to say "starting from the current state, let's rebuild the plan".
architecture
Datastar wants the server to drive everything, and the client side to be thin bordering on absent. (my app has some expandable sections that have JS state, but that can be handled with a few data-* attributes.) all the substantial actions happen with server traffic.
on the server side, my goal is a "stateless" CQRS style. of course it's not really stateless, there's a database! but each request is conceptually starting from a blank slate and reading what it needs from the DB. (this is hyperlith's model, more or less.)
sketch of the lifecycle
rendering a page on the initial GET would (1) take a root component; (2) run its query via Pathom; (3) feed that tree straight(?) to the component; (4) render to a string and onto the wire.
actions are rendered in the HTML by a function that turns a list of Fulcro mutations into a Datastar data-on:click=@post('/some/page?various=details'). those POSTs are handled by (probably) one receiver route per top-level page, which runs the mutations more or less like a classic Pathom-powered Fulcro remote.
re-rendering is actually just another root render, sending the entire page again over SSE and letting Datastar's morph logic figure it out. with Brotli compression and a not-crazy page structure, this is so compact and fast as to be in a "don't prematurely optimize targeted renders" world.
how achievable?
with the recent work on fulcro.raw and the headless app stuff from last week, it sounds like this isn't so far out of reach. it does put me in the bizarre position of wanting to use Fulcro but without its absolutely central normalized database! there simply isn't anywhere it's needed in the outline above. (unless I'm missing something where the denormalized tree alone isn't sufficient for a one-shot render?)
ISTM the biggest hurdle is how tied to React rendering the existing RAD core is. there's a warning about that in the docs for RAD, though I don't know how out-of-date or otherwise it is. of course all the RAD rendering is pluggable, and I'm expecting to have to write my own reports and forms as well as the leaf form controls.
RAD as metadata?
perhaps it makes more sense to instead borrow RAD's design of extensible attributes and roll my own form and report rendering, without depending on Fulcro itself. that's where I'm at currently, but I'm not thrilled at the feeling that I'm re-implementing a bunch of Fulcro rather than just using it.
has anyone done purely server-side rendering before?
I've only seen comments on running headless on the JVM for testing and LLMs, or SSR as an optimization with a client-side Fulcro app to hydrate on the other side; neither of those models is a great match for my needs.
Not tied to react render at all. The question for datastar is “what holds your denormalized state of the client app?“. All you’re doing with datastar is moving the client code/state from the client into the server. Honestly I didn’t read your whole question (it was very long) but the headless support in Fulcro is just that: it is completely detached from React and browsers. You can emit hiccup, strings, whatever. The lambdas you co-locate on components come out as data in the non-string versions (e.g. hiccup or just the internal map representations). I guess you wouldn’t really make those lambdas??? I have no personal use for Datastar so I don’t know what you’d want to do there, but it might not end up being the best match, since Fulcro is intended to give you a clean reasoning model about a user-space client application that happens to talk to “remotes”. No idea if that makes any sense with datastar, but my understanding of it is that you’re just moving the client code into the server, but you still have client code and state…which seems a bit off to me for most applications? Why pay for infra to do what someone’s browser will do for free? Anyway, don’t want to start a flame war. I’ve seen the startup/interactive speed demos. I also wrote in Scala Lift back in 2012 (which was very close to this model…we keep reinventing solutions). Now, on the RAD thing YES. The concepts there are just that: attributes and the general model don’t need Fulcro (though RAD itself assumes that’s what you’d use). I’m getting tons and tons and tons of mileage out of the general ideas I made there: attribute is a thing with a var, unique keyword, docstring that represents everything you could say ABOUT that fact in your model. Contextual “things” (forms, reports, API endpoints, XML, JSON, CSV import/export) then give conceptual ways to play with those attributes in those contexts. The save delta standard for forms that form-state in Fulcro defines combined with a resolver system gives you a database agnostic way to represent data flow in your system of any kind.
And while it is true that my target for headless is LLM use and testing (verification of UIs in test suites), the fact is that you can start N fulcro apps in a CLJ server and run them all in parallel, and have them all render as data