{"id":17579300,"url":"https://github.com/arichiardi/fonda","last_synced_at":"2025-12-12T01:13:40.477Z","repository":{"id":54123884,"uuid":"156791740","full_name":"arichiardi/fonda","owner":"arichiardi","description":"An async pipeline approach to functional core - imperative shell.","archived":false,"fork":false,"pushed_at":"2021-03-09T02:03:22.000Z","size":179,"stargazers_count":23,"open_issues_count":7,"forks_count":1,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-04-18T18:33:23.776Z","etag":null,"topics":["clojurescript","functional-programming","javascript"],"latest_commit_sha":null,"homepage":null,"language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/arichiardi.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}},"created_at":"2018-11-09T01:21:44.000Z","updated_at":"2024-12-10T20:12:50.000Z","dependencies_parsed_at":"2022-08-13T07:01:02.541Z","dependency_job_id":null,"html_url":"https://github.com/arichiardi/fonda","commit_stats":null,"previous_names":["elasticpath/fonda"],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arichiardi%2Ffonda","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arichiardi%2Ffonda/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arichiardi%2Ffonda/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arichiardi%2Ffonda/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/arichiardi","download_url":"https://codeload.github.com/arichiardi/fonda/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251352638,"owners_count":21575865,"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":["clojurescript","functional-programming","javascript"],"created_at":"2024-10-22T00:44:28.596Z","updated_at":"2025-12-12T01:13:35.430Z","avatar_url":"https://github.com/arichiardi.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Fonda\n\nAn async pipeline approach to functional core - imperative shell from by Gary Bernhardt's [Boundaries talk.](https://www.destroyallsoftware.com/talks/boundaries)\n\n## Minimal Viable Example\n\nThis example illustrates `fonda`'s basic mechanics:\n\n```clojure\n(ns example.simple\n  (:require [cljs-http.client :as http]\n            [cljs.core.async :as cca :include-macros true]\n            [clojure.set :as set]\n            [fonda.core :as fonda]))\n\n(defn fetch-user\n  [ctx]\n  (http/get \"http://insecure-endpoint.com\"\n            {:basic-auth (select-keys ctx [:username :password])}))\n\n(fonda/execute\n {:initial-ctx {:username js/process.USER\n                :password js/process.PASSWORD}}\n\n [{:processor  :example.simple/fetch-user                ;; can be either a function or a keyword\n   :path       [:github-response]}\n\n  {:processor  :example.simple/github-response-\u003ethings   ;; pure function - ctx in -\u003e ctx out\n   :path       [:github-things]}]\n\n ;; on-exception\n (fn [exception]\n   (handle-exception exception))\n\n ;; on-success\n (fn [ctx]\n   (handle-success (:github-things ctx))))\n```\n\n*HINT*: The parameter order makes it easy to partially apply `execute` for leaner call sites.\n\n---\n\nFonda sequentially executes a series of [steps](#trivia), one after the other, augmenting a context map. The steps can be synchronous or asyncronous. After the steps are run, the termination callbacks will be executed.\n\nIf a `js/Error`, an exception in `fonda` parlance, is thrown it will be automatically caught and the chain short circuits and the `on-exception` function is called with the `js/Error`.\n\nExceptions are things we can't and don't want to recover from, like unexpected bugs.\n\n## Anomalies\n\nAnomaly is\n\n  \u003e Deviation or departure from the normal or common order, form, or rule.\n\nThere is a stark contrast between anomalies and JavaScript `js/Error`s, Promise rejections or Java `Exception`s: anomalies are data that define a recoverable error, like receiving a 409 (conflict) http status code that you can retry.\n\nAnomalies are first class citizens in `fonda` and by default they are maps containing the [`:cognitect.anomalies/anomaly`](https://github.com/cognitect-labs/anomalies) key.\n\nIf `anomaly?` is truthy and an anomaly is returned by a step the whole pipeline short circuits and the `on-anomaly` function is called.\n\nIt is also possible to redefine what an anomaly is by passing a config predicate, `anomaly?`, so that client code can have its own representation of an anomaly as data.\n\nThe following section describes the parameters `fonda/execute` accepts.\n\n## Parameters\n\n- **config** - static configuration map\n\n    | Key | Optional? | Notes |\n    |---|---|---|\n    | `:anomaly?` | Yes | A boolean or a function that gets a map and determines if it is an anomaly. |\n    | `:initial-ctx` | Yes | The data that initializes the context. Must be a map, `{}` by default. |\n    | `:anomaly-handlers` | Yes | A map from step name keyword to function that gets called with a map `{:ctx \u003cctx\u003e :anomaly \u003canomaly\u003e}` when the step returns an anomaly. |\n    | `:exception-handlers` | Yes |  A map from step name keyword to function that gets called with a map `{:ctx \u003cctx\u003e :exception \u003cexception\u003e}` when the step triggers an exception. |\n\n- **steps** - each item must be either a `Tap` or a `Processor`, or a `Injector`\n\n  - tap\n\n    | Key | Optional? | Notes |\n    |---|---|---|\n    | `:tap` | No | A function that gets the context but doesn't augment it. If it succeeds the result is ignored. If asynchronous it will still block the pipeline and interrupt the execution whenever either an anomaly or an exception happen. |\n    | `:name` | Yes | The name of the step as string or keyword |\n\n  - processor\n\n    | Key | Optional? | Notes |\n    |---|---|---|\n    | `:processor` | No | A function that gets the context and returns data. The data is [assoced-in](https://clojuredocs.org/clojure.core/assoc-in) at the given path Can be asynchronous. If asynchronous it will still block the pipeline and interrupt the execution whenever either an anomaly or an exception happen. |\n    | `:path` | No | Path where to assoc the result of the processor |\n    | `:name` | Yes | The name of the step as string or keyword |\n\n  - injector\n\n    | Key | Optional? | Notes |\n    |---|---|---|\n    | `:injector` | No | A function that gets the context and returns either a step or a collection of steps. The step(s) returned will be executed right after the injector step and just before the next steps. Can be asynchronous.\n    | `:name` | Yes | The name of the injector step as string or keyword |\n\n\n- **on-exception**          Function called with an exception when any of the steps throws one.\n- **on-success**            Function called with the context if all steps succeed.\n- [Optional] **on-anomaly** Function called in case of anomaly with the anomaly data itself.\n\n\n## Full Example\n\n```clojure\n(ns example.full\n  ...)\n\n(defn print-remote-thing\n  [{:keys [remote-thing-response]}]\n  (println \"the remote thing response was:\" remote-thing-response))\n\n(defn get-remote-thing\n  [ctx]\n  (ajax/GET \"http://remote-thing-url.com\" {:params (:remote-thing-params ctx)}))\n\n(fonda/execute\n  {:initial-ctx {:env-var-xyz \"value\",\n                 :remote-thing-params {:p1 \"p1\" :p2 \"p2\"}\n                 :other-remote-thing-responses []}\n   :anomaly-handlers {:get-remote-thing (fn [{:keys [anomaly]}]\n                                           (post-error-to-log-server anomaly))}\n   :exception-handlers {:get-remote-thing (fn [{:keys [exception]}]\n                                              (js/console.log \"An exception retrieving the remote thing occurred:\" exception))}}\n\n  [{:processor  :example.full/get-remote-thing\n    :name       \"get-remote-thing\"\n    :path       [:remote-thing-response]}\n\n   {:tap        :example.full/print-remote-thing}\n\n   {:processor  :other.namespace/process-remote-thing-response\n    :path       [:remote-thing]}\n\n   ;; Injector returns a collection of steps to be added right after the injector step\n   {:inject         (fn [{:keys [remote-thing]}]\n                      (-\u003e\u003e (:side-effect-post-urls remote-thing)\n                           (map (fn [side-effect-post-url]\n                                  {:tap (fn [{:keys [remote-thing-params]}]\n                                          (ajax/POST side-effect-post-url remote-thing-params))}))))}]\n\n  ;; on-exception\n  (fn [exception]\n   (handle-exception exception))\n\n  ;; on-success\n  (fn [{:keys [remote-thing-processed]}]\n   (handle-success remote-thing-processed))\n\n  ;; on-anomaly\n  (fn [anomaly]\n   (handle-anomaly anomaly)))\n\n```\n\n## Thanks\n\nThe conception of the library has taken place during early research and\ndevelopment work at [Elastic Path Software\nInc.](https://www.elasticpath.com). A heart-felt thank you goes especially to\n[Matt Bishop](https://github.com/mattbishop) for supporting open source.\n\n## Trivia\n\nThe name got inspired by Jane Fonda's step very successful fitness programs.\n\n![](https://img.buzzfeed.com/buzzfeed-static/static/enhanced/webdr03/2013/8/15/10/anigif_enhanced-buzz-31474-1376578012-1.gif?downsize=700:*\u0026output-format=auto\u0026output-quality=auto)\n\nAs with the fitness program, `fonda` consist of well-curated 🚶‍♀️ steps 🚶‍♂️.\n\n## Contributing\n\nSee [CONTRIBUTING.md](./CONTRIBUTING.md).\n\n## License\n\nCopyright 2018-2019, The Fonda [Authors](./AUTHORS)\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\nhttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farichiardi%2Ffonda","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Farichiardi%2Ffonda","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farichiardi%2Ffonda/lists"}