Hey everyone — I’m diving into using Clojure for backend services. I come from a frontend background, and while I’m not totally new to the Clojure ecosystem (I’ve written a bit of CLJS and feel fairly comfortable with the language), backend work feels like a whole new world. I’ve been exploring on my own for a few days, but it’s been a bit of a guessing game. Debugging takes a lot of time, and I’d really like to be more intentional about how I learn — without leaning too much on AI. Any advice on how to approach this? Especially around understanding how the various libraries fit together — a lot of them seem to build on each other, and it’s easy to get overwhelmed. Also, if you know any good books or resources for someone new to backend development — maybe not just Clojure-specific, or even better, something that teaches backend through Clojure — I’d really appreciate the recommendations. Thanks so much! I'm not sure if this is the appropriate channel for this, please advice me on the appropriate channel
Maybe using a resource like https://practical.li/clojure-web-services/introduction/overview/ would be beneficial
I have a similar feeling about CLJS, presumably because I've use it much less than JVM Clojure. @obioradhani.el if you share some more details about the stack you're working with, we may be able to give some more precise pointers.
I’m new to the backend world, I’ll appreciate directions on libraries to consider please
I think the ones in that practicalli book are good starting points
I also have a https://practical.li/clojure/clojure-cli/projects/templates/practicalli/that will generate a working API service, with http-kit (webserver), Reitit ring (request routing & middleware), mulog (logs), OpenAPI (swagger API docs) and some development tools.
Or clone this repo https://github.com/practicalli/gameboard-donut
Thank you so much, I'll start with these and hope it get's me up to speed
I've found that personally it's good to understand the basics. At the most basic step, what happens is you have to open a port on the computer, that's done by making a OS call, so the OS exposes a function that lets you open a port where network packets can then be received. The OS gives you a callback or buffer where it'll call you or put the received packets into. And you application can read and react to them. Now you have a server. That's the basic. Then the packets themselves they follow a particular format that is of type TCP, and as part of that it establishes a response channel back to the sender as well. Now you have TCP connections with clients that can connect to your server by making requests to it. Now inside that you have another protocol which is HTTP that dictates how those packets can model request payloads, it comes with verbs like POST, PUT, GET, etc. And it establishes a response payload, which will contain HTML. Now you have an http server. After that, it's just a matter of handling each http request payloads, to return the appropriate HTML response payload. You can also return other things than HTML, for example JSON, on which case it's not an HTML server but a REST server, etc.
That's true of all languages. Now specific to Clojure. You don't want to implement what I described yourself. So this is the first "library" you need, which is to find a HTTP server library. There exist many HTTP server libraries. And there are also some that are frameworks. Because not all HTTP server library/framework are as performant, have the same features, etc., it's something you might want to change. In order to be able to change or not have to relearn how each one of them works. Clojure community came up with an abstraction, an a HTTP server interface library, called RING. Ring doesn't actually implement an http server, but it provides an interface for one, and then oher http servers implements the Ring interface.
Ring is therefore the most important library for making Clojure backends. It's the interface you will be using. You have to provide ring with an actual http server. That's where there is ring-jetty for example, or http-kit is also ring compatible. Etc. The http server would be jetty (a popular Java one), or it would be http-kit (a popular clojure one). But in either case, they simply implement the Ring interface and you're actually working with Ring.
In reality, Ring is all you need for backend work in Clojure. You can start playing only with Ring, and you should be able to make a hello-world web page.
As you do that, you'll learn about multiple little things that you wish there was a library for, instead of doing it yourself.
1. When you receive a http request, it has a URL property, you probably want to parse that URL and depending on the path do something different. This is called "routing". You can just use cond and a regex to do it, but there exist many libraries that make it easier, faster, and add other conveniences and so on. The popular ones are Compojure and Reitit
2. When you receive a http request, you probably want to return some HTML, which is really just a big string. You can use str to construct this HTML, but that's a bit annoying, so you probably want a better library to help construct HTML. The popular ones are Hiccup or Selmer.
And so on. As you keep going, you'll probably want to connect to a DB to persist and retrieve things between requests. So you'll look for libraries to help connect to a DB and query/update it. You might want to protect yourself from hackers and have some security measures in place, so you'll look for a library to help do that. You'll probably want to log things or publish metrics to help monitor and debug issues, you'll look for a library for that. You might want to start sending some emails, you'll look for a library for that. You might want to create user accounts, manage login/logout and sessions, with user permissions and what not, you'll look for a library for that.
Etc.
This process is what Clojure calls the "library approach". As you encounter specific use cases, you go looking for a library to help solve it, it's up to you to choose if you roll your own, or which library you choose, and how you decide to integrate or leverage the library within your existing code. It gives you a lot more control, you can really customize things to your application specific needs, and you're aware of how everything works as you put it together yourself.
The alternative is called the "framework approach". That approach means you use a framework that has already implemented all those things, and even more of them. It already implemented things you won't even need. It's already decided how they all get integrated together. Now within that existing framework that already has everything setup, you have to figure out how to customize and configure it so it does what you want your application to do. The advantage is that it comes with a ton of things by default and does things by default that might be good practices, or that you haven't even thought about but are good ideas. You don't have to decide how to do things, or what library to choose from, since it's already decided how everything will be done exactly. The disadvantage is that you won't really understand it all, because it does so much, a lot that you won't even be using. And you can't easily decide that you don't like how it does one thing and change it, it's kind of an all or nothing offering. And you have to figure out how to customize it and configure it to do what you want, which feels more like you're just learning about this particular framework, and not about the fundamentals of how to do the thing, but more how to configure the framework to get it to do the thing you want.
Unfortunately or fortunately, there is no such framework in Clojure 🤣. At least no open source one that's well maintained, feature complete, and broadly used.
Along the lines @didibus has laid out here, I strongly recommend https://caveman.mccue.dev/ who proves that the best framework is no framework