HackerReposA FIELD GUIDE TO OPEN SOURCE
ProSponsor the shelf♧ 0
← Back to top all-time
ring-clojure
ring
Clojure2012–2026
ring-clojure / ring

ring

Clojure HTTP server abstraction

21Distinct people
28Mentions
1Explicit recommenders
3.9KGitHub stars
REPOSITORY AT A GLANCE

What to know before exploring

What it is

Clojure HTTP server abstraction

GitHub

Where it is used

No documented product use was verified in the last source check.

What people are saying

Mitchell: First allow me to apologize for the tone of my comment. Re-reading it now, I realize that I glossed over a key point: I used Vagrant every day for a team of 5 people.…

snprbob86 used a recommendation phrase. One indexed comment matches. This is a phrase match, not a verdict.

No classified warning is attached to this page. Read the discussions before treating silence as approval.

Also in these discussions

Mentioned in the same Hacker News threads. That is not proof they are substitutes.

This week on Hacker News

Nov 8–Nov 15 UTC · Direct repository links and separately labeled related project stories. Indexed through 11/15/2026, 12:45:45 PM UTC.

No direct repository links were found in the indexed HN discussions over the past seven days. Older discussions are available below.

Mentions over time

HN mentions by year28 dated mentions

Observed mentions: 2012-08-20 17:16:56 UTC to 2026-08-12 14:24:30 UTC.

02462012-01-01 00:00:00 UTC to 2013-01-01 00:00:00 UTC (exclusive): 1 mentions; 0 submissions, 1 comments20122013-01-01 00:00:00 UTC to 2014-01-01 00:00:00 UTC (exclusive): 6 mentions; 0 submissions, 6 comments2014-01-01 00:00:00 UTC to 2015-01-01 00:00:00 UTC (exclusive): 2 mentions; 0 submissions, 2 comments2015-01-01 00:00:00 UTC to 2016-01-01 00:00:00 UTC (exclusive): 2 mentions; 0 submissions, 2 comments2016-01-01 00:00:00 UTC to 2017-01-01 00:00:00 UTC (exclusive): 4 mentions; 0 submissions, 4 comments2017-01-01 00:00:00 UTC to 2018-01-01 00:00:00 UTC (exclusive): 0 mentions; 0 submissions, 0 comments2018-01-01 00:00:00 UTC to 2019-01-01 00:00:00 UTC (exclusive): 2 mentions; 0 submissions, 2 comments2019-01-01 00:00:00 UTC to 2020-01-01 00:00:00 UTC (exclusive): 5 mentions; 0 submissions, 5 comments20192020-01-01 00:00:00 UTC to 2021-01-01 00:00:00 UTC (exclusive): 3 mentions; 2 submissions, 1 comments2021-01-01 00:00:00 UTC to 2022-01-01 00:00:00 UTC (exclusive): 0 mentions; 0 submissions, 0 comments2022-01-01 00:00:00 UTC to 2023-01-01 00:00:00 UTC (exclusive): 1 mentions; 0 submissions, 1 comments2023-01-01 00:00:00 UTC to 2024-01-01 00:00:00 UTC (exclusive): 1 mentions; 0 submissions, 1 comments2024-01-01 00:00:00 UTC to 2025-01-01 00:00:00 UTC (exclusive): 0 mentions; 0 submissions, 0 comments2025-01-01 00:00:00 UTC to 2026-01-01 00:00:00 UTC (exclusive): 0 mentions; 0 submissions, 0 comments2026-01-01 00:00:00 UTC to 2027-01-01 00:00:00 UTC (exclusive): 1 mentions; 0 submissions, 1 comments. Incomplete calendar period.2026*

* An incomplete calendar period is marked with an outlined bar. Its count is not directly comparable with a full period.

Counts come from the repository’s dated source evidence. Index cutoff: 2026-11-02 08:16:59 UTC.

View mention counts and date ranges
UTC ranges include their start and exclude their end. Select a period to filter the source evidence.
PeriodStart (UTC)End (exclusive, UTC)MentionsSubmissionsComments
20122012-01-01 00:00:00 UTC2013-01-01 00:00:00 UTC101
20132013-01-01 00:00:00 UTC2014-01-01 00:00:00 UTC606
20142014-01-01 00:00:00 UTC2015-01-01 00:00:00 UTC202
20152015-01-01 00:00:00 UTC2016-01-01 00:00:00 UTC202
20162016-01-01 00:00:00 UTC2017-01-01 00:00:00 UTC404
20172017-01-01 00:00:00 UTC2018-01-01 00:00:00 UTC000
20182018-01-01 00:00:00 UTC2019-01-01 00:00:00 UTC202
20192019-01-01 00:00:00 UTC2020-01-01 00:00:00 UTC505
20202020-01-01 00:00:00 UTC2021-01-01 00:00:00 UTC321
20212021-01-01 00:00:00 UTC2022-01-01 00:00:00 UTC000
20222022-01-01 00:00:00 UTC2023-01-01 00:00:00 UTC101
20232023-01-01 00:00:00 UTC2024-01-01 00:00:00 UTC101
20242024-01-01 00:00:00 UTC2025-01-01 00:00:00 UTC000
20252025-01-01 00:00:00 UTC2026-01-01 00:00:00 UTC000
2026 (partial)2026-01-01 00:00:00 UTC2027-01-01 00:00:00 UTC101

