{"id":15010705,"url":"https://github.com/metosin/muuntaja","last_synced_at":"2025-05-14T03:06:47.536Z","repository":{"id":10396967,"uuid":"65613306","full_name":"metosin/muuntaja","owner":"metosin","description":"Clojure library for fast http api format negotiation, encoding and decoding.","archived":false,"fork":false,"pushed_at":"2025-03-05T17:42:14.000Z","size":1282,"stargazers_count":469,"open_issues_count":22,"forks_count":52,"subscribers_count":21,"default_branch":"master","last_synced_at":"2025-05-13T22:10:29.252Z","etag":null,"topics":["clojure","content-negotiation","edn","http","interceptor","json","metosin-stable","middleware","ring","transit"],"latest_commit_sha":null,"homepage":"https://cljdoc.org/d/metosin/muuntaja","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"epl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/metosin.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","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":"2016-08-13T11:01:21.000Z","updated_at":"2025-05-03T06:23:58.000Z","dependencies_parsed_at":"2024-06-18T18:43:45.681Z","dependency_job_id":"7d44d7f2-f23c-4118-8a64-c5cb1f01f618","html_url":"https://github.com/metosin/muuntaja","commit_stats":{"total_commits":780,"total_committers":52,"mean_commits":15.0,"dds":"0.35769230769230764","last_synced_commit":"747acdb895abf125208c17e652ac669c78d3b52e"},"previous_names":[],"tags_count":21,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/metosin%2Fmuuntaja","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/metosin%2Fmuuntaja/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/metosin%2Fmuuntaja/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/metosin%2Fmuuntaja/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/metosin","download_url":"https://codeload.github.com/metosin/muuntaja/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254059500,"owners_count":22007768,"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","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-negotiation","edn","http","interceptor","json","metosin-stable","middleware","ring","transit"],"created_at":"2024-09-24T19:35:26.424Z","updated_at":"2025-05-14T03:06:47.514Z","avatar_url":"https://github.com/metosin.png","language":"Clojure","funding_links":[],"categories":["Clojure"],"sub_categories":[],"readme":"# Muuntaja [![Continuous Integration status](https://secure.travis-ci.org/metosin/muuntaja.png)](http://travis-ci.org/metosin/muuntaja) [![cljdoc badge](https://cljdoc.xyz/badge/metosin/muuntaja)](https://cljdoc.xyz/jump/release/metosin/muuntaja)\n\n\u003cimg src=\"https://raw.githubusercontent.com/metosin/muuntaja/master/doc/images/muuntaja-small.png\" align=\"right\"/\u003e\n\nClojure library for fast HTTP format negotiation, encoding and decoding. Standalone library, but ships with adapters for ring (async) middleware \u0026 Pedestal-style interceptors. Explicit \u0026 extendable, supporting out-of-the-box [JSON](http://www.json.org/), [EDN](https://github.com/edn-format/edn) and [Transit](https://github.com/cognitect/transit-format) (both JSON \u0026 Msgpack). Ships with optional adapters for [MessagePack](http://msgpack.org/) and [YAML](http://yaml.org/).\n\nBased on [ring-middleware-format](https://github.com/ngrunwald/ring-middleware-format),\nbut a complete rewrite ([and up to 30x faster](doc/Performance.md)).\n\n\u003e Hi! We are [Metosin](https://metosin.fi), a consulting company. These libraries have evolved out of the work we do for our clients.\n\u003e We maintain \u0026 develop this project, for you, for free. Issues and pull requests welcome!\n\u003e However, if you want more help using the libraries, or want us to build something as cool for you, consider our [commercial support](https://www.metosin.fi/en/open-source-support).\n\n## Rationale\n\n- explicit configuration\n- fast with good defaults\n- extendable \u0026 pluggable: new formats, behavior\n- typed exceptions - caught elsewhere\n- support runtime docs (like swagger) \u0026 inspection (negotiation results)\n- support runtime configuration (negotiation overrides)\n\n## Modules\n\n* `metosin/muuntaja` - the core abstractions + [Jsonista JSON](https://github.com/metosin/jsonista), EDN and Transit formats\n* `metosin/muuntaja-cheshire` - optional [Cheshire JSON](https://github.com/dakrone/cheshire) format\n* `metosin/muuntaja-charred` - optional [Charred](https://github.com/cnuernber/charred) format\n* `metosin/muuntaja-form` - optional `application/x-www-form-urlencoded` formatter using [ring-codec](https://github.com/ring-clojure/ring-codec)\n* `metosin/muuntaja-msgpack` - Messagepack format\n* `metosin/muuntaja-yaml` - YAML format\n\n## Posts\n\n* [Muuntaja, a boring library everyone should use](https://www.metosin.fi/blog/muuntaja/)\n\nCheck [the docs on cljdoc.org](https://cljdoc.org/d/metosin/muuntaja)\nfor detailed API documentation as well as more guides on how to use Muuntaja.\n\n## Latest version\n\n```clj\n[metosin/muuntaja \"0.6.11\"]\n```\n\nOptionally, the parts can be required separately:\n\n```clj\n[metosin/muuntaja-form \"0.6.11\"]\n[metosin/muuntaja-cheshire \"0.6.11\"]\n[fi.metosin/muuntaja-charred \"0.6.11\"]\n[metosin/muuntaja-msgpack \"0.6.11\"]\n[metosin/muuntaja-yaml \"0.6.11\"]\n```\n\nMuuntaja requires Java 1.8+\n\n## Quickstart\n\n### Standalone\n\nUse default Muuntaja instance to encode \u0026 decode JSON:\n\n```clj\n(require '[muuntaja.core :as m])\n\n(-\u003e\u003e {:kikka 42}\n     (m/encode \"application/json\"))\n; =\u003e #object[java.io.ByteArrayInputStream]\n\n(-\u003e\u003e {:kikka 42}\n     (m/encode \"application/json\")\n     slurp)\n; =\u003e \"{\\\"kikka\\\":42}\"\n\n(-\u003e\u003e {:kikka 42}\n     (m/encode \"application/json\")\n     (m/decode \"application/json\"))\n; =\u003e {:kikka 42}\n```\n\n### Ring\n\nAutomatic decoding of request body and response body encoding based on `Content-Type`, `Accept` and `Accept-Charset` headers:\n\n```clj\n(require '[muuntaja.middleware :as middleware])\n\n(defn echo [request]\n  {:status 200\n   :body (:body-params request)})\n\n; with defaults\n(def app (middleware/wrap-format echo))\n\n(def request\n  {:headers\n   {\"content-type\" \"application/edn\"\n    \"accept\" \"application/transit+json\"}\n   :body \"{:kikka 42}\"})\n\n(app request)\n; {:status 200,\n;  :body #object[java.io.ByteArrayInputStream]\n;  :headers {\"Content-Type\" \"application/transit+json; charset=utf-8\"}}\n```\n\nAutomatic decoding of response body based on `Content-Type` header:\n\n```clj\n(-\u003e request app m/decode-response-body)\n; {:kikka 42}\n```\n\nThere is a more detailed [Ring guide](doc/With-Ring.md) too. See also [differences](doc/Differences-to-existing-formatters.md) to ring-middleware-format \u0026 ring-json.\n\n### Interceptors\n\nMuuntaja support [Sieppari](https://github.com/metosin/sieppari) -style interceptors too. See [`muuntaja.interceptor`](https://github.com/metosin/muuntaja/blob/master/modules/muuntaja/src/muuntaja/interceptor.clj) for details.\n\nInterceptors can be used with [Pedestal](http://pedestal.io/) too, all but the `exception-interceptor` which conforms to the simplified exception handling model of Sieppari.\n\n### Configuration\n\nExplicit Muuntaja instance with custom EDN decoder options:\n\n```clj\n(def m\n  (m/create\n    (assoc-in\n      m/default-options\n      [:formats \"application/edn\" :decoder-opts]\n      {:readers {'INC inc}})))\n\n(-\u003e\u003e \"{:value #INC 41}\"\n     (m/decode m \"application/edn\"))\n; =\u003e {:value 42}\n```\n\nExplicit Muuntaja instance with custom date formatter:\n\n```clj\n(def m\n  (m/create\n    (assoc-in\n      m/default-options\n      [:formats \"application/json\" :encoder-opts]\n      {:date-format \"yyyy-MM-dd\"})))\n\n(-\u003e\u003e {:value (java.util.Date.)}\n     (m/encode m \"application/json\")\n     slurp)\n; =\u003e \"{\\\"value\\\":\\\"2019-10-15\\\"}\"\n```\n\nExplicit Muuntaja instance with camelCase encode-key-fn:\n\n```clj\n(require '[camel-snake-kebab.core :as csk])\n\n(def m\n  (m/create\n    (assoc-in\n      m/default-options\n      [:formats \"application/json\" :encoder-opts]\n      {:encode-key-fn csk/-\u003ecamelCase})))\n\n(-\u003e\u003e {:some-property \"some-value\"}\n     (m/encode m \"application/json\")\n     slurp)\n; =\u003e \"{\\\":someProperty\\\":\\\"some-value\\\"}\"\n```\n\nReturning a function to encode transit-json:\n\n```clj\n(def encode-transit-json\n  (m/encoder m \"application/transit+json\"))\n\n(slurp (encode-transit-json {:kikka 42}))\n; =\u003e \"[\\\"^ \\\",\\\"~:kikka\\\",42]\"\n```\n\n## Encoding format\n\nBy default, `encode` writes value into a `java.io.ByteArrayInputStream`. This can be changed with a `:return` option, accepting the following values:\n\n| value            | description\n|------------------|----------------------------------------------------------------------------------\n| `:input-stream`  | encodes into `java.io.ByteArrayInputStream` (default)\n| `:bytes`         | encodes into `byte[]`. Faster than Stream, enables NIO for servers supporting it\n| `:output-stream` | encodes lazily into `java.io.OutputStream` via a callback function\n\nAll return types satisfy the following Protocols \u0026 Interfaces:\n\n* `ring.protocols.StreamableResponseBody`, Ring 1.6.0+ will stream these for you\n* `clojure.io.IOFactory`, so you can slurp the response\n\n### `:input-stream`\n\n```clj\n(def m (m/create (assoc m/default-options :return :input-stream)))\n\n(-\u003e\u003e {:kikka 42}\n     (m/encode m \"application/json\"))\n; #object[java.io.ByteArrayInputStream]\n```\n\n### `:bytes`\n\n```clj\n(def m (m/create (assoc m/default-options :return :bytes)))\n\n(-\u003e\u003e {:kikka 42}\n     (m/encode m \"application/json\"))\n; #object[\"[B\" 0x31f5d734 \"[B@31f5d734\"]\n```\n\n### `:output-stream`\n\n```clj\n(def m (m/create (assoc m/default-options :return :output-stream)))\n\n(-\u003e\u003e {:kikka 42}\n     (m/encode m \"application/json\"))\n; \u003c\u003cStreamableResponse\u003e\u003e\n```\n\n### Format-based return\n\n```clj\n(def m (m/create (assoc-in m/default-options [:formats \"application/edn\" :return] :output-stream)))\n\n(-\u003e\u003e {:kikka 42}\n     (m/encode m \"application/json\"))\n; #object[java.io.ByteArrayInputStream]\n\n(-\u003e\u003e {:kikka 42}\n     (m/encode m \"application/edn\"))\n; \u003c\u003cStreamableResponse\u003e\u003e\n```\n\n## HTTP format negotiation\n\nHTTP format negotiation is done using request headers for both request (`content-type`, including the charset) and response (`accept` and `accept-charset`). With the default options, a full match on the content-type is required, e.g. `application/json`. Adding a `:matches` regexp for formats enables more loose matching. See [Configuration docs](doc/Configuration.md#loose-matching-on-content-type) for more info.\n\nResults of the negotiation are published into request \u0026 response under namespaced keys for introspection. These keys can also be set manually, overriding the content negotiation process.\n\n## Exceptions\n\nWhen something bad happens, an typed exception is thrown. You should handle it elsewhere. Thrown exceptions have an `ex-data` with the following `:type` value (plus extra info to enable generating descriptive erros to clients):\n\n* `:muuntaja/decode`, input can't be decoded with the negotiated `format` \u0026 `charset`.\n* `:muuntaja/request-charset-negotiation`, request charset is illegal.\n* `:muuntaja/response-charset-negotiation`, could not negotiate a charset for the response.\n* `:muuntaja/response-format-negotiation`, could not negotiate a format for the response.\n\n## Server Spec\n\n### Request\n\n* `:muuntaja/request`, client-negotiated request format and charset as `FormatAndCharset` record. Will\nbe used in the request pipeline.\n* `:muuntaja/response`, client-negotiated response format and charset as `FormatAndCharset` record. Will\nbe used in the response pipeline.\n* `:body-params` contains the decoded body.\n\n### Response\n\n* `:muuntaja/encode`, if set to truthy value, the response body will be encoded regardles of the type (primitives!)\n* `:muuntaja/content-type`, handlers can use this to override the negotiated content-type for response encoding, e.g. setting it to `application/edn` will cause the response to be formatted in JSON.\n\n## Options\n\n### Default options\n\n```clj\n{:http {:extract-content-type extract-content-type-ring\n        :extract-accept-charset extract-accept-charset-ring\n        :extract-accept extract-accept-ring\n        :decode-request-body? (constantly true)\n        :encode-response-body? encode-collections}\n\n :allow-empty-input? true\n :return :input-stream\n\n :default-charset \"utf-8\"\n :charsets available-charsets\n\n :default-format \"application/json\"\n :formats {\"application/json\" json-format/json-format\n           \"application/edn\" edn-format/edn-format\n           \"application/transit+json\" transit-format/transit-json-format\n           \"application/transit+msgpack\" transit-format/transit-msgpack-format}}\n```\n\n## Profiling\n\n\u003cimg src=\"https://raw.githubusercontent.com/wiki/metosin/muuntaja/yklogo.png\"/\u003e\n\nYourKit supports open source projects with its full-featured Java Profiler. YourKit, LLC is the creator of \u003ca href=\"https://www.yourkit.com/java/profiler/index.jsp\"\u003eYourKit Java Profiler\u003c/a\u003e and \u003ca href=\"https://www.yourkit.com/.net/profiler/index.jsp\"\u003eYourKit .NET Profiler\u003c/a\u003e, innovative and intelligent tools for profiling Java and .NET applications.\n\n## License\n\n### [Picture](https://commons.wikimedia.org/wiki/File:Oudin_coil_Turpain.png)\n\nBy Unknown. The drawing is signed \"E. Ducretet\", indicating that the apparatus was made by Eugene Ducretet, a prominent Paris scientific instrument manufacturer and radio researcher. The drawing was undoubtedly originally from the Ducretet instrument catalog. [Public domain], via Wikimedia Commons.\n\n### Original Code (ring-middleware-format)\n\nCopyright \u0026copy; 2011, 2012, 2013, 2014 Nils Grunwald\u003cbr\u003e\nCopyright \u0026copy; 2015, 2016 Juho Teperi\n\n### This library\n\nCopyright \u0026copy; 2016-2020 [Metosin Oy](http://www.metosin.fi)\n\nDistributed under the Eclipse Public License 2.0.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmetosin%2Fmuuntaja","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmetosin%2Fmuuntaja","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmetosin%2Fmuuntaja/lists"}