beginners

Anand Sundaram 2025-09-18T01:21:46.702339Z

I'm struggling to wrap my head around https://github.com/ring-clojure/ring-anti-forgery in practice if using multiple services. If serving the HTML out of a Clojure webserver it's pretty simple, but if I want to have a separate web server possibly not in Clojure with a Clojure API server, I don't know how best to use the least amount of custom code and most standard techniques to implement CSRF protection with Ring. I've spent a bunch of time reading https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#javascript-automatically-including-csrf-tokens-as-an-ajax-request-header documentation and am pretty confused. It seems like some implementations expose an API on a server that just set a cookie or returns a body with a CSRF token and then expect the same token in a custom header, which sounds simple but doesn't seem to work for me in practice. Anyone have tips for this?

gaverhae 2025-09-24T11:54:00.886889Z

Note that CSRF is specifically a browser attack, exploiting the way the server manages cookies and the various ways in which a user can get tricked into triggering browser requests. If your Clojure server is an API server, i.e. it gets called by non-browser services, CSRF doe snot really apply.

gaverhae 2025-09-24T11:55:38.943529Z

Say if your setup is something like

browser <-> python web server <-> Clojure API
and there is no way for the browser to reach the Clojure API, CSRF is very important in the browser <-> python web server communication, but does not make as much sense in the python web server <-> Clojure API context.

πŸ™Œ 1
Anand Sundaram 2025-09-27T13:41:38.701899Z

Thanks @gaverhae! There was also some discussion I found online indicating CSRF was more important for HTML forms submission and may not apply as much for REST APIs, but I didn't completely understand the nuances. It sounds like if I just route everything through a web server API proxy I don't have to set up CSRF On the API server, but then I just maybe have to worry about it on the web server still

2025-09-18T01:39:35.528129Z

Ring anti forgery depends on ring session, and ring session defaults to a stateful in memory store which can be tricky to make work correctly in certain setups

2025-09-18T01:39:51.360899Z

How are you routing?

Anand Sundaram 2025-09-18T01:46:24.900039Z

For now I am only running the entire project on my local machine. I've got a couple docker containers running on different ports and an API proxy on the web server that just sends certain endpoints to the API server

2025-09-18T02:01:13.923689Z

What library are you using for defining routes

2025-09-18T02:01:48.794389Z

Reitit has some well known gotchas with ring sessions in memory store

2025-09-18T02:03:23.914179Z

If you are using reitit or something else post compojure https://github.com/metosin/reitit/issues/205#issue-399958744 may be a useful read

Anand Sundaram 2025-09-18T02:08:21.805839Z

Oh I was using compojure, sorry I misinterpreted the question. But I'll check that out anyway

Anand Sundaram 2025-09-18T02:09:07.145899Z

The issue described does seem related though, since the session id cookie seems to keep getting updated on every API call from what I was seeing in the browser network tab

gaverhae 2025-09-29T12:59:48.439699Z

CSRF is basically a way to "trick" a web browser into sending a user's credentials along with a command the user did not intend. So it is very specific to the browser context. It is, however, super important in the browser context, so you should absolutely implement it there. And, in an ideal world, you would still have some level of authentication between your internal API server and your web-exposed server.

βœ… 1
ChillPillzKillzBillz 2025-09-18T17:20:06.615049Z

Hello All I am trying to create a time loop which shows time at regular intervals of time. The many examples that are available online show the use of timeout function. when I execute the following code I expect to see the print of timestamps.

(go-loop [cnt 0]
  (when-let [t (<! (timeout 100))]
    (if (= cnt 5)
      (print (str (time/now)))
      (recur (inc cnt)))))
Instead I get
#object[clojure.core.async.impl.channels.ManyToManyChannel 0x5fb2504e "clojure.core.async.impl.channels.ManyToManyChannel@5fb2504e"]
Any ideas?

gaverhae 2025-09-24T12:06:40.129689Z

To clarify what Dan was trying to say (the end of his message is a bit garbled), since timeout closes its channel after the elapsed time, the <! operation will return nil, meaning the value t gets bound to is nil, which in turn leads to when-let saying "well, the bound value is nil, so I don't need to do anything", and the whole body of when-let gets skipped. So no print operation ever happens, and you also never recur. What you're seeing is the return value of go-loop, which in this case is a closed channel that "contains" nothing. Changing when-let to just let will at least get your loop running the expected number of iterations.

dpsutton 2025-09-18T17:24:08.679639Z

go-loop returns a channel which is the result you see there. i suspect your repl is not capturing the output from the thread that is printing and it’s going somewhere else

dpsutton 2025-09-18T17:24:23.245139Z

if you reproduce this in a clj repl i expect you will see your output

ChillPillzKillzBillz 2025-09-18T17:24:50.626439Z

ok I'll try

ChillPillzKillzBillz 2025-09-18T17:24:54.243199Z

thanks!

dpsutton 2025-09-18T17:29:01.976069Z