From the project README

An excerpt from the repository, fetched Nov 15, 2026.

Read the project’s introduction
Ring Ring is a Clojure web applications library inspired by Python's WSGI and Ruby's Rack. By abstracting the details of HTTP into a simple, unified API, Ring allows web applications to be constructed of modular components that can be shared among a variety of applications, web servers, and web frameworks. The SPEC.md file at the root of this distribution provides a complete description of the Ring interface. The Wiki contains more in-depth documentation on how to use Ring. Libraries ring/ring - meta-package containing all relevant dependencies ring/ring-core - core functions and middleware for Ring handlers, requests and responses org.ring-clojure/ring-core-protocols - contains only the protocols necessary for building Ring responses org.ring-clojure/ring-websocket-protocols - contains only the protocols necessary for WebSockets ring/ring-devel - functions for developing and debugging Ring applications ring/ring-servlet - construct legacy Java Servlets (≤ 4.0) from Ring handlers org.ring-clojure/ring-jakarta-servlet construct Jakarta Servlets (≥ 5.0) from Ring handlers ring/ring-jetty-adapter - a Ring adapter that uses an embedded Jetty web server Installation To include one of the above libraries, for instance ring-core, add the following dependency to your deps.edn file: ring/ring-core {:mvn/version "1.15.6"} Or to your Leiningen project file: [ring/ring-core "1.15.6"] Documentation Changelog Wiki API docs Contributing Please read CONTRIBUTING.md before submitting a pull request. Thanks This project borrows heavily from Ruby's Rack and Python's WSGI; thanks to those communities for their work. Thanks also go to the many individuals who have contributed to Ring's code and documentation over the years. License Copyright © 2009-2026 Mark McGranaghan, James Reeves & contributors. Released under the MIT license.
Read the full README on GitHub

What Hacker News says

