{"id":14986552,"url":"https://github.com/oliyh/martian","last_synced_at":"2025-05-14T05:10:46.132Z","repository":{"id":37588115,"uuid":"56529818","full_name":"oliyh/martian","owner":"oliyh","description":"The HTTP abstraction library for Clojure/script, supporting OpenAPI, Swagger, Schema, re-frame and more","archived":false,"fork":false,"pushed_at":"2025-05-07T23:46:25.000Z","size":22900,"stargazers_count":552,"open_issues_count":28,"forks_count":45,"subscribers_count":16,"default_branch":"master","last_synced_at":"2025-05-08T00:29:15.176Z","etag":null,"topics":["cljdoc-badge","clojure","clojurescript","http","http-client","interceptor","interceptors","martian","open-api","openapi","schema","swagger","swagger-api","swagger-spec"],"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/oliyh.png","metadata":{"files":{"readme":"README.md","changelog":null,"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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":["oliyh"]}},"created_at":"2016-04-18T17:59:39.000Z","updated_at":"2025-05-07T23:46:30.000Z","dependencies_parsed_at":"2022-09-02T11:01:29.099Z","dependency_job_id":"78b93be9-4a5e-4980-90ce-2c24f2474dcf","html_url":"https://github.com/oliyh/martian","commit_stats":{"total_commits":378,"total_committers":31,"mean_commits":"12.193548387096774","dds":"0.14550264550264547","last_synced_commit":"aa62c183ff65227db165ec4c28863a4e83bcc144"},"previous_names":[],"tags_count":29,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oliyh%2Fmartian","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oliyh%2Fmartian/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oliyh%2Fmartian/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oliyh%2Fmartian/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/oliyh","download_url":"https://codeload.github.com/oliyh/martian/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253823489,"owners_count":21969851,"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":["cljdoc-badge","clojure","clojurescript","http","http-client","interceptor","interceptors","martian","open-api","openapi","schema","swagger","swagger-api","swagger-spec"],"created_at":"2024-09-24T14:13:07.860Z","updated_at":"2025-05-14T05:10:46.112Z","avatar_url":"https://github.com/oliyh.png","language":"Clojure","funding_links":["https://github.com/sponsors/oliyh"],"categories":[],"sub_categories":[],"readme":"# Martian\n\nCalling HTTP endpoints can be complicated. You have to construct the right URL with the right route parameters, remember\nwhat the query parameters are, what method to use, how to encode the body and many other things that leak into your codebase.\n\n**Martian** takes a description of these details (either from your [OpenAPI/Swagger](http://swagger.io/) server,\nor just as [lovely Clojure data](#no-swagger-no-problem)) and provides a client interface to the API that abstracts you away from HTTP and lets you\nsimply call operations with parameters, keeping your codebase clean.\n\nYou can bootstrap it in one line and start calling the server:\n```clojure\n(require '[martian.core :as martian]\n         '[martian.clj-http :as martian-http])\n\n(let [m (martian-http/bootstrap-openapi \"https://pedestal-api.herokuapp.com/swagger.json\")]\n  (martian/response-for m :create-pet {:name \"Doggy McDogFace\" :type \"Dog\" :age 3})\n  ;; =\u003e {:status 201 :body {:id 123}}\n\n  (martian/response-for m :get-pet {:id 123}))\n  ;; =\u003e {:status 200 :body {:name \"Doggy McDogFace\" :type \"Dog\" :age 3}}\n```\n\nImplementations for many popular HTTP client libraries are supplied as modules (see below),\nbut any other HTTP library can be used due to the extensibility of Martian's interceptor chain.\nIt also allows custom behaviour to be injected in a uniform and powerful way.\n\nThe `martian-test` library allows you to assert that your code constructs valid requests to remote servers without ever\nactually calling them, using the OpenApi spec to validate the parameters. It can also generate responses in the same way,\nensuring that your response handling code is also correct. Examples are below.\n\n`martian-re-frame` integrates martian event handlers into `re-frame`, simplifying connecting your UI to data sources.\n\n## Latest versions \u0026 API docs\n\nThe core library (required):\n\n[![Clojars Project](https://img.shields.io/clojars/v/com.github.oliyh/martian.svg)](https://clojars.org/com.github.oliyh/martian) [![cljdoc badge](https://cljdoc.org/badge/com.github.oliyh/martian)](https://cljdoc.org/d/com.github.oliyh/martian/CURRENT)\n\nSupport for various HTTP client libraries:\n|HTTP client|JVM|Javascript|Babashka|Docs|\n| --------- | --- | ------ | ------ | --- |\n|[![Clojars Project](https://img.shields.io/clojars/v/com.github.oliyh/martian-hato.svg)](https://clojars.org/com.github.oliyh/martian-hato)|✔|||[![cljdoc badge](https://cljdoc.org/badge/com.github.oliyh/martian-hato)](https://cljdoc.org/d/com.github.oliyh/martian-hato/CURRENT)|\n|[![Clojars Project](https://img.shields.io/clojars/v/com.github.oliyh/martian-clj-http.svg)](https://clojars.org/com.github.oliyh/martian-clj-http)|✔|||[![cljdoc badge](https://cljdoc.org/badge/com.github.oliyh/martian-clj-http)](https://cljdoc.org/d/com.github.oliyh/martian-clj-http/CURRENT)|\n|[![Clojars Project](https://img.shields.io/clojars/v/com.github.oliyh/martian-clj-http-lite.svg)](https://clojars.org/com.github.oliyh/martian-clj-http-lite)|✔||✔|[![cljdoc badge](https://cljdoc.org/badge/com.github.oliyh/martian-clj-http-lite)](https://cljdoc.org/d/com.github.oliyh/martian-clj-http-lite/CURRENT)|\n|[![Clojars Project](https://img.shields.io/clojars/v/com.github.oliyh/martian-httpkit.svg)](https://clojars.org/com.github.oliyh/martian-httpkit)|✔||✔|[![cljdoc badge](https://cljdoc.org/badge/com.github.oliyh/martian-httpkit)](https://cljdoc.org/d/com.github.oliyh/martian-httpkit/CURRENT)|\n|[![Clojars Project](https://img.shields.io/clojars/v/com.github.oliyh/martian-cljs-http.svg)](https://clojars.org/com.github.oliyh/martian-cljs-http)||✔||[![cljdoc badge](https://cljdoc.org/badge/com.github.oliyh/martian-cljs-http)](https://cljdoc.org/d/com.github.oliyh/martian-cljs-http/CURRENT)|\n|[![Clojars Project](https://img.shields.io/clojars/v/com.github.oliyh/martian-cljs-http-promise.svg)](https://clojars.org/com.github.oliyh/martian-cljs-http-promise)||✔||[![cljdoc badge](https://cljdoc.org/badge/com.github.oliyh/martian-cljs-http-promise)](https://cljdoc.org/d/com.github.oliyh/martian-cljs-http-promise/CURRENT)|\n|[![Clojars Project](https://img.shields.io/clojars/v/com.github.oliyh/martian-babashka-http-client.svg)](https://clojars.org/com.github.oliyh/martian-babashka-http-client)|✔||✔|[![cljdoc badge](https://cljdoc.org/badge/com.github.oliyh/martian-babashka-http-client)](https://cljdoc.org/d/com.github.oliyh/martian-babashka-http-client/CURRENT)|\n\nTesting and other interop libraries:\n\n[![Clojars Project](https://img.shields.io/clojars/v/com.github.oliyh/martian-test.svg)](https://clojars.org/com.github.oliyh/martian-test) [README](https://github.com/oliyh/martian/tree/master/test)\n\n[![Clojars Project](https://img.shields.io/clojars/v/com.github.oliyh/martian-vcr.svg)](https://clojars.org/com.github.oliyh/martian-vcr) [![cljdoc badge](https://cljdoc.org/badge/com.github.oliyh/martian-vcr)](https://cljdoc.org/d/com.github.oliyh/martian-vcr/CURRENT) [README](https://github.com/oliyh/martian/tree/master/vcr)\n\n[![Clojars Project](https://img.shields.io/clojars/v/com.github.oliyh/martian-re-frame.svg)](https://clojars.org/com.github.oliyh/martian-re-frame) [![cljdoc badge](https://cljdoc.org/badge/com.github.oliyh/martian-re-frame)](https://cljdoc.org/d/com.github.oliyh/martian-re-frame/CURRENT) [README](https://github.com/oliyh/martian/tree/master/re-frame)\n\n\n## Features\n\n- Bootstrap an instance from just a OpenAPI/Swagger url, a local definition file or provide your own API mapping\n- Modular with support for many HTTP client libraries (see table above)\n- Build urls and request maps from code or generate and perform the request, returning the response\n- Validate requests and responses to ensure they are correct before the data leaves/enters your system\n- Explore an API from your REPL\n- Extensible via interceptor pattern - inject your own interceptors anywhere in the chain\n- Negotiates the most efficient content-type and handles serialisation and deserialisation including `transit`, `edn` and `json`\n- Easy to add support for any other content-type\n- Support for integration testing without requiring external HTTP stubs\n- Routes are named as idiomatic kebab-case keywords of the `operationId` of the endpoint in the OpenAPI/Swagger definition\n- Parameters are aliased to kebab-case keywords so that your code remains idiomatic, neat and clean\n- Parameter defaults can be optionally applied\n- Simple, data driven behaviour with low coupling using libraries and patterns you already know\n- Pure client code, no server code or modifications required\n- Write generative, realistic tests using [martian-test](https://github.com/oliyh/martian/tree/master/test) to generate response data\n- Record and play back HTTP calls using [martian-vcr](https://github.com/oliyh/martian/tree/master/vcr)\n\nFor more details and rationale you can [watch the talk given to London Clojurians](https://www.youtube.com/watch?v=smzc8XlvlSQ) or there is also an older [talk given at ClojureX Bytes](https://skillsmatter.com/skillscasts/8843-clojure-bytes#video).\n\n## Clojure / ClojureScript\n\nGiven an [OpenAPI/Swagger API definition](https://pedestal-api.herokuapp.com/swagger.json)\nlike that provided by [pedestal-api](https://github.com/oliyh/pedestal-api):\n\n```clojure\n(require '[martian.core :as martian]\n         '[martian.clj-http :as martian-http])\n\n;; bootstrap the Martian instance by simply providing the url serving the openapi/swagger description\n(let [m (martian-http/bootstrap-openapi \"https://pedestal-api.herokuapp.com/swagger.json\")]\n\n  ;; explore the endpoints\n  (martian/explore m)\n  ;; =\u003e [[:get-pet \"Loads a pet by id\"]\n  ;;     [:create-pet \"Creates a pet\"]]\n\n  ;; explore the :get-pet endpoint\n  (martian/explore m :get-pet)\n  ;; =\u003e {:summary \"Loads a pet by id\"\n  ;;     :parameters {:id s/Int}}\n\n  ;; build the url for a request\n  (martian/url-for m :get-pet {:id 123})\n  ;; =\u003e https://pedestal-api.herokuapp.com/pets/123\n\n  ;; build the request map for a request\n  (martian/request-for m :get-pet {:id 123})\n  ;; =\u003e {:method :get\n  ;;     :url \"https://pedestal-api.herokuapp.com/pets/123\"\n  ;;     :headers {\"Accept\" \"application/transit+msgpack\"\n  ;;     :as :byte-array}\n\n  ;; perform the request to create a pet and read back the pet-id from the response\n  (let [pet-id (-\u003e (martian/response-for m :create-pet {:name \"Doggy McDogFace\" :type \"Dog\" :age 3})\n                   (get-in [:body :id]))]\n\n    ;; load the pet using the id\n    (martian/response-for m :get-pet {:id pet-id}))\n\n    ;; =\u003e {:status 200\n    ;;     :body {:name \"Doggy McDogFace\"\n    ;;            :type \"Dog\"\n    ;;            :age 3}}\n\n  ;; :martian.core/body can optionally be used in lieu of explicitly naming the body schema\n  (let [pet-id (-\u003e (martian/response-for m :create-pet {::martian/body {:name \"Doggy McDogFace\" :type \"Dog\" :age 3}})\n                   (get-in [:body :id]))])\n\n  ;; the name of the body object can also be used to nest the body parameters\n  (let [pet-id (-\u003e (martian/response-for m :create-pet {:pet {:name \"Doggy McDogFace\" :type \"Dog\" :age 3}})\n                   (get-in [:body :id]))]))\n```\n\nNote that when calling `bootstrap-openapi` you can also provide a url to a local resource, e.g. `(martian-http/bootstrap-openapi \"public/openapi.json\")`.\nFor ClojureScript the file can only be read at compile time, so a slightly different form is required using the `martian.file/load-local-resource` macro:\n```clj\n(martian/bootstrap-openapi \"https://sandbox.example.com\" (load-local-resource \"openapi-test.json\") martian-http/default-opts)\n```\n\n## No Swagger, no problem\n\nAlthough bootstrapping against a remote OpenAPI or Swagger API using `bootstrap-openapi` is simplest\nand allows you to use the golden source to define the API, you may likely find yourself\nneeding to integrate with an API beyond your control which does not use OpenAPI or Swagger.\n\nMartian offers a separate `bootstrap` function which you can provide with handlers defined as data.\nHere's an example:\n\n```clojure\n(martian/bootstrap \"https://api.org\"\n                   [{:route-name :load-pet\n                     :path-parts [\"/pets/\" :id]\n                     :method :get\n                     :path-schema {:id s/Int}}\n\n                    {:route-name :create-pet\n                     :produces [\"application/xml\"]\n                     :consumes [\"application/xml\"]\n                     :path-parts [\"/pets/\"]\n                     :method :post\n                     :body-schema {:pet {:id   s/Int\n                                         :name s/Str}}}])\n\n```\n\n## Testing with martian-test\n\nTesting code that calls external systems can be tricky - you either build often elaborate stubs which start\nto become as complex as the system you are calling, or else you ignore it all together with `(constantly true)`.\n\nMartian will assert that you provide the right parameters to the call, and `martian-test` will return a response\ngenerated from the response schema of the remote application. This gives you more confidence that your integration is\ncorrect without maintenance of a stub.\n\nThe following example shows how exceptions will be thrown by bad code and how responses can be generated:\n```clojure\n(require '[martian.core :as martian]\n         '[martian.httpkit :as martian-http]\n         '[martian.test :as martian-test])\n\n(let [m (-\u003e (martian-http/bootstrap-openapi \"https://pedestal-api.herokuapp.com/swagger.json\")\n            (martian-test/respond-with-generated {:get-pet :random}))]\n\n  (martian/response-for m :get-pet {})\n  ;; =\u003e ExceptionInfo Value cannot be coerced to match schema: {:id missing-required-key}\n\n  (martian/response-for m :get-pet {:id \"bad-id\"})\n  ;; =\u003e ExceptionInfo Value cannot be coerced to match schema: {:id (not (integer? bad-id))}\n\n  (martian/response-for m :get-pet {:id 123}))\n  ;; =\u003e {:status 200, :body {:id -3, :name \"EcLR\"}}\n\n```\n`martian-test` has interceptors that always give successful responses, always errors, or a random choice.\nBy making your application code accept a Martian instance you can inject a test instance within your tests, making\npreviously untestable code testable again.\n\nMore documentation is available at [martian-test](https://github.com/oliyh/martian/tree/master/test).\n\n## Recording and playback with martian-vcr\n\nmartian-vcr allows you to record responses from real HTTP requests and play them back later, allowing you to build realistic test\ndata quickly and easily.\n\n```clj\n(require '[martian.vcr :as vcr])\n\n(def m (http/bootstrap \"https://foo.com/api\"\n                       {:interceptors (inject http/default-interceptors\n                                              (vcr/record opts)\n                                              :after http/perform-request)}))\n\n(m/response-for m :load-pet {:id 123})\n;; the response is recorded and now stored at test-resources/vcr/load-pet/-655390368/0.edn\n```\n\nMore documentation is available at [martian-vcr](https://github.com/oliyh/martian/tree/master/vcr).\n\n## Idiomatic parameters\n\nIf an API has a parameter called `FooBar` it's difficult to stop that leaking into your own code - the Clojure idiom is to\nuse kebab-cased keywords such as `:foo-bar`. Martian maps parameters to their kebab-cased equivalents so that your code looks neater\nbut preserves the mapping so that the API is passed the correct parameter names:\n\n```clojure\n(let [m (martian/bootstrap \"https://api.org\"\n                           [{:route-name  :create-pet\n                             :path-parts  [\"/pets/\"]\n                             :method      :post\n                             :body-schema {:pet {:PetId     s/Int\n                                                 :FirstName s/Str\n                                                 :LastName  s/Str}}}])]\n\n  (martian/request-for m :create-pet {:pet-id 1 :first-name \"Doggy\" :last-name \"McDogFace\"}))\n\n;; =\u003e {:method :post\n;;     :url    \"https://api.org/pets/\"\n;;     :body   {:PetId     1\n;;              :FirstName \"Doggy\"\n;;              :LastName  \"McDogFace\"}}\n```\n\nBody parameters may be supplied in three ways: with an alias, destructured or as an explicit value.\n\n```clojure\n;; the following three forms are equivalent\n(request-for m :create-pet {:pet {:pet-id 1 :first-name \"Doggy\" :last-name \"McDogFace\"}})           ;; the :pet alias\n(request-for m :create-pet {:pet-id 1 :first-name \"Doggy\" :last-name \"McDogFace\"})                  ;; destructured\n(request-for m :create-pet {::martian/body {:pet-id 1 :first-name \"Doggy\" :last-name \"McDogFace\"}}) ;; explicit body value\n\n```\n\n## Custom behaviour\n\nYou may wish to provide additional behaviour to requests. This can be done by providing Martian with interceptors\nwhich behave in the same way as pedestal interceptors.\n\n### Global behaviour\n\nYou can add interceptors to the stack that get executed on every request when bootstrapping martian.\nFor example, if you wish to add an authentication header and a timer to all requests:\n\n```clojure\n(require '[martian.core :as martian]\n         '[martian.clj-http :as martian-http])\n\n(def add-authentication-header\n  {:name ::add-authentication-header\n   :enter (fn [ctx]\n            (assoc-in ctx [:request :headers \"Authorization\"] \"Token: 12456abc\"))})\n\n(def request-timer\n  {:name ::request-timer\n   :enter (fn [ctx]\n            (assoc ctx ::start-time (System/currentTimeMillis)))\n   :leave (fn [ctx]\n            (-\u003e\u003e ctx ::start-time\n                 (- (System/currentTimeMillis))\n                 (format \"Request to %s took %sms\" (get-in ctx [:handler :route-name]))\n                 (println))\n            ctx)})\n\n(let [m (martian-http/bootstrap-openapi\n               \"https://pedestal-api.herokuapp.com/swagger.json\"\n               {:interceptors (concat\n                                [add-authentication-header request-timer]\n                                martian-http/default-interceptors)})]\n\n        (martian/response-for m :all-pets {:id 123}))\n        ;; Request to :all-pets took 38ms\n        ;; =\u003e {:status 200 :body {:pets []}}\n```\n\nThere is also a way to augment/override the default coercion matcher that is used by a Martian instance for params coercion:\n\n```clojure\n;; adding an extra coercion instead/after the default one\n(martian-http/bootstrap-openapi\n  \"https://pedestal-api.herokuapp.com/swagger.json\"\n  {:coercion-matcher (fn [schema]\n                       (or (martian/default-coercion-matcher schema)\n                           (my-extra-coercion-matcher schema)))})\n\n;; switching to some coercion matcher from 'schema-tools'\n(require '[schema-tools.coerce :as stc])\n(martian/bootstrap\n  \"https://api.org\"\n  [{:route-name  :create-pet\n    :path-parts  [\"/pets/\"]\n    :method      :post\n    :body-schema {:pet {:PetId     s/Int\n                        :FirstName s/Str\n                        :LastName  s/Str}}}]\n  {:coercion-matcher stc/json-coercion-matcher})\n```\n\n### Per route behaviour\n\nSometimes individual routes require custom behaviour. This can be achieved by writing a\nglobal interceptor which inspects the route-name and decides what to do, but a more specific\noption exists using `bootstrap` and providing `:interceptors` as follows:\n\n```clojure\n(martian/bootstrap \"https://api.org\"\n                   [{:route-name :load-pet\n                     :path-parts [\"/pets/\" :id]\n                     :method :get\n                     :path-schema {:id s/Int}\n                     :interceptors [{:name ::override-load-pet-method\n                                     :enter #(assoc-in % [:request :method] :xget)}]}])\n```\n\nAlternatively you can use the helpers like `update-handler` to update a martian created from `bootstrap-openapi`:\n\n```clojure\n(-\u003e (martian/bootstrap-openapi \"https://api.org\" openapi-definition)\n    (martian/update-handler :load-pet assoc :interceptors [{:name ::override-load-pet-method\n                                                            :enter #(assoc-in % [:request :method] :xget)}]))\n```\n\nInterceptors provided at a per-route level are inserted into the interceptor chain at execution time by the interceptor called\n`:martian.interceptors/enqueue-route-specific-interceptors`. This results in the following chain:\n\n- `set-method`\n- `set-url`\n- `set-query-params`\n- `set-body-params`\n- `set-form-params`\n- `set-header-params`\n- `enqueue-route-specific-interceptors` - injects the following at runtime:\n  - `route-interceptor-1` e.g. `::override-load-pet-method`\n  - `route-interceptor-2`\n  - etc\n- `encode-body`\n- `default-coerce-response`\n- `perform-request`\n\nThis means your route interceptors have available to them the unserialised request on enter and the deserialised response on leave.\nYou may move or provide your own version of `enqueue-route-specific-interceptors` to change this behaviour.\n\n## Custom content-types\n\nMartian allows you to add support for content-types in addition to those supported out of the box - `transit`, `edn` and `json`.\n\n```clojure\n(require '[martian.core :as m])\n(require '[martian.httpkit :as http])\n(require '[martian.encoders :as encoders])\n(require '[martian.interceptors :as i])\n(require '[clojure.string :as str])\n\n(def magic-encoder str/upper-case)\n(def magic-decoder str/lower-caser)\n\n(let [encoders (assoc (encoders/default-encoders)\n                      \"application/magical\" {:encode magic-encoder\n                                             :decode magic-decoder\n                                             :as :magic})]\n  (http/bootstrap-openapi\n   \"https://example-api.com\"\n   {:interceptors (concat m/default-interceptors\n                          [(i/encode-body encoders)\n                           (i/coerce-response encoders)\n                           http/perform-request])}))\n\n```\n\n## Response validation\n\nMartian provides a response validation interceptor which validates the response against the response schemas.\nIt is not included in the default interceptor stack, but you can include it yourself:\n\n```clojure\n(http/bootstrap-openapi\n \"https://example-api.com\"\n {:interceptors (cons (i/validate-response {:strict? true})\n                      http/default-interceptors)})\n```\n\nThe `strict?` argument defines whether any response with an undefined schema is allowed, e.g. if a response\nschema is defined for a 200 status code only, but the server returns a 500, strict mode will throw an error but\nnon-strict mode will allow it. Strict mode defaults to false.\n\n## Defaults\n\nMartian can read `default` directives from Swagger, or you can supply them if bootstrapping from data. They can be seen using `explore` and merged with your params if you set the optional `use-defaults?` option.\n\n```clojure\n(require '[schema-tools.core :as st])\n(require '[martian.interceptors :refer [merge-defaults]])\n\n(let [m (martian/bootstrap \"https://api.org\"\n                           [{:route-name :create-pet\n                             :path-parts [\"/pets/\"]\n                             :method :post\n                             :body-schema {:pet {:id   s/Int\n                                                 :name (st/default s/Str \"Bryson\")}}}]\n                           {:use-defaults? true})]\n\n  (martian/explore m :create-pet)\n  ;; {:summary nil, :parameters {:pet {:id Int, :name (default Str \"Bryson\")}}, :returns {}}\n\n  (martian/request-for m :create-pet {:pet {:id 123}})\n  ;; {:method :post, :url \"https://api.org/pets/\", :body {:id 123, :name \"Bryson\"}}\n  )\n```\n\n## Development mode\n\nWhen martian is bootstrapped it closes over the route definitions and any options you provide, returning an immutable instance.\nThis can hamper REPL development when you wish to rapidly iterate on your martian definition, so all martian API calls also accept a function or a var that returns the instance instead:\n\n```clojure\n(martian/url-for (fn [] (martian/bootstrap ... )) :load-pet {:id 123}) ;; =\u003e \"https://api.com/pets/123\"\n```\n\n## Java\n\n```java\nimport martian.Martian;\nimport java.util.Map;\nimport java.util.HashMap;\n\nMap\u003cString, Object\u003e swaggerSpec = { ... };\nMartian martian = new Martian(\"https://pedestal-api.herokuapp.com\", swaggerSpec);\n\nmartian.urlFor(\"get-pet\", new HashMap\u003cString, Object\u003e {{ put(\"id\", 123); }});\n\n// =\u003e https://pedestal-api.herokuapp.com/pets/123\n```\n\n## Caveats\n- You need `:operationId` in the OpenAPI/Swagger spec to name routes when using `bootstrap-openapi`\n  - [pedestal-api](https://github.com/oliyh/pedestal-api) automatically generates these from the route name\n\n## Development\n[![Circle CI](https://circleci.com/gh/oliyh/martian.svg?style=svg)](https://circleci.com/gh/oliyh/martian)\n\nUse `cider-jack-in-clj` or `cider-jack-in-clj\u0026cljs` to start Clojure (and Clojurescript where appropriate) REPLs for development.\nYou may need to `lein install` first if you're working in a module that depends on another.\n\n## Issues and features\nPlease feel free to raise issues on Github or send pull requests.\n\n## Acknowledgements\nMartian uses [tripod](https://github.com/frankiesardo/tripod) for routing, inspired by [pedestal](https://github.com/pedestal/pedestal).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foliyh%2Fmartian","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Foliyh%2Fmartian","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foliyh%2Fmartian/lists"}