{"id":17061103,"url":"https://github.com/lispyclouds/navi","last_synced_at":"2025-04-09T19:17:40.433Z","repository":{"id":46248266,"uuid":"345664197","full_name":"lispyclouds/navi","owner":"lispyclouds","description":"A tiny, data-driven library converting OpenAPI spec to Reitit routes enabling spec-first practices.","archived":false,"fork":false,"pushed_at":"2025-03-28T08:42:46.000Z","size":68,"stargazers_count":62,"open_issues_count":1,"forks_count":8,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-04-09T19:17:37.074Z","etag":null,"topics":["api","clojure","data-driven","openapi","rest-api","swagger"],"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/lispyclouds.png","metadata":{"files":{"readme":"README.md","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,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-03-08T13:24:13.000Z","updated_at":"2025-04-07T13:18:36.000Z","dependencies_parsed_at":"2025-02-18T10:10:47.927Z","dependency_job_id":"4408a51d-b7b5-47ae-a547-526926db97c7","html_url":"https://github.com/lispyclouds/navi","commit_stats":{"total_commits":23,"total_committers":2,"mean_commits":11.5,"dds":0.04347826086956519,"last_synced_commit":"3aa43479e0e766bcabd249a7c5377d18ef52f584"},"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lispyclouds%2Fnavi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lispyclouds%2Fnavi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lispyclouds%2Fnavi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lispyclouds%2Fnavi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lispyclouds","download_url":"https://codeload.github.com/lispyclouds/navi/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248094988,"owners_count":21046770,"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":["api","clojure","data-driven","openapi","rest-api","swagger"],"created_at":"2024-10-14T10:46:05.476Z","updated_at":"2025-04-09T19:17:40.410Z","avatar_url":"https://github.com/lispyclouds.png","language":"Clojure","readme":"# navi\n\n[![Tests](https://github.com/lispyclouds/navi/actions/workflows/ci.yaml/badge.svg)](https://github.com/lispyclouds/navi/actions/workflows/ci.yaml)\n[![Clojars Project](https://img.shields.io/clojars/v/org.clojars.lispyclouds/navi.svg)](https://clojars.org/org.clojars.lispyclouds/navi)\n\nA tiny library converting [OpenAPI](https://www.openapis.org/) route definitions to [Reitit](https://cljdoc.org/jump/release/metosin/reitit) routes.\n\nSuitable for [spec-first](https://www.atlassian.com/blog/technology/spec-first-api-development) servers.\n\n## Features\n\n- Read OpenAPI 3 definitions as JSON or YAML\n- Remote and relative [refs](https://swagger.io/docs/specification/using-ref/)\n- Request and response coercions powered by [Malli](https://github.com/metosin/malli)\n- requestBody coercion\n- Strings with uuid and pattern types\n- A large subset of OpenAPI types are currently supported, please raise an issue if something is unsupported\n\nCurrently unsupported (raise an issue if needed!):\n- Other coercion libs\n- `oneOf` composed schema (mostly can be handled by `anyOf`)\n- Some string formats:\n  - Email\n  - File\n\nAny contributions are much much welcome and appreciated!\n\n## Installation\nLeiningen/Boot\n```clojure\n[org.clojars.lispyclouds/navi \"0.1.3\"]\n```\n\nClojure CLI/deps.edn\n```clojure\n{org.clojars.lispyclouds/navi {:mvn/version \"0.1.3\"}}\n```\n\nGradle\n```groovy\ncompile 'org.clojars.lispyclouds:navi:0.1.3'\n```\n\nMaven\n```xml\n\u003cdependency\u003e\n  \u003cgroupId\u003eorg.clojars.lispyclouds\u003c/groupId\u003e\n  \u003cartifactId\u003enavi\u003c/artifactId\u003e\n  \u003cversion\u003e0.1.3\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n## Usage\n\nGiven a `api.yaml`:\n```yaml\nopenapi: \"3.0.0\"\n\ninfo:\n  title: My calculator\n  version: \"1.0\"\n  description: My awesome calc!\n\npaths:\n  \"/add/{n1}/{n2}\":\n    get:\n      operationId: AddGet\n      summary: Adds two numbers\n\n      parameters:\n        - name: n1\n          required: true\n          in: path\n          description: The first number\n          schema:\n            type: integer\n        - name: n2\n          required: true\n          in: path\n          description: The second number\n          schema:\n            type: integer\n    post:\n      operationId: AddPost\n      summary: Adds two numbers via POST\n\n      requestBody:\n        description: The numebers map\n        required: true\n        content:\n          application/json:\n            schema:\n              $ref: \"#/components/schemas/NumbersMap\"\n  \"/health\":\n    get:\n      operationId: HealthCheck\n      summary: Returns Ok if all is well\n\ncomponents:\n  schemas:\n    NumbersMap:\n      type: object\n      required:\n        - n1\n        - n2\n      properties:\n        n1:\n          type: integer\n          description: The first number\n        n2:\n          type: integer\n          description: The second number\n```\n\nA clojure map of OperationId to handler fns:\n```clojure\n(def handlers\n  {\"AddGet\" (fn [{{{:keys [n1 n2]} :path} :parameters}]\n              {:status 200\n               :body (str (+ n1 n2))})\n   \"AddPost\" (fn [{{{:keys [n1 n2]} :body} :parameters}]\n               {:status 200\n                :body (str (+ n1 n2))})\n   \"HealthCheck\" (fn [_]\n                   {:status 200\n                    :body \"Ok\"})})\n```\n\nGenerate the routes:\n```clojure\n(require '[navi.core :as navi])\n\n(navi/routes-from (slurp \"api.yaml\") handlers)\n=\u003e\n[[\"/add/{n1}/{n2}\"\n  {:get\n   {:handler #function[navi.core/fn--8260],\n    :parameters\n    {:path\n     [:map\n      [:n1 #function[clojure.core/int?]]\n      [:n2 #function[clojure.core/int?]]]}},\n   :post\n   {:handler #function[navi.core/fn--8266],\n    :parameters\n    {:body\n     [:map\n      {:closed false}\n      [:n1 #function[clojure.core/int?]]\n      [:n2 #function[clojure.core/int?]]]}}}]\n [\"/health\" {:get {:handler #function[navi.core/fn--8271]}}]]\n```\n\nBootstrapping a Jetty server:\n```clojure\n(ns server.main\n  (:require\n   [muuntaja.core :as m]\n   [navi.core :as navi]\n   [reitit.coercion.malli :as malli]\n   [reitit.http :as http]\n   [reitit.http.coercion :as coercion]\n   [reitit.http.interceptors.exception :as exception]\n   [reitit.http.interceptors.muuntaja :as muuntaja]\n   [reitit.http.interceptors.parameters :as parameters]\n   [reitit.interceptor.sieppari :as sieppari]\n   [reitit.ring :as ring]\n   [ring.adapter.jetty :as jetty])\n  (:gen-class))\n\n(def server\n  (http/ring-handler\n   (http/router (-\u003e \"api.yaml\"\n                    slurp\n                    (navi/routes-from handlers)) ; handlers is the map described before\n                {:data {:coercion malli/coercion\n                        :muuntaja m/instance\n                        :interceptors [(parameters/parameters-interceptor)\n                                       (muuntaja/format-negotiate-interceptor)\n                                       (muuntaja/format-response-interceptor)\n                                       (exception/exception-interceptor)\n                                       (muuntaja/format-request-interceptor)\n                                       (coercion/coerce-exceptions-interceptor)\n                                       (coercion/coerce-response-interceptor)\n                                       (coercion/coerce-request-interceptor)]}})\n   (ring/routes\n    (ring/create-default-handler\n     {:not-found (constantly {:status 404\n                              :headers {\"Content-Type\" \"application/json\"}\n                              :body \"{\\\"message\\\": \\\"Took a wrong turn?\\\"}\"})}))\n   {:executor sieppari/executor}))\n\n(defn -main\n  [\u0026 _]\n  (jetty/run-jetty (var server)\n                   {:host \"0.0.0.0\"\n                    :port 7777\n                    :join? false\n                    :async? true}))\n```\n\n### Build Requirements\n- JDK 8+\n- Clojure [tools.deps](https://clojure.org/guides/getting_started)\n\n### Running tests locally\n- `clojure -X:test` to run all tests\n\n## License\n\nCopyright © 2020- Rahul De\n\nDistributed under the MIT License. See LICENSE.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flispyclouds%2Fnavi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flispyclouds%2Fnavi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flispyclouds%2Fnavi/lists"}