5 sources
Some of my favorite Clojure libraries in this space: - instaparse: takes EBNF (and other formats) as a string and gives you a parser instantly (!!!) https://github.com/Engelberg/instaparse - re-frame: React-Redux alternative that IMO is much easier to work with https://github.com/day8/re-frame - reagent: React wrapper https://reagent-project.github.io/ - ring https://github.com/ring-clojure/ring - fulcro https://github.com/fulcrologic/fulcro More than any library, though, what keeps bringing me back to Clojure is its incredibly blissful set of persistent data structures. It's a pernicious accident of history that we are taught to accept references to mutable objects as normal, inevitable, and desirable in most CS curricula. It is much easier to develop, debug, and maintain immutable data structures instead, and I consider my good fortune
Read the rest
every time I get to debug something in Clojure rather than in some reference-laden OOP soup.
In Using Clojure for Web Apps
A proof of correctness is what static typing is fundamentally. The example I provided simply turns this up to 11. However, if you relax the constraint then you're losing benefits of statically checking properties at compile time. If you agree that having a 10 line version that does less checking is better than having a 260 line version that does, then you're agreeing that there is a cost associated with static checking. There are plenty of real world scenarios where static checking gets in the way even if you're not trying to encode complex properties using the type system. One problem is that static typing is at odds with modularization since type declarations are considered globally. For example, Ring HTTP abstraction in Clojure represents requests and responses as maps. Middleware functions [1] can update these maps to inject additiona
Read the rest
l keys, or modify existing keys. These functions often live in separate libraries that know nothing about one another. A static type system precludes this since it would require you to provide a static declaration for every possible request and response map. [1] https://github.com/ring-clojure/ring/wiki/Middleware-Pattern...
In What I Want from a Type System (2016)
It's more about controlling state. I like to think of ring[0] as a really nice example of this that's pretty easy to reason about. It abstracts http handling into a series of key/value data structures. The request that's pulled in looks like this: {:remote-addr "localhost" :headers {"host" "localhost" "content-type" "application/json" "accept" "application/json"} :server-port 80 :content-type "application/json" :uri "/" :server-name "localhost" :query-string nil :body "" :scheme :http :request-method :get} The response like this: {:status 200 :headers {"Content-Type" "text/plain"} :body "Hello World"} Functions manipulate these structures, but their input and outputs are primarily those data structures. (defn handler [request] {:status 200 :headers {"Con
Read the rest
tent-Type" "text/plain"} :body "Hello World"}) Your app is now just a function stack that get's called in this order: (def app (-> handler (wrap-content-type "text/html"))) Request => wrap-content-type => handler (generate response) => wrap-content-type => Returned Want to add middleware? Define a function that handles it, here's an example[1]: (defn middleware [handler] (fn [request] ;; Do something to the request before sending it down the chain. (let [response (handler request)] ;; Do something to the response that's coming back up the chain. response))) Then add the function to your app: (def app (-> handler (wrap-content-type "text/html") (wrap-keyword-params) (wrap-params))) You want to handle session data? Ring provides wrap session[2], add it and then use your own middleware or handler to manipulate the session by just changing the value at :session. Example [3]: (defn handler [{session :session}] (let [count (:count session 0) session (assoc session :count (inc count))] (-> (response (str "You accessed this page " count " times.")) (assoc :session session)))) Much of your world is now reduced down to: * Does this data structure look correct? * How can I fix it? * Do I need some resource like a db connection? Ok how do I add it to my data structure? It all composes, and you end up worrying a lot less. Finally I find that doing things this way ends up creating programs that just have much less explicit state, as opposed to temporary state that's derived from other values and so only changes when those things change. You can program in other languages like this, it's just a bit harder, and you have to hope that no one else you're working with breaks one of the assumptions that a system like this is built on, as opposed to having the language enforce those for you. Sorry if I've been unclear =)... [0] - https://github.com/ring-clojure/ring/wiki (Examples taken from ring). [1] - https://stackoverflow.com/a/19459508 [2] - https://github.com/ring-clojure/ring/blob/95e4ca25d5b98c45f9... [3] - https://github.com/ring-clojure/ring/wiki/Sessions
In The Functional Style (2018)
Except that they're not the same. Static typing restricts you to a set of statements that can be verified by the type checker. This is a subset of all valid statements, otherwise you could just type check code in any language at compile time. Static typing makes many dynamic patterns either difficult or impossible to use. For example, Ring middleware becomes pretty much impossible in a static language https://github.com/ring-clojure/ring/wiki/Middleware-Pattern... The pattern here is that the request and response are expressed as maps. The request map is passed through a set of middleware functions, and each one can modify this map in some way. A dynamic language makes it possible to write middleware libraries that know absolutely nothing about each other, and compose seamlessly. A static language would require you to provide a full descr
Read the rest
iption of every possible permutation of the request map, and every library would have to conform to it. This creates coupling because any time a library needs to create a new key that only it cares about, the global spec needs to be modified to support it. My experience is that immutability plays a far bigger role than types in addressing the problem of maintainability. Immutability as the default makes it natural to structure applications using independent components. This indirectly helps with the problem of tracking types in large applications as well. You don't need to track types across your entire application, and you're able to do local reasoning within the scope of each component. Meanwhile, you make bigger components by composing smaller ones together, and you only need to know the types at the level of composition which is the public API for the components.
In Fearless Concurrency: Clojure, Rust, Pony, Erlang and Dart
There is a cost associated with eliminating this class of errors using static typing. The cost is that you're restricted to a set of statements that the type checker can verify to be correct. Writing code for the benefit of the type checker is often at odds with writing it in a way that conveys the meaning best to the human reader. This is necessarily less expressive than the dynamic approach. Code written in dynamic languages tends to do a better job of expressing its intent because it can be written in a more direct fashion. Here's a concrete real world example of what I'm talking about: >When I first wrote the core.async go macro I based it on the state monad. It seemed like a good idea; keep everything purely functional. However, over time I've realized that this actually introduces a lot of incidental complexity. And let me explain t
Read the rest
hat thought. >What are we concerned about when we use the state monad, we are shunning mutability. Where do the problems surface with mutability? Mostly around backtracking (getting old data or getting back to an old state), and concurrency. >In the go macro transformation, I never need old state, and the transformer isn't concurrent. So what's the point? Recently I did an experiment that ripped out the state monad and replaced it with mutable lists and lots of atoms. The end result was code that was about 1/3rd the size of the original code, and much more readable. >So more and more, I'm trying to see mutability through those eyes: I should reach for immutable data first, but if that makes the code less readable and harder to reason about, why am I using it? https://groups.google.com/forum/#!topic/clojure/wccacRJIXvg Another example of something that's trivial in a dynamic language, but difficult to do in a static one would be Ring middleware: https://github.com/ring-clojure/ring/wiki/Middleware-Pattern... So really what you're doing with static typing is trading one set of problems for another. This is perfectly fine if those are the kinds of problems you prefer to deal with, but it's important to recognize that you are making a trade off as opposed to getting something for free here.
In IBM releases Elm-powered app

MAKE YOURSELF AT HOME

A few shortcuts

/
Search the collection
?
Open this guide
Esc
Close dialogs and navigation

Your saved repositories and interests stay in this browser.