Isn't Clojure overkill for small web apps?

Hi folks, May I ask for your opinion and advice, please?

Is Clojure a reasonable choice for simple web apps? I am talking about hobby projects, mostly CRUD stuff, dozens of users. Things like a simple booking system for a local gym, etc., not expecting millions of concurrent requests :slight_smile:

I am also not a professional developer, so I need something that can handle ā€œstupidā€ hacky junior code. Some advanced Clojure concepts are beyond my (current) skills. The reason Iā€™m drawn to Clojure is that I find lisp style and syntax actually very friendly, natural and elegantā€¦

I know frameworks are not very popular in Clojureverse, but would you recommend some that have solid userbase, good documentation, and maybe some similarities with e.g. Phoenix? And have beginner-friendly tooling, including much-hated code generators maybe? Itā€™s a bit difficult to figure out whatā€™s what in Clojure.

Thank you!

4 Likes

The only two ā€œframeworksā€ Iā€™m aware of for Clojure are Coast and Luminus and even the latter is really just a project template that composes several libraries.

Luminus is more popular, by far, than Coast, but I donā€™t consider Luminus to be beginner-friendly. I donā€™t know much about Coast but my understanding is that itā€™s written by a Rubyist who wanted something Rails-like in Clojure.

The reality is that people just donā€™t use frameworks in Clojure and, for the most part, they donā€™t build ā€œmostly CRUD stuffā€ with Clojure ā€“ so thatā€™s a bit of a bare spot that beginners really notice when they come from other languages that either focus on or at least heavily support ā€œmostly CRUD stuffā€.

What Rails and Phoenix etc offer has no parallel in Clojure. See this massive ClojureVerse thread about what beginners struggle with and youā€™ll see quite a few posts about the lack of easy web app stuff.

Iā€™ve been using Clojure for a decade in production at this point and I doubt I would use it for hobby web apps that are mostly forms, validation, and database access. Iā€™m not sure what I would use, to be honest. I donā€™t like Ruby as a language but I might consider Rails, or I might look to PHP or Python for something quick and dirty out of the box ā€“ but it would mostly depend on what tech was easiest for wherever I wanted to host this.

7 Likes

Just for completeness Iā€™ll add Fulcro to the list of frameworks. Not a recommendation however as itā€™s just something on my ā€˜to investigateā€™ list.

For some reason I thought Fulcro was front-end only but it does appear to have some server-side components. It looks extremely complex so I would not consider it ā€œbeginner-friendlyā€ despite the extensive documentation.

Itā€™s hard to compare these ecosystems, they have very different demographics and philosophies. The preference for libraries instead of frameworks has good reasons which Iā€™m not going to rehash here, the net result is that thereā€™s more of an initial learning curve Iā€™m the beginning. Thatā€™s not so much because these libraries together are more complex than an equivalent framework, but it takes effort to figure out which bits you need, and how to wire then up, which is why a template that embodies these opinions regarding the stack and that takes care of the wiring like Luminus exists.

I also feel like thereā€™s more stuff happening around React based apps. Plenty of people are doing server side rendered too, but there are fewer libs coming out in that space, fewer people writing about it. But I do still think itā€™s a great choice for a lot of stuff and there are good libs to choose from.

My personal recommendation would be to lean in to the Clojure way: learn libraries one by one, try them out at the repl, then piece them together. I would start with

Model - honeysql
View - hiccup
Controller - Iā€™d like to say Reitit, but for starting out Compojure is probably better

Thatā€™s it, you can make an app with just these three. The rest youā€™ll figure out as you need it.

Update: as pointed out here vanilla hiccup is susceptible to cross-site scripting attacks, and you should use Hiccup 2, which is nominally in alpha but which has been perfectly fine to use for years. Alternatively use any other Hiccup implementation. For the Lambda Island site we render our Hiccup with Enlive.

4 Likes

hiccup is not safe by default, as it requires you to manually escape every string. Iā€™m not even comfortable using it myself, much less recommending it to a beginner. Iā€™d recommend using rum, since you can use the same syntax but stringify it with rum.core/render-html in place of hiccup.core/html. rum 0.11.5 for now though, as the 0.12.x releases currently require ClojureScript to compile.

You might write a small helper if you do need to set unescaped strings frequently, as rum uses a very verbose React-compatible syntax:

[:div {:dangerouslySetInnerHTML {:__html "<span>test</span>"}}]
5 Likes

This is a bit of an aside, but the hiccup 2 alpha does escape strings by default. I donā€™t know how alpha is alpha in this case, however, and there seems to have been little activity in the repo for a while.

1 Like

Biff is intended to fit this use case, though at the moment itā€™s still an early project and probably needs a lot of work before the onboarding experience for beginners is smooth. If youā€™re interested in this project, I could really use people to try it out and let me know what pain points they run into. Iā€™m using it myself in production and am quite happy with it, but it can be hard to predict what issues other people will have (especially for the documentation).

5 Likes

The alpha only implements the core as hiccup2.core. Other namespaces like hiccup.page have the old unsafe behavior. It really wouldnā€™t be that hard to implement, but thereā€™s just no reason to when there are good alternatives that also integrate with ClojureScript.

2 Likes

Can you give an example of what you mean by this? Itā€™s not clear to me.

If you have some hiccup code like this:

(defn message [{:keys [username text]}]
  [:div username " said: " text])

and someone sends a message like this:

hey, <script>alert("you got pwned");</script>, have a nice day

then the JS code will run for anyone who views the page. Usually you would want that text to be converted by default to something like hey, &lt;script&gt;alert(&quot;you got pwned&quot;);&lt;/script&gt;, have a nice day.

7 Likes

Oh, also: Firebase + ClojureScript is actually a very nice experience, and I would absolutely look into that. I made Biff after running into limitations with Firebase, but for a lot of apps Firebase will likely be more than sufficient. I have a non-trivial example app here (itā€™s a board game): https://github.com/jacobobryant/mystery-cows

4 Likes

So, I would use Clojure myself, but now you ask as a beginner, and for something that can handle ā€œstupidā€ hacky junior code. With those constraints, you probably donā€™t want to use Clojure, unless you are doing it as an exercise to learn about Clojure and web dev.

It might depend how beginner you are. Do you know SQL and can you create database tables, relations, etc.?

Do you know HTML/CSS and basic JavaScript?

You might be able to follow some basic Ring tutorials in this case. That said, even then you might struggle with deployments, and serving requests and anything harder will be pretty confusing, like basic auth, and certs, sessions, etc.

1 Like

What I meant by hacky junior code is that my solutions are usually not the most efficient, robust and elegant ones. Thereā€™s amazing stuff that can be done in Clojure in 2 lines of code while still being readable, while my solution would probably still be less ā€œclojuricā€ and take 10-15 linesā€¦

Iā€™ve done my fair share in Swift, Ruby, Python, Elixir, etc. so itā€™s not like Iā€™m a total noob. But I would still prefer a sort of framework with ā€œconvention over configurationā€ ā€“ a starting point with safe bet choices for many tasks. These choices are hard to make if youā€™re in a completely new ecosystem, so you just need to start somewhere. Iā€™m not looking for a black box, I am looking for a collection of informed conservative decisions tied together in a ā€œboringā€ postgres-clojure-clojurescript phoenix-like starter pack :slight_smile:

1 Like

Most of my professional apps are ā€œsmallā€ and ā€œCRUDdyā€. I work in a university setting and have to maintain a dozen apps with user-counts usually from the tens to the dozens. I love Clojure for this because, aside from the size, it gives me two things:

  1. Stable Robustness. In the web application world of insane flux, Clojure[script]'s ability to stabilize and provide longevity is truly priceless.

  2. Superior dev experience. The REPL and (on front-end) Figwheel/Shadow.cljs experience is really hard to go without once youā€™ve tasted the wonder. It is more fun, more rewarding, and, as a result, faster dev experience.

    These two benefits depend on one requirement. The fact is that Clojureā€™s higher difficulty for beginners is the price to pay for a language that actively pushes you to be a better developer. It will be challenging at the start, but after your first month of Clojure you will be a better developer than that same first month of Javascript, PHP, Python, C, or Java. As a hacker, I think this has to be what you want before other benefits.

    In the name of this learning, I second earlier mentions on this thread of Luminus, which is a terrific way to see how the pieces fit together so that down the road you are ready to start customizing/switching them out as needed.

5 Likes

Thank you all! I am starting with Luminus that was mentioned several times and seems to have a decent history. Also seems to have solid documentation and isnā€™t too quirky (just a little bit) :+1:

If you really do want to start with something fairly bare bones for a CRUD app with SSR (server-side rendering of HTML), my user manager example should be fairly easy to follow.

Itā€™s built with Ring, Compojure (routes), Selmer (HTML templates), Component (lifecycle management of resources), next.jdbc (database access).

It uses SQLite for normal operation, but any RDBMS could be used. It has tests and uses H2 in-memory as the test database.

It uses the Clojure CLI / deps.edn which I prefer over Leiningen (at work, we started with lein a decade ago because it was the only option, then switched to boot in 2015, and then to the new, official CLI tooling in 2018). I think itā€™s a lot simpler to work with than Leiningen (although Leiningen is ā€œeasierā€ to get started with).

6 Likes

Thanks for the link to your user manager demo app, I wasnā€™t familiar with it. Iā€™ve been using Luminus but itā€™s great to see another (simpler!) example of how to put things together.

1 Like

Yep, I agree. Ring+Compojure+Hiccup+one of the SQL libraries will get you a long way. And there are lots of examples of how to put together that stack, so I recommend it to beginners as the default starting place. It may not be the most performant stack for heavy-duty applications, but it will handle a lot and can be further tuned if necessary. Add Selmer if you want more advanced templating.

2 Likes

It seems like Luminus recently switched itā€™s routing library from compojure to reitit. The last time I checked (which was last year) it was still compojure.

I was pleasantly surprised because in my opinion this makes frontend and server routing somewhat more streamlined and therefore less decision to make when mix-and-matching libraries. (When we create a reagent-template lein project, it comes with reitit client+server routing by default, so having Luminus - which often is the framework I use as reference - use the same routing library makes it easier for a newcomer like me setup a project from scratch).

3 Likes