|
| 1 | +--- |
| 2 | +title: Clojure REPL driven API project from scratch |
| 3 | +date: |
| 4 | + created: 2024-07-21 |
| 5 | + updated: 2024-07-21 |
| 6 | +authors: |
| 7 | + - practicalli |
| 8 | +categories: |
| 9 | + - clojure |
| 10 | +tags: |
| 11 | + - clojure |
| 12 | + - api |
| 13 | +draft: true |
| 14 | +--- |
| 15 | + |
| 16 | +Creating a comprehensive API project in Clojure. |
| 17 | + |
| 18 | +??? HINT "Practicalli Project Templates" |
| 19 | + [Practicalli Project Templates](https://practical.li/clojure/clojure-cli/projects/templates/practicalli) provides several templates to build production level projects. Generated projects can include component management systems, e.g. donut-party/system or integrant. |
| 20 | + |
| 21 | + Create a new project called gameboard managed by donut-party/system with an example API service and swagger documentation. |
| 22 | + ```shell |
| 23 | + clojure -T:project/create :template practicalli/service :name practicalli/gameboard :component donut |
| 24 | + ``` |
| 25 | + |
| 26 | +<!-- more --> |
| 27 | + |
| 28 | +## Create a project |
| 29 | + |
| 30 | +Create a Clojure CLI project using Practicalli Clojure CLI config. Clojure 1.12. supports hot loading of library dependencies. |
| 31 | + |
| 32 | +```shell |
| 33 | +clojure -T:project/create :template practicalli/application :name practicalli/repl-driven-api |
| 34 | +``` |
| 35 | + |
| 36 | +## Start REPL |
| 37 | + |
| 38 | +Start a REPL process along with nREPL server to enable your Clojure aware editor to connect to the REPL and give instant feedback. |
| 39 | + |
| 40 | +!!! EXAMPLE "Start a REPL with nREPL server & a rich terminal UI" |
| 41 | +```shell |
| 42 | +clojure -M:repl/rebel |
| 43 | +``` |
| 44 | + |
| 45 | +> The REPL could also be started from a Clojure aware editor (jack-in) |
| 46 | +
|
| 47 | + |
| 48 | +## Web server |
| 49 | + |
| 50 | +The Web Server listens to HTTP requests and sends HTTP responses back to the calling client. |
| 51 | + |
| 52 | + |
| 53 | +Http kit and jetty are the most commonly used web servers. Http-kit is predominantly written in Clojure and will be used for these examples |
| 54 | + |
| 55 | + |
| 56 | +!!! EXAMPLE "Add http-kit library" |
| 57 | + ```shell |
| 58 | + ;; add-libs command |
| 59 | + ``` |
| 60 | + |
| 61 | + |
| 62 | +!!! EXAMPLE "Add function to start & stop http-kit server" |
| 63 | + ```clojure |
| 64 | + |
| 65 | + ``` |
| 66 | + |
| 67 | +## Convert HTTP request to Clojure |
| 68 | + |
| 69 | +Ring is the defacto library that bridges the world of HTTP requests & responses with Clojure. Ring acts as an adaptor on top of the web server. |
| 70 | + |
| 71 | +Ring converts an HTTP request into a Clojure Hash Map, the request map. |
| 72 | + |
| 73 | +Once a response has been created, Ring converts the response map into an HTTP response. |
| 74 | + |
| 75 | +!!! EXAMPLE "Add Reitit libraries" |
| 76 | + ```shell |
| 77 | + |
| 78 | + ``` |
| 79 | + |
| 80 | +## Routing http requests |
| 81 | + |
| 82 | +Routing takes a request and sends it to a header function to create a response. |
| 83 | + |
| 84 | +Reitit configuration is a hash-map with HTTP type (GET, POST, ) |
| 85 | +Add a request router function to dispatch requests to specific handler functions |
| 86 | + |
| 87 | +!!! EXAMPLE "Add a reitit-ring router" |
| 88 | + ```clojure |
| 89 | + |
| 90 | + ``` |
| 91 | + |
| 92 | +> Reitit is a library that provides routing for both Clojure and ClojureScript. Reitit-ring provides the webserver adaptor for back-end Clojure applications. Reitit also includes libraries for middleware, coersion, etc. |
| 93 | +
|
| 94 | + |
| 95 | + |
| 96 | + |
| 97 | +[Anatomy of a Clojure Web Service](https://practical.li/clojure-web-services/app-servers){target=_blank .md-button} |
| 98 | + |
| 99 | +??? INFO: |
| 100 | + |
| 101 | + |
| 102 | +## Service REPL |
| 103 | + |
| 104 | + |
| 105 | + |
| 106 | +## Integrant |
| 107 | + |
| 108 | + |
| 109 | +Integrant is a data centric approach although I would like simpler code design to work with. |
| 110 | + |
| 111 | + |
| 112 | +Integrant REPL is a separate library |
| 113 | + |
| 114 | + |
| 115 | +## Donut |
| 116 | + |
| 117 | + |
| 118 | + |
| 119 | +A data as code approach, extended by simple functions |
| 120 | + |
| 121 | +defmethod only used to create different system types, so only a minimal used (unless there is a very complex set of systems) |
| 122 | + |
| 123 | +REPL tools are built into the System library. |
| 124 | + |
| 125 | + |
| 126 | + |
| 127 | +## Reloadable Code |
| 128 | + |
| 129 | +Minimise |
| 130 | +- macros |
| 131 | +- defmethod, deftype, defrecord |
| 132 | + |
| 133 | + |
| 134 | +## Exclude code from reload |
| 135 | + |
| 136 | +Set refresh directories to include all paths except those that should be excluded from the reload, e.g `(set-refresh-dirs "src" "resources")` will exclude the `"test"` and `"dev"` directories from the refresh. |
| 137 | + |
| 138 | +The `set-refresh-dirs` function returns the directory paths set, so including the function call within a printlin expression provides useful feedback on REPL startup. |
| 139 | + |
| 140 | +!!! EXAMPLE "Set directories to reload" |
| 141 | + ```clojure |
| 142 | + (require '[clojure.tools.namespace.repl :refer [set-refresh-dirs]]) |
| 143 | + |
| 144 | + ;; --------------------------------------------------------- |
| 145 | + ;; Avoid reloading `dev` code |
| 146 | + ;; - code in `dev` directory should be evaluated if changed to reload into repl |
| 147 | + (println |
| 148 | + "Set REPL refresh directories to " |
| 149 | + (set-refresh-dirs "src" "resources")) |
| 150 | + ;; --------------------------------------------------------- |
| 151 | + ``` |
| 152 | + |
| 153 | + |
| 154 | +--- |
| 155 | +Thank you. |
| 156 | + |
| 157 | +[:globe_with_meridians: Practical.li Website](https://practical.li){target=_blank .md-button} |
| 158 | + |
| 159 | +[:fontawesome-brands-github: Practical.li GitHub Org](https://github.com/practicalli){target=_blank .md-button} |
| 160 | +[:fontawesome-brands-github: practicalli-johnny profile](https://github.com/practicalli-johnny){target=_blank .md-button} |
| 161 | + |
| 162 | +[:fontawesome-brands-mastodon: @practicalli@clj.social](https://clj.social/@practicalli){target=_blank .md-button} |
| 163 | +[:fontawesome-brands-twitter: @practical_li](https://twitter.com/practcial_li){target=_blank .md-button} |
0 commit comments