In simple cases I just serialize an edn map server side in JSON and write it to the HTML document. This serves as the initial config for the client, and if I’m using CSRF, that token is being filled from
ring.middleware.anti-forgery/*anti-forgery-token* (only available or ‘bound’ during the request).
In more complex cases where there’s lots of dynamic things going on before the client initializes, I add an API endpoint (e.g. /api/hello) which returns said data.
As far as I know, for SPAs this is kinda the way to do it: get the CSRF token (and potentially other secrets) to the client somehow – use whatever you find appropriate!
Update: Also, a somewhat neat trick if you write some data to the HTML document is that you can have the script element remove itself from the DOM after initializing:
window._app_init_data = '<serialized json>';
document.currentScript && document.currentScript.remove();
Which of course will still be visible when inspecting the raw HTML response, but at least it doesn’t leave a bunch of potentially sensitive data in the DOM. You can also unset the
__app_init_data key from the global context as soon as your app’s done. None of this is giving you any real security, of course, but it does feel good to not just leave a trail of data through every user’s browser.