{"id":30160149,"url":"https://github.com/tessellator/bell","last_synced_at":"2025-08-11T15:35:42.118Z","repository":{"id":51225427,"uuid":"520317399","full_name":"tessellator/bell","owner":"tessellator","description":"A simple Clojure HTTP router. Zero dependencies. Zero macros.","archived":false,"fork":false,"pushed_at":"2024-06-16T14:19:09.000Z","size":21,"stargazers_count":6,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-07-16T18:58:18.413Z","etag":null,"topics":["babashka","clojure","http","ring","routing"],"latest_commit_sha":null,"homepage":"","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tessellator.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-08-02T01:52:38.000Z","updated_at":"2024-06-10T23:29:38.000Z","dependencies_parsed_at":"2023-11-07T05:38:51.493Z","dependency_job_id":"af7a3745-0b41-48cb-9160-ab70c288be97","html_url":"https://github.com/tessellator/bell","commit_stats":{"total_commits":5,"total_committers":1,"mean_commits":5.0,"dds":0.0,"last_synced_commit":"5b33cf2ac429cbe8026533f5e6217c294430c2af"},"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/tessellator/bell","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tessellator%2Fbell","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tessellator%2Fbell/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tessellator%2Fbell/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tessellator%2Fbell/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tessellator","download_url":"https://codeload.github.com/tessellator/bell/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tessellator%2Fbell/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":269914968,"owners_count":24495635,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-11T02:00:10.019Z","response_time":75,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["babashka","clojure","http","ring","routing"],"created_at":"2025-08-11T15:35:38.570Z","updated_at":"2025-08-11T15:35:42.104Z","avatar_url":"https://github.com/tessellator.png","language":"Clojure","readme":"# Bell\n\nA simple Clojure HTTP router. Zero dependencies. Zero macros.\n\n[![Clojars Project](https://img.shields.io/clojars/v/net.tessellator/bell.svg)](https://clojars.org/net.tessellator/bell)\n[![cljdoc badge](https://cljdoc.org/badge/net.tessellator/bell)](https://cljdoc.org/d/net.tessellator/bell/CURRENT)\n[![bb compatible](https://raw.githubusercontent.com/babashka/babashka/master/logo/badge.svg)](https://babashka.org)\n\n## Why another HTTP router?\n\nThere are plenty of good options for HTTP routers today. However, I felt that\nmost of them were far more complex than necessary for my use cases. I wanted\na small router that was simple to understand and use.\n\nThis router is heavily inspired by https://github.com/matryer/way, which is a\nsimple HTTP router for Go applications. (There's also a dash of\nhttps://go-chi.io/ too.)\n\n## Example\n\n```clojure\n(ns sample.core\n  (:require [bell.core :refer [GET router]]\n            [ring.adapter.jetty :refer [run-jetty]]))\n\n(defn get-id-handler [request]\n  {:status 200\n   :headers {}\n   :body (get-in request [:path-params :id])})\n\n(def handler\n  (router\n   (GET \"/paths/:id\" get-id-handler)))\n\n(defn -main [\u0026 _args]\n  (run-jetty handler {:port 8080 :join? false}))\n```\n\n## Concepts\n\nBell provides a few concepts and each has a corresponding function: `route`,\n`group`, `subrouter`, and `router`. The details of each will be discussed in\nturn.\n\nA route matches an HTTP verb and pattern to a handler. If a request does not\nmatch the route specified, the handler will return nil. This differs from\ntypical ring handlers, which should always return a response. However, a route\nis usually not used in isolation, and the `router` function will return a ring\nhandler that is guaranteed to return a response. Routes are usually composed by\na router.\n\nAn example for defining a route might look like the following:\n\n```clojure\n(bell/route :get \"/my/path/:id\" my-path-id-handler)\n```\n\nNote that the pattern contains `:id`. This specified a path parameter named id.\nWhen a request matches a route, the path parameters will be parsed into a map\non the request under the key `:path-params`.\n\nPatterns may also serve as prefixes. If a pattern ends with `/` (except the root\nroute) or `...`, the pattern is treated as a prefix. Here are some examples. The\npattern `/api/` matches `/api/some/other/path` and the pattern\n`/images/image-...` matches `/images/image-logo.png` but not\n`images/logo.png`.\n\nThere are convenience methods for each of the HTTP methods (e.g., GET), and a\nspecial one, ANY, that matches any method.\n\nA group is a collection of routes. Groups are useful for attaching middleware to\nseveral routes at once. It is important to note that there is not an analog to\ncompojure's `wrap-matched-routes`. If you want to ensure that a middleware only\napplies to a matched handler, apply it to each route individually.\n\nA group will try to apply each route in order. If a route returns nil, it is\nconsidered unmatched, and the group will attempt the next route in the group. If\nno routes match, the group will return nil.\n\nA subrouter is a collection of routes that are mounted at a prefix path (e.g.,\n`/api`). Path parameters that are part of the prefix will be parsed into the\n`:path-params` map. Like a group, a subrouter will apply the request to each\nroute in order until one is matched.\n\nA router is a group of routes that includes a default not-found handler. It is\nguaranteed to return a response, and it is generally the top-level concept used.\n\n## Extending\n\nOne of the key features of bell is that a handler that returns nil indicates\nthat a particular route has not been matched and that bell should attempt to\nmatch against the next route in its sequence. You may use this feature to\nwrite middlewares that return nil to force bell to attempt matching against\nanother route.\n\nOne potential example would be implementing a middleware that ensures that path\nparameters conform to a spec. The middleware would be applied to a handler. It\nwould pull out the path parameters and validate them against a spec. If the\nparameters are valid, the middleware will pass the request on to the handler.\nHowever, if the parameters do not conform, the middleware may return nil to\nindicate to bell that the route was not matched. Bell will then move on to the\nnext route.\n\n```clojure\n(bell/GET \"/my/path/:id\" (wrap-ensure-spec my-handler [:map [:id :uuid]]))\n```\n\n## Design Decisions\n\n- Simple \u003e fast. I wanted to build something small that could be read and\n  understood in its entirety in 30min or less. I do trade off some speed to\n  maintain simplicity. That does not mean that bell is slow, but that it is not\n  as fast as some other routers. I believe bell is suited for most use cases.\n\n- No macros. Macros are confusing to read, write, and debug. They should be\n  used sparingly. They are particularly difficult for newcomers to understand.\n  Ring provides a great model of composing functions, and bell builds on this\n  approach.\n\n- No default data representation. Bell tries to be as unopinionated as possible.\n  It is a pretty straightforward endeavor to write a function that parses\n  whatever data structure you like and transforms it into a router based on\n  bell's functions.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftessellator%2Fbell","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftessellator%2Fbell","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftessellator%2Fbell/lists"}