{"id":29894135,"url":"https://github.com/juxt/pick","last_synced_at":"2025-08-01T04:45:18.787Z","repository":{"id":70995157,"uuid":"276588496","full_name":"juxt/pick","owner":"juxt","description":"A Clojure library for HTTP server-driven content negotiation.","archived":false,"fork":false,"pushed_at":"2024-01-14T15:34:47.000Z","size":120,"stargazers_count":19,"open_issues_count":3,"forks_count":1,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-07-29T14:45:00.821Z","etag":null,"topics":["clojure","content-negotation","http","web"],"latest_commit_sha":null,"homepage":"https://juxt.pro","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/juxt.png","metadata":{"files":{"readme":"README.adoc","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}},"created_at":"2020-07-02T08:15:46.000Z","updated_at":"2025-02-14T11:24:54.000Z","dependencies_parsed_at":null,"dependency_job_id":"c9073960-9c5a-468c-84d8-e653d4352ac6","html_url":"https://github.com/juxt/pick","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/juxt/pick","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/juxt%2Fpick","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/juxt%2Fpick/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/juxt%2Fpick/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/juxt%2Fpick/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/juxt","download_url":"https://codeload.github.com/juxt/pick/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/juxt%2Fpick/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268171714,"owners_count":24207417,"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-01T02:00:08.611Z","response_time":67,"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":["clojure","content-negotation","http","web"],"created_at":"2025-08-01T04:45:16.129Z","updated_at":"2025-08-01T04:45:18.744Z","avatar_url":"https://github.com/juxt.png","language":"Clojure","readme":"= pick\n\nA Clojure library for HTTP server-driven content negotiation.\n\n[WARNING]\n--\nSTATUS: Stable. In use.\n--\n\n== Quick Start\n\nSuppose you are writing a web service and want to provide proactive content\nnegotiation based on the accept headers in the client request. Suppose also that\nyou have established which representations you want to choose between.\n\nA representation is a map that one or more of the following keys:\n\n* `:juxt.http/content-type` -- the media-type and charset of the representation\n* `:juxt.http/content-encoding` -- how the representation has been encoded (e.g. `gzip`)\n* `:juxt.http/content-language` -- the natural language of the representation (e.g. `es`)\n* `:juxt.http/content-length` -- the length of the payload\n\n.An example representation\n====\n[source,clojure]\n----\n{\n…\n {:juxt.http/content-type \"text/html;charset=utf-8\"\n  :juxt.http/content-encoding \"gzip\"\n  :juxt.http/content-language \"en-US\"\n  :juxt.http/content-length 20578}\n…\n}\n----\n====\n\nYou can use *pick* to select the representation that is most acceptable to the user agent.\n\nHere's how:\n\n[source,clojure]\n----\n(require '[juxt.pick.ring :refer [pick]])\n\n(pick\n  ;; A ring request (where headers indicate preferences)\n  {:request-method :get\n   :uri \"/\"\n   :headers {\"accept\" \"text/html\"\n             \"accept-language\" \"de\"}}\n\n  ;; Possible representations\n  [\n   {:id 1\n    :juxt.http/content-type \"text/html;charset=utf-8\"\n    :juxt.http/content-language \"en\"}\n\n   {:id 2\n    :juxt.http/content-type \"text/html;charset=utf-8\"\n    :juxt.http/content-language \"de\"}\n\n   {:id 3\n    :juxt.http/content-type \"text/plain;charset=utf-8\"}])\n\n=\u003e\n{:juxt.pick/representation {…} ; most acceptable representation\n :juxt.pick/representations […] ; all representations (scored)\n}\n\n----\n\n== Introduction\n\nContent negotiation is a feature of HTTP that allows different 'representations'\nto be served from the same URI.\n\n*pick* is a Clojure library that determines quality values of each of the\nnegotiable dimensions and corresponding rules specified in RFC 7231. These can\nbe used by content negotiation algorithms to determine the most appropriate\ncontent to respond with.\n\n*pick* _also_ includes a built-in reference implementation based on Apache's\n _de-facto_\n https://httpd.apache.org/docs/current/en/content-negotiation.html#algorithm[content\n negotiation algorithm].\n\n=== Problem Statement\n\nContent negotiation is one of the primary ways that the web is\ninclusive, supporting a wide range of user agents and other software-based\nclients.\n\nContent negotiation can also contribute to improved performance, by allowing the\ncompression of content for optimising network bandwidth and by cache\nintegration.\n\nAlso, since content negotiation allows for gradual migration from old to new\ndata formats it enables graceful and continuous improvement, balancing the needs\nfor both innovation and solid dependability.\n\nHowever, while content negotiation is often used for static resources, which is\nwell implemented by web servers such as Apache's httpd and nginx, it is often\nmissing from, or poorly implemented by, the majority of web libraries and\nframeworks.\n\n=== API Reference\n\nThe `pick` function 3 arguments:\n\n* The Ring request\n* A collection of possible representations\n* An option map\n\nThe option map supports the following entries:\n\n[cols=\"3m,5\"]\n|===\n|:juxt.pick/explain?|if truthy, provide an explain in the return value\n|:juxt.pick/vary?|if truthy, compute the preferences that will vary the choice\n|===\n\n=== API alternative: Use of metadata strings\n\nUp until now, we have described a representation as a Clojure map with keywords in the `juxt.http` namespace.\nAlternatively, a representation can be any object with Clojure metadata.\nThe metadata should contain the string keywords corresponding to the HTTP representation metadata.\n\n[%header,cols=\"1m,1m\"]\n|===\n|keyword|metadata string alternative\n|:juxt.http/content-type|\"content-type\"\n|:juxt.http/content-encoding|\"content-encoding\"\n|:juxt.http/content-language|\"content-language\"\n|:juxt.http/content-length|\"content-length\"\n|===\n\nFor example, here is a representation defined as a Clojure function.\n\n[source,clojure]\n----\n^{\"content-type\" \"text/html;charset=utf-8\"\n  \"content-language\" \"en\"}\n(fn [] …)\n----\n\n=== Alternatives\n\nMost Clojure alternatives only deal with media-types, and perhaps charsets. Note\nthat *pick* fully implements _all_ proactive (server-driven) content negotiation\nrules contained in https://tools.ietf.org/html/rfc7231[RFC 7231], including\nmedia-types, charsets, encodings, languages, wildcards, qvalues and precedence\nrules. Where not prescribed specifically by the RFC, *pick* adopts the\n_de-facto_ decisions made by the Apache httpd engine.\n\nhttps://github.com/ngrunwald/ring-middleware-format[ring-middleware-format] and\nMetosin's https://github.com/metosin/muuntaja[Muuntaja] library negotiate\nmedia-types and charsets, and perform automatic decoding/encoding of\nrequest/response bodies\n\n[CAUTION]\n--\n*pick* only provides the decision of which representation(s) to select,\ngiven the context of an HTTP request and a set of representation to choose between.\n\nUnlike Muuntaja, *pick* does _not_ include support for format transformation or coercion, which is considered _out of scope_ for this library.\n--\n\nhttps://github.com/clojure-liberator/liberator/commits/master[Liberator]\n\nhttps://github.com/juxt/yada[yada]\n\n== References\n\nhttps://tools.ietf.org/html/rfc7231[RFC 7231] is the normative standard on content negotiation.\n\nThis https://developer.mozilla.org/en-US/docs/Web/HTTP/Content_negotiation[MDN guide on content negotiation from Mozilla] is very informative.\n\nhttps://httpd.apache.org/docs/current/en/content-negotiation.html#algorithm\n\nWhile *pick* attempts to be reasonably performant, due to the per-request nature\nof content negotiation some users may consider using a memoization strategy,\nmaking use of a memoization library such as\nhttps://github.com/clojure/core.memoize[clojure.core.memoize].\n\n== License\n\nThe MIT License (MIT)\n\nCopyright © 2020-2023 JUXT LTD.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjuxt%2Fpick","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjuxt%2Fpick","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjuxt%2Fpick/lists"}