Hi folks,
While it's not directly to your point, here is a pretty complete (IMHO)
repository that I put together for getting you going with a new
Clojure+Clojurescript website:
https://gitlab.com/lambdatronic/clojure-webapp-template
Just clone it and check out the README.md file for a description of the
build and runtime software requirements, build aliases, and expected usage
in development and production modes. All of the build config files are
built around the tools.deps clojure command-line interface and use a modern
version of figwheel for development.
The deps.edn file contains aliases to:
1. Initialize a Postgresql database, including custom Clojure code that
provides a simple "namespace"-like dependency system for your SQL files
using a topo-sort procedure for dependency resolution. If you prefer to use
a different database, you could easily tweak build_db.clj and create_db.sql
to meet your needs.
2. Compile your CLJS code into app.js using advanced compilation settings
(for production deployment).
3. Compile your CLJ code and launch an embedded Jetty web server on a port
that you specify (or 8080 by default).
4. Launch Figwheel, compile your CLJS code into app.js using {optimizations
:none} (for rapid development), launch an embedded Jetty web server on port
8080, and automatically hotload any live changes to your CLJS files into
your browser session and live changes to CLJ files into your server
(available on browser refresh).
In addition, the Clojure code provides a pre-configured middleware stack
with custom middlewares for request and response logging, exception
handling, and parsing EDN params in the request object. There is also
simple routing-handler function using plain old Clojure with some helper
function to render HTML and EDN/JSON responses that you can extend or
replace to meet your objectives.
Super-cool features include a pre-configured system of routes, CLJ
handlers, and asynchronous CLJS functions (using core.async) that allow you
to trivially call SQL functions directly from Clojure (synchronously) and
to call both CLJ and SQL functions from Clojurescript (asynchronously)
using a similar calling syntax. All database interaction is done through
Sean Corfield's excellent next.jdbc library.
In my usual programming model, I encode any operations that should happen
in the database as SQL functions in Postgresql. These are loaded by my
`clojure -A:build-db only-functions` alias. Then from Clojure, I can call
these SQL functions like so:
```clojure
(call-sql "contact.get_user" user-id)
;;=> [{:name "Rich Hickey" :residence "Hammock"}]
```
Alternatively, I can call the same function from Clojurescript like so:
```clojure
(def my-contact (atom nil))
(go
(reset! my-contact (<! (call-sql-async! "contact.get_user" user-id))))
;; At which point @my-contact now evaluates to:
;;=> [{:name "Rich Hickey" :residence "Hammock"}]
```
You can do the same thing with CLJ functions from CLJS like so:
```clojure
(def cool-result (atom nil))
(go
(reset! cool-result (<! (call-clj-async! "my-cool-clojure-function" 1 2
3))))
;; At which point @cool-result now evaluates to whatever the result of
calling (my-cool-clojure-function 1 2 3) on the webserver evalutes to.
```
NOTE: For security, only CLJ functions that are explicitly listed in
remote_api.clj are allowed to respond to `call-clj-async!` requests from
the browser.
Another pretty useful feature is a simple set of CLJS functions for storing
arbitrary Clojure associative data (i.e. maps) in your browser's session
storage:
- get-session-storage
- set-session-storage
- remove-session-storage
- clear-session-storage
The back-end code also includes a very simple `send-email` function
implemented using `com.draines/postal` and a straightforward set of
synchronously logging utility functions called `log` and `log-str` built
using Clojure agents since using `println` and friends for back-end logging
on a concurrent webserver is a recipe for unreadable spaghetti logs.
The front-end code is just a sparse skeleton using Reagent that provides a
largely blank home page and an unstyled not found page. You can copy either
page and use it as a template for adding any additional pages to your site.
Note that the exported `init` method on each CLJS page receives the HTTP
request params in JSON encoding as their first argument, so that you can
easily respond to request parameters from the browser side of your
application if you choose.
And last but not least, I've included a .dir-locals.el file in the toplevel
of the repo that automatically configures your Emacs CIDER installation to
seamlessly use the same figwheel settings as calling `clojure -A:figwheel`
does. Magic!
Alright, folks. That's it from me for now. I hope some of you find this
repository useful in your Clojure+Clojurescript web programming adventures.
Happy hacking!
Gary
P.S. Specifically to your authentication point: The most straightforward
and general place to implement authentication is in your middleware stack
(handler.clj). Look for an existing Clojure library that provides an
authentication middleware and add it at the level in the stack that you
feel is most appropriate for your application. There are several of them
out there.
Or...you could just be a maverick and roll your own. The fundamental
operation of this middleware function is to grab some information from the
request object and apply a strong test to it to determine if the next
handler in the stack should be called or if an error response map should be
returned. That's basically it. You can decide how best to design that
authentication challenge based on your application's needs. Use encrypted
passwords in your database, temporary user/session tokens, OAuth tokens,
LDAP requests, or whatever else seems appropriate. If the test passes, call
the next step handler from your authentication middleware. If it fails,
return some kind of error response map with a meaningful status code (e.g.,
403 Forbidden).
Another very useful option (which may be combined with middleware-level
authentication) is to perform more find-grained route-level authentication
at the level of the routing-handler function (handler.clj). Maybe your
authentication middleware could attach a :role or :permission-level
attribute to the request map before calling the next-step handler. Then in
your routing-handler, you can check that attribute to determine if the
authenticated user should be granted access to a particular route. If not,
just return an error response map from your routing-handler function (again
with something like a 403 Forbidden status code).
These are general suggestions for how to approach the authentication and
authorization process within a Clojure web app. Other developers may (and
probably do) have their own opinions on how to do it. As always, YMMV.
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to [email protected]
Note that posts from new members are moderated - please be patient with your
first post.
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/clojure/1954346f-ed06-4b66-a6d3-3d7ac5a4db87%40googlegroups.com.