{"id":22478731,"url":"https://github.com/superstructor/re-frame-fetch-fx","last_synced_at":"2025-12-12T01:09:48.268Z","repository":{"id":43164179,"uuid":"220718458","full_name":"superstructor/re-frame-fetch-fx","owner":"superstructor","description":"js/fetch Effect Handler for re-frame","archived":false,"fork":false,"pushed_at":"2023-08-27T10:16:28.000Z","size":145,"stargazers_count":53,"open_issues_count":1,"forks_count":9,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-06-09T11:50:56.623Z","etag":null,"topics":["fetch","fetch-api","http-requests","re-frame","re-frame-effects"],"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/superstructor.png","metadata":{"files":{"readme":"README.adoc","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null},"funding":{"github":"superstructor"}},"created_at":"2019-11-09T23:49:08.000Z","updated_at":"2025-06-03T19:34:38.000Z","dependencies_parsed_at":"2022-09-12T12:12:44.515Z","dependency_job_id":null,"html_url":"https://github.com/superstructor/re-frame-fetch-fx","commit_stats":null,"previous_names":["day8/re-frame-fetch-fx"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/superstructor/re-frame-fetch-fx","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/superstructor%2Fre-frame-fetch-fx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/superstructor%2Fre-frame-fetch-fx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/superstructor%2Fre-frame-fetch-fx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/superstructor%2Fre-frame-fetch-fx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/superstructor","download_url":"https://codeload.github.com/superstructor/re-frame-fetch-fx/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/superstructor%2Fre-frame-fetch-fx/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261937041,"owners_count":23232846,"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":["fetch","fetch-api","http-requests","re-frame","re-frame-effects"],"created_at":"2024-12-06T15:01:45.107Z","updated_at":"2025-12-12T01:09:43.242Z","avatar_url":"https://github.com/superstructor.png","language":"Clojure","readme":":source-highlighter: coderay\n:source-language: clojure\n:toc:\n:toc-placement: preamble\n:sectlinks:\n:sectanchors:\n:toc:\n:icons: font\n\nimage:https://img.shields.io/clojars/v/superstructor/re-frame-fetch-fx?style=for-the-badge\u0026logo=clojure\u0026logoColor=fff[\"Clojars Project\", link=\"https://clojars.org/superstructor/re-frame-fetch-fx\"]\nimage:https://img.shields.io/github/issues-raw/superstructor/re-frame-fetch-fx?style=for-the-badge\u0026logo=github[\"GitHub issues\", link=\"https://github.com/superstructor/re-frame-fetch-fx/issues\"]\nimage:https://img.shields.io/github/license/superstructor/re-frame-fetch-fx?style=for-the-badge[\"License\", link=\"https://github.com/superstructor/re-frame-fetch-fx/blob/master/LICENSE\"]\n\n== js/Fetch Effect Handler for re-frame\n\nThis re-frame library contains an\nhttps://github.com/superstructor/re-frame/blob/develop/docs/Effects.md[Effect Handler]\nfor fetching resources.\n\nKeyed `:fetch`, it wraps browsers' native\nhttps://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch[`js/fetch` API].\n\n== Quick Start\n\n=== Step 1. Add Dependency\n\nAdd the following project dependency:\nimage:https://img.shields.io/clojars/v/superstructor/re-frame-fetch-fx?style=for-the-badge\u0026logo=clojure\u0026logoColor=fff[\"Clojars Project\", link=\"https://clojars.org/superstructor/re-frame-fetch-fx\"]\n\nRequires re-frame \u003e= 0.8.0.\n\n=== Step 2. Registration and Use\n\nIn the namespace where you register your event handlers, prehaps called\n`events.cljs`, you have two things to do.\n\n*First*, add this require to the `ns`:\n\n```\n(ns app.events\n  (:require\n   ...\n   [superstructor.re-frame.fetch-fx]\n   ...))\n```\n\nBecause we never subsequently use this require, it appears redundant. But its\nexistence will cause the `:fetch` effect handler to self-register with re-frame,\nwhich is important to everything that follows.\n\n*Second*, write an event handler which uses this effect:\n\n```\n(reg-event-fx\n  :handler-with-fetch\n  (fn [{:keys [db]} _]\n    {:fetch {:method                 :get\n             :url                    \"https://api.github.com/orgs/day8\"\n             :mode                   :cors\n             :timeout                5000\n             :response-content-types {#\"application/.*json\" :json}\n             :on-success             [:good-fetch-result]\n             :on-failure             [:bad-fetch-result]}}))\n```\n\n== Request Content Type\n\nWith the exception of JSON there is no special handling of the `:body` value or\nthe request's `Content-Type` header. So for anything except JSON you *must*\nhandle that yourself.\n\nFor convenience for JSON requests `:request-content-type :json` is supported\nwhich will:\n\n* set the `Content-Type` header of the request to `application/json`\n* evaluate `clj-\u003ejs` on the `:body` then `js/JSON.stringify` it.\n\n== Response Content Types\n\n`:response-content-type` is a mapping of pattern or string to a keyword\nrepresenting one of the following processing models in Table 1.\n\nThe pattern or string will be matched against the response\nhttps://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type[`Content-Type` header]\nthen the associated keyword is used to determine the processing model and result\ntype.\n\nIn the absence of a response `Content-Type` header the value that is matched\nagainst will default to `text/plain`.\n\nIn the absence of a match the processing model will default to `:text`.\n\n.Response Content Types\n[options=\"header,footer\"]\n|========================\n| Keyword | Processing | Result Type\n| `:json` | https://developer.mozilla.org/en-US/docs/Web/API/Body/json[`json()`] then `js-\u003eclj :keywordize-keys true`| ClojureScript\n| `:text` | https://developer.mozilla.org/en-US/docs/Web/API/Body/text[`text()`] | String\n| `:form-data` | https://developer.mozilla.org/en-US/docs/Web/API/FormData[`formData()`] | https://developer.mozilla.org/en-US/docs/Web/API/FormData[`js/FormData`]\n| `:blob` | https://developer.mozilla.org/en-US/docs/Web/API/Body/blob[`blob()`] | https://developer.mozilla.org/en-US/docs/Web/API/Blob[`js/Blob`]\n| `:array-buffer` | https://developer.mozilla.org/en-US/docs/Web/API/Body/arrayBuffer[`arrayBuffer()`] | https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer[`js/ArrayBuffer`]\n|========================\n\n== Comprehensive Example\n\nAll possible values of a `:fetch` map.\n\n```\n(reg-event-fx\n  :handler-with-fetch\n  (fn [{:keys [db]} _]\n    {:fetch {;; Required. Can be one of:\n             ;; :get | :head | :post | :put | :delete | :options | :patch\n             :method                 :get\n\n             ;; Required.\n             :url                    \"https://api.github.com/orgs/day8\"\n\n             ;; Optional. Can be one of:\n             ;; ClojureScript Collection | String | js/FormData | js/Blob | js/ArrayBuffer | js/BufferSource | js/ReadableStream\n             :body                   \"a string\"\n\n             ;; Optional. Only valid with ClojureScript Collection as :body.\n             :request-content-type   :json\n\n             ;; Optional. Map of URL query params\n             :params                 {:user     \"Fred\"\n                                      :customer \"big one\"}\n\n             ;; Optional. Map of HTTP headers.\n             :headers                {\"Authorization\"  \"Bearer QWxhZGRpbjpvcGVuIHNlc2FtZQ==\"\n                                      \"Accept\"         \"application/json\"}\n\n             ;; Optional. Defaults to :same-origin. Can be one of:\n             ;; :cors | :no-cors | :same-origin | :navigate\n             ;; See https://developer.mozilla.org/en-US/docs/Web/API/Request/mode\n             :mode                   :cors\n\n             ;; Optional. Defaults to :include. Can be one of:\n             ;; :omit | :same-origin | :include\n             ;; See https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials\n             :credentials            :omit\n\n             ;; Optional. Defaults to :follow. Can be one of:\n             ;; :follow | :error | :manual\n             ;; See https://developer.mozilla.org/en-US/docs/Web/API/Request/redirect\n             :redirect               :follow\n\n             ;; Optional. Can be one of:\n             ;; :default | :no-store | :reload | :no-cache | :force-cache | :only-if-cached\n             ;; See https://developer.mozilla.org/en-US/docs/Web/API/Request/cache\n             :cache                  :default\n\n             ;; Optional. Can be one of:\n             ;; :no-referrer | :client\n             ;; See https://developer.mozilla.org/en-US/docs/Web/API/Request/referrer\n             :referrer               :client\n\n             ;; See https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity\n             :integrity              \"sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=\"\n\n             :timeout                5000\n\n             :response-content-types {#\"application/.*json\"      :json\n                                      \"text/plain\"               :text\n                                      \"multipart/form-data\"      :form-data\n                                      #\"image/.*\"                :blob\n                                      \"application/octet-stream\" :array-buffer}\n\n             ;; Optional. If you want to associate multiple requests with a single\n             ;; AbortSignal you can pass it as value for the :abort-signal and use your own\n             ;; (external) AbortController to handle aborts.\n             :abort-signal            (.-signal (js/AbortController)\n\n             ;; Use :request-id with ::abort effect to abort the request\n             ;; Note: when using :abort-signal you cannot abort the request using :request-id\n             :request-id             :my-custom-request-id\n             ;; or auto-generated\n             :on-request-id          [:fetch-request-id]\n\n             :on-success             [:good-fetch-result]\n\n             :on-failure             [:bad-fetch-result]}}))\n```\n\n== Aborting Requests\nThere are two different ways you can abort requests:\n\n * Abort a (single) request by passing its **request-id** to the `::abort` effect:\n```\n(reg-event-fx\n  :abort-request\n  (fn [_ [request-id]]\n    {::abort {:request-id request-id}}))\n```\n**Note:** Reusing the same request-id for multiple different requests **will not work**.\n          The `::abort` effect would only abort the last of these requests.\n\n* Abort multiple requests by using an external **AbortController**. Pass the AbortController's  **AbortSignal** instance\nas value for the `:abort-signal` inside the `::fetch` effect map.\n\n**Note**: When you decide to use an external AbortController by passing its `:abort-signal`\n          in the `::fetch` map, you **cannot** abort this request via the `::abort` effect anymore.\n\n== Success Handling\n\n`:on-success` is dispatched with a response map like:\n\n```\n{:url         \"http://localhost...\"\n :ok?         true\n :redirected? false\n :status      200\n :status-text \"OK\"\n :type        \"cors\"\n :final-uri?  nil\n :body        \"Hello World!\"\n :headers     {:cache-control \"private, max-age=0, no-cache\" ...}}\n```\n\nNote the type of `:body` changes drastically depending on both the provided\n`:response-content-types` map *and* the response's `Content-Type` header.\n\n== Failure Handling\n\n=== Problems with no Response\n\nUnfortunately for cases where there is no server response the `js/fetch` API\nprovides terribly little information that can be captured programatically. If\n`:on-failure` is dispatched with a response like:\n```\n{:problem         :fetch\n :problem-message \"Failed to fetch\"}\n```\n\nThen it may be caused by any of the following or something else not included here:\n\n* `:url` syntax error\n* unresolvable hostname in `:url`\n* no network connection\n* Content Security Policy\n* Cross-Origin Resource Sharing (CORS) Policy or lacking `:mode :cors`\n\nLook in the Chrome Developer Tools console. There is usually a useful error\nmessage indicating the problem but so far I have not found out how to capture it\nto provide more fine grained `:problem` keywords.\n\n=== Problem due to Timeout\n\nIf `:timeout` is exceeded, `:on-failure` will be dispatched with a response like:\n\n```\n{:problem         :timeout\n :problem-message \"Fetch timed out\"}\n```\n\n=== Problems Reading the Response Body\n\nIf there is a problem reading the body after the server has responded, such as\na JSON syntax error, `:on-failure` will be dispatched with a response like:\n```\n{:problem         :body\n :reader          :json\n :problem-message \"Unexpected token \u003c in JSON at position 0\"\n ... rest of normal response map excluding :body ... }\n```\n\n=== Problems with the Server\n\nIf the server responds with an unsuccessful HTTP status code, such as 500 or 404,\n`:on-failure` will be dispatched with a response like:\n```\n{:problem :server\n ... rest of normal response map ... }\n```\n\n== Differences to `:http-xhrio`\n\n=== `:uri` Renamed to `:url`\n\nPreviously with `:http-xhrio` it was keyed `:uri`.\n\nNow with `:fetch` we follow the\nhttps://fetch.spec.whatwg.org/[Fetch Standard] nomenclature so it is keyed\n`:url`.\n\n=== `:params` != `:body`\n\nPreviously with `:http-xhrio` URL parameters and the request body were both\nkeyed as `:params`. Which one it was depended on the `:method` (i.e. GET would\nresult in URL parameters whereas POST would result in a request body).\n\nNow with `:fetch` there are two keys.\n\n`:params` is *only* URL parameters. It will always be added to the URL regardless\nof `:method`.\n\n`:body` is the request body. In practice it is only supported for `:put`, `:post`\nand `:patch` methods. Theoretically HTTP request bodies are allowed for all\nmethods except `:trace`, but just don't as there be dragons.\n\n=== No `:request-format` or `:response-format`\n\nThis has completely changed in every way including the keys used, how to specify\nthe handling of the response body and the types of values used for the response\nbody. See \u003c\u003cRequest Content Type\u003e\u003e and \u003c\u003cResponse Content Types\u003e\u003e.\n\n=== Cross-Origin Resource Sharing (CORS)\n\nPreviously with `:http-xhrio`\nhttps://developer.mozilla.org/en-US/docs/Web/HTTP/CORS[CORS] requests would\n'just work'.\n\nNow with `:fetch` `:mode :cors` *must* be set explicitly as the default mode for\n`js/fetch` is `:same-origin` which blocks CORS requests.\n\n\n== License\n\nCopyright \u0026copy; 2019 Isaac Johnston.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject 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,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n","funding_links":["https://github.com/sponsors/superstructor"],"categories":["Clojure"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsuperstructor%2Fre-frame-fetch-fx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsuperstructor%2Fre-frame-fetch-fx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsuperstructor%2Fre-frame-fetch-fx/lists"}