HomeServicesExpertiseOpen source supportAboutStoryReferencesCareerInsightsContact
Monday, 3 September, 2018
Tommi Reiman
Technology

Welcome Reitit 0.2.0!

Post cover image

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-frontendLink 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:

  • simple app with reitit.frontend.easy
  • simple app with controllers

reitit-middlewareLink 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-httpLink 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.

  • example app with reitit-http and pedestal

reitit-sieppariLink 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).

  • example with reitit-http + ring-async + sieppari

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.

Tommi Reiman
Monday, 3 September, 2018

Related posts

Three Levels of AI Impact

by Tapio Nissilä
Cover Image for Three Levels of AI Impact

A Guide for Technology Executives

by Tapio Nissilä
Cover Image for A Guide for Technology Executives

Building Humane AI Adoption

by Tapio Nissilä
Cover Image for Building Humane AI Adoption
More

Contact

HomeServicesExpertiseOpen source supportAboutStoryReferencesCareerInsightsContact

Tampere
Hämeenkatu 13 A 5
33100 Tampere

Helsinki
Kalevankatu 13, 3rd floor
00100 Helsinki

Jyväskylä
Väinönkatu 30, 5th floor
40100 Jyväskylä

Oulu
Kirkkokatu 4 A 51
90100 Oulu

Business-id: 3374640-7
E-invoice: 003733746407
Operator: 003723327487 / Apix
metosin@skannaus.apix.fi

first.last@metosin.fi

Github
Instagram
LinkedIn
YouTube

Cookie settings