Fortunately, the
api:with-routes[ns=io.pedestal.connector] macro handles all of this for us,
provided development mode is enabled.
with-routes internally uses the
api:routes-from[ns=io.pedestal.http.route] macro, which is aware of the Pedestal execution mode.
In production mode, routes are expanded once at startup — exactly what you want for performance.
In development mode, routes are re-expanded on every incoming request, and all symbols are
dynamically re-resolved, so changes to handler functions, routes, and interceptors are picked up
immediately.
To enable development mode, set the system property io.pedestal.dev-mode to true when starting
the JVM. With the clj tool, this can be done through the deps.edn:
{:paths ["src"]
:deps {io.pedestal/pedestal.http-kit {:mvn/version "{pedestal-version}"}
org.slf4j/slf4j-simple {:mvn/version "2.0.17"}}
:aliases
{:dev {:jvm-opts ["-Dio.pedestal.dev-mode=true"]}}}
Then start the REPL in development mode via clj -A:dev.
Alternatively, you can set the environment variable PEDESTAL_DEV_MODE=true.
Once development mode is enabled, no code changes are needed. Our existing code, with
conn/with-routes, already does the right thing:
(defn create-connector []
(-> (conn/default-connector-map 9999)
(conn/with-default-interceptors)
(conn/with-routes routes) (1)
(hk/create-connector nil)))
| 1 | In development mode, this re-expands routes on every request, dynamically resolving all symbols. |
Let’s stop the connector and start fresh, this time with development mode on:
Verify the original behavior:
> http -pb :9999/hello
Hello, World!
Now, change hello-handler to return "Hello, Clojure World!", reload the namespace, and retest:
> http -pb :9999/hello
Hello, Clojure World!
Success! The change was picked up without restarting the connector.
Let’s also change the route path from /hello to /hi, reload the namespace, and try it:
> http -pb :9999/hi
Hello, Clojure World!
That works too. You can add, remove, or otherwise change your routes, update handler functions, add interceptors …
wherever your development takes you, and those changes will be re-evaluated and re-loaded on each request.
|
|
Not For Production
In development mode, route expansion happens on every incoming request. Even a trivial
route specification takes a chunk of time to expand, so this would absolutely trash your
production server’s throughput. This is why it must be explicitly enabled via the system property
or environment variable; by default Pedestal operates in production mode.
|
In development mode, Pedestal will also write a formatted routing table to the console at startup, and at any later time that the routing table changes:
Routing table:
┌──────┬──────┬───────────────────────────┐
│Method│ Path │ Name │
├──────┼──────┼───────────────────────────┤
│ :get│/hello│:org.example.hello/hello │
└──────┴──────┴───────────────────────────┘
This output is especially useful to know the correct route name to pass to
api:url-for[ns=io.pedestal.http.route]
(to generate application URLs included in responses)
or
api:response-for[ns=io.pedestal.connector.test]
(used in your unit tests).
|
|
How does this work under the hood?
with-routes is a macro that calls the routes-from macro internally. In production mode,
routes-from simply expands the routes once. In development mode, it generates a zero-argument function
that re-expands the routes on each call, using ns-resolve to dynamically look up every symbol.
This means that both the route definitions and the handler functions they reference are always
resolved to their current values.
|