Welcome Reitit 0.2.0!
After a few months in the making (and a holiday in between), we are proud to release a new version of reitit, the fast routing library for Clojure/Script:
[metosin/reitit "0.2.0"]
It contains a lot of new things, improvements to the core router and some fixes too. Here's a quick tour:
reitit-frontend
Link to reitit-frontend
A new module dedicated to frontend routing. It builds on top reitit-core
adding helpers for parsing of query-string, fragment and html5 history and support for Keechma-style controllers. We have been using the controllers for some time now and are really happy how it simplifies the state management in the frontend. The module docs haven't been polished yet, but the example-apps should show how to use the module:
reitit-middleware
Link to reitit-middleware
A common question has been how do you download/upload a file with reitit?. Answer is that reitit-ring
is fully compatible with Ring, so anything you can do with Ring, you can do with reitit-ring
. To make things easier, we decided to lift some common ring middleware into data-driven middleware intoduced by reitit. These include:
- content-negotiation, request and response formatting (via Muuntaja)
- multipart-handling (via ring default middleware)
- exception-handling (polished version from compojure-api)
reitit-middleware
module makes reitit-ring
feature wise more on par with popular web frameworks like compojure-api. Below is an example app with all the default middleware in place:
(ns example.server
(:require [reitit.ring :as ring]
[reitit.swagger :as swagger]
[reitit.swagger-ui :as swagger-ui]
[reitit.ring.coercion :as coercion]
[reitit.coercion.spec]
[reitit.ring.middleware.muuntaja :as muuntaja]
[reitit.ring.middleware.exception :as exception]
[reitit.ring.middleware.multipart :as multipart]
[ring.middleware.params :as params]
[ring.adapter.jetty :as jetty]
[muuntaja.core :as m]
[clojure.java.io :as io]))
(def app
(ring/ring-handler
(ring/router
[["/swagger.json"
{:get {:no-doc true
:swagger {:info {:title "my-api"}}
:handler (swagger/create-swagger-handler)}}]
["/files"
{:swagger {:tags ["files"]}}
["/upload"
{:post {:summary "upload a file"
:parameters {:multipart {:file multipart/temp-file-part}}
:responses {200 {:body {:name string?, :size int?}}}
:handler (fn [{{{:keys [file]} :multipart} :parameters}]
{:status 200
:body {:name (:filename file)
:size (:size file)}})}}]
["/download"
{:get {:summary "downloads a file"
:swagger {:produces ["image/png"]}
:handler (fn [_]
{:status 200
:headers {"Content-Type" "image/png"}
:body (io/input-stream
(io/resource "reitit.png"))})}}]]
["/math"
{:swagger {:tags ["math"]}}
["/plus"
{:get {:summary "plus with spec query parameters"
:parameters {:query {:x int?, :y int?}}
:responses {200 {:body {:total int?}}}
:handler (fn [{{{:keys [x y]} :query} :parameters}]
{:status 200
:body {:total (+ x y)}})}
:post {:summary "plus with spec body parameters"
:parameters {:body {:x int?, :y int?}}
:responses {200 {:body {:total int?}}}
:handler (fn [{{{:keys [x y]} :body} :parameters}]
{:status 200
:body {:total (+ x y)}})}}]]]
{:data {:coercion reitit.coercion.spec/coercion
:muuntaja m/instance
:middleware [;; query-params & form-params
params/wrap-params
;; content-negotiation
muuntaja/format-negotiate-middleware
;; encoding response body
muuntaja/format-response-middleware
;; exception handling
exception/exception-middleware
;; decoding request body
muuntaja/format-request-middleware
;; coercing response bodys
coercion/coerce-response-middleware
;; coercing request parameters
coercion/coerce-request-middleware
;; multipart
multipart/multipart-middleware]}})
(ring/routes
(swagger-ui/create-swagger-ui-handler
{:path "/"
:config {:validatorUrl nil}})
(ring/create-default-handler))))
(defn start []
(jetty/run-jetty #'app {:port 3000, :join? false})
(println "server running in port 3000"))
The syntax is more verbose than in compojure-api, but then again: there are no macros or hidden defaults. User is in control of everything.
reitit-http
Link to reitit-http
Part of the 0.2.0 release, but still considered work-in-progress. It builds on top of reitit-ring
, but swaps the :middleware
execution model with pedestal-style interceptors using the :interceptors
key. Goal is to provide both an optional routing interceptor to Pedestal and a standalone implementation on top of ring-async.
reitit-sieppari
Link to reitit-sieppari
Sieppari is a new super-alpha interceptor runner for Clojure (later also to ClojureScript). For the adventurous, it provides simple interceptor model with no external dependencies and supporting pluggable async (core.async, manifold and promesa for now).
As the version 0.0.0-alpha5
of sieppari suggests, it's not meant for production. Things like backpressure need to be addressed first.
Other StuffLink to Other Stuff
Besides the new modules, there are lot of impromevents to existing functionality. These include:
- support for (duct-style) interceptor & middleware registries
- new fast and correct path parameter decoder - for both jvm & js
- improvements for swagger support for both schema & clojure.spec
- automatic route name conflict resolution
- guide for composable & dynamic routers
- support for sequential routes
Next?Link to Next?
There are lot's of ideas what to do next, mostly written as gihub issues. Comments and contributions welcome on those. Here is a list of some of the topics:
- finalize the interceptor-modules
- awesome error messages
- support for OpenAPI3
- ensure everything runs on ClojureScript
- a re-frame-10x -style dev console & visualization
- bundle all the good stuff into an opinionated, newbie-friendly application library/framework
But now, let's enjoy the 0.2.0 release. Big thanks to all the contributors!
You can also join #reitit
channel in the Clojurians Slack.