user=> (doc timeout)
-------------------------
clojure.core.async/timeout
([msecs])
  Returns a channel that will close after msecs

dpsutton 2025-09-18T17:29:07.376429Z

timeout doesn’t return a value

ChillPillzKillzBillz 2025-09-18T17:29:38.319769Z

yes... I understand that timeout just holds the thread up for the time duration specified..

dpsutton 2025-09-18T17:30:18.074319Z

(go-loop [cnt 0]
  (if-let [t (<! (timeout 100))]
    (if (= cnt 5)
      (do (println (str (java.time.LocalDateTime/now)))
          :done)
      (recur (inc cnt)))
    (println "did not get anything from timeout")))
when-let on it’s return value will never be satisfied then `yes. so if you

Alex Miller (Clojure team) 2025-09-18T18:10:27.746049Z

you could also use java.util.Timer or java.util.concurrent.ScheduledThreadPoolExecutor in the JDK

Alex Miller (Clojure team) 2025-09-18T18:15:21.805719Z

(def exec (java.util.concurrent.Executors/newScheduledThreadPool 1))(.scheduleAtFixedRate exec #(println (java.util.Date.) " hi") 0 5 java.util.concurrent.TimeUnit/SECONDS)

Alex Miller (Clojure team) 2025-09-18T18:16:14.528879Z

something like that is what your friendly Java resource would probably recommend

ChillPillzKillzBillz 2025-09-18T18:16:30.907209Z

many thanks @alexmiller

Colton Goates 2025-09-18T19:11:37.223729Z

I'm writing an Uno card game while attempting to use REPL-aided development. I want to be able to make one move at a time as I experiment, so it feels like I need an atom and swap function to track the state. Is there a better way to do this?

(comment
  (do
    (def game-atom (atom (make-init-game)))
    (swap! game-atom play-card {:color :red, :type 5})
    (swap! game-atom play-card {:color :red, :type 0})
    (swap! game-atom play-card {:color :wild, :type :wild})
    (swap! game-atom assoc :current-color :blue)
    (swap! game-atom play-card {:color :blue :type 8})
    (swap! game-atom play-card {:color :blue :type 7})
    (swap! game-atom draw-card)
    (swap! game-atom update-in [:players 2 :hand]
           conj
           {:color :blue :type :draw-two})
    (swap! game-atom play-card {:color :blue :type :draw-two})
    (swap! game-atom update-in [:players 0 :hand]
           conj
           {:color :blue :type :reverse})
    (swap! game-atom play-card {:color :blue :type :reverse})))

2025-09-19T20:26:39.008239Z

that was my mistake, I was thinking of drop-last

πŸ‘ 1
2025-09-19T20:27:02.016369Z

or just take to decide how many to process really

πŸ‘ 1
daveliepmann 2025-09-18T19:46:17.328359Z

That is a fine way to do it. Another way which might illuminate other possibilities would be to represent state as a map passed from function to function. So instead of the game-atom (state) taking play-card and parameters, play-card would get the state as any old map parameter. Test data in the REPL could then be a declarative sequence of moves resulting in a seq of states (or states-and-moves), letting you inspect from a slightly higher vantage point.

gunnar 2025-09-18T20:22:02.762499Z

If you haven't already, I can highly recommend watching the https://www.parens-of-the-dead.com. You can find much inspiration there for developing and structuring a game in clojure.

Colton Goates 2025-09-18T20:59:57.117329Z

@gar thanks, I'll check that out! @daveliepmann Thanks for the response! I'm not sure I understand the point about the state map; play-card already takes the contents of the game-atom, which is a map representing game state. Do you mean something like this code, like my example, but without swapping atom state? Then just observing the state at the end of the threaded calls?

(-> (make-init-game)
      (play-card {:color :red, :type 5})
      (play-card {:color :red, :type 0})
      (play-card {:color :wild, :type :wild})
      (assoc :current-color :blue)
      (play-card {:color :blue :type 8})
      (play-card {:color :blue :type 7})
      (draw-card)
      (update-in [:players 2 :hand]
                 conj
                 {:color :blue :type :draw-two})
      (play-card {:color :blue :type :draw-two})
      (update-in [:players 0 :hand]
                 conj
                 {:color :blue :type :reverse})
      (play-card {:color :blue :type :reverse}))

πŸ‘ 1
2025-09-18T21:50:21.851879Z

the version without the swaps makes more sense to me. you might find it useful to add a vector that acts as a log of plays, draws and game states (could be useful for debugging)

2025-09-18T21:52:31.641769Z

also instead of threading many calls to play-card etc., you could have a vector of moves, and a reduce that consumes individual data structures describing moves and returns new game state

2025-09-18T21:53:22.235529Z

that way, replacing reduce with reductions would give you a sequence of intermediate states

2025-09-18T21:56:26.535179Z

I am imagining something like

(def moves
  [{:action :play-card :data {:color :red, :type 5}}
   {:action :play-card :data {:color :red, :type 0}}
   {:action :change-color :data {:color :blue}}
   {:action :draw-card :data {:player 2 {:color :blue :type :draw-two}}])

2025-09-18T21:57:26.036909Z

also as I look closer at the individual steps, I see a mix of declarative game actions, and place specific data updates - IMHO it's worth the extra work to make every step declarative and move the place specific things into the implementation

2025-09-18T21:58:01.503859Z

eg. when you decide to give players names instead of vector indexes, it's easier if the code to change is in one place

2025-09-18T21:59:11.108119Z

or, when you decide to use a deck instead of hard coding which cards are drawn

2025-09-18T22:00:53.515149Z

this is a great chance to explore event sourcing, and representing games as graphs, if those are at all interesting to you (both of these abstractions are useful beyond games)

Colton Goates 2025-09-18T23:06:42.905029Z

@noisesmith > the version without the swaps makes more sense to me. you might find it useful to add a vector that acts as a log of plays, draws and game states (could be useful for debugging) The one without the swaps certainly looks cleaner, the problem is that I can't run a single piece of the pipeline at once, hence the need tor the atom. I actually already have a log vector, but it's a simple string--it might be better to log other game states too! > that way, replacing reduce with reductions would give you a sequence of intermediate states I like the idea of reducing a collection of moves to see intermediate states, that's really cool. > this is a great chance to explore event sourcing, and representing games as graphs, if those are at all interesting to you (both of these abstractions are useful beyond games) I really like graphs, it would be fun to use them! I'll keep event sourcing in mind, too!

2025-09-19T02:48:44.919109Z

with the version using a vector of inputs, you can use butlast to see the state up to a certain step

Colton Goates 2025-09-19T05:02:03.113589Z

@noisesmith I used reductions below, it works quite well.

(reductions (fn [game move]
                (apply (:func move)
                       (cons game (:args move))))
              (make-init-game)
              [{:func play-card :args [{:color :red, :type 5}]}
               {:func play-card :args [{:color :red, :type 5}]}
               {:func play-card :args [{:color :red, :type 0}]}
               {:func play-card :args [{:color :wild, :type :wild}]}])
Can you provide an example of how I might use butlast? butlast can only remove one element, but drop-last can remove multiple, so it seems like I'd want to use drop-last

2025-09-18T22:32:32.451049Z

Where can I find some good codebases that I can read and learn from? Would you suggest any specific project?

hrtmt brng 2025-09-21T08:17:47.186809Z

I don't recommend to look too much at other's code. You should become your own teacher. Always ask yourself if there could be a better way of doing something. I recommend reading a lot in the Clojure documentation. Again and again. The Clojure standard library is so full of features and secrets. You will hardly find them all in existing code. Over the time you will discover this. The learning experience is much nicer than just reading other code. In my experience, Clojure is a very learning friendly language. In C++ you need thick books, because you really don't see how to efficiently use all the constructs the language has. In C++ there is a big gap between understanding a language construct and successfully applying it in practice. In Clojure I don't feel such a gap. In Clojure I can read something in the documentation and immediately see how this can be used in the real world. In my experience, learning Clojure is learning to read documentation, while learning other languages is more like hacking around examples that you find in text books or other projects. For me it is a very satisfying experience to read something (for example https://clojuredocs.org/clojure.core/for) and being able to directly start using all of this without the need to learn anything else. The documentation is so full of examples. Quite often there is even exactly what you need. That is so great and so satisfactory! Sorry, I am a bit emotional here. With Clojure I feel like back in the 90s, where computers were new. There was so much to try out and learning was just a personal liaison between you, the documentation and your problem.

1
teodorlu 2025-09-19T07:30:05.466059Z

I learned a ton from https://github.com/nextjournal/calendula. It's very direct: does something real (book calendar appointments) without excessive indirection.

πŸ‘€ 1
seancorfield 2025-09-18T22:38:58.903569Z

Depending on where you are on your learning journey, maybe Metabase? There aren't many applications that are open source to read -- and a lot of libraries do "unusual" stuff that isn't always very idiomatic. I generally recommend learning from books (esp. the various Pragmatic Press/Bookshelf Clojure books -- Getting Clojure to start with, Programming Clojure next, Clojure Applied after that), and asking lots of questions here.

3
Alex Miller (Clojure team) 2025-09-18T22:42:43.303449Z

speaking of which, Pragmatic is having a flash sale NOW (code = flashsale) for 45% off everything!

πŸ™Œ 2
2025-09-18T22:45:37.865099Z

Thanks for letting me know @alexmiller (these discounts help brazilian developers a lot).

😎 2
2025-09-22T14:29:35.976779Z

Thanks for you answer @hbrng.computer. My intent of taking a look at other codebases is based on a vision that "to know how to write good code, we need to be exposed to good code". I believe this approach also helps to see different ways to reach the same goal when writing code to solve a specific problem. Maybe my approach is a bit odd, but I'm taking your suggestion as a valid approach (I never spent too much time on the docs, maybe this is the time to take a further look).