Skip to content
Mischov edited this page Apr 27, 2015 · 19 revisions

Gate middleware is just standard Ring middleware, but Gate does a few things which makes it even nicer to use.


What is Ring Middleware?

Ring middleware is any function that accepts a handler and returns a function accepting a request.

(defn upperware
  [handler]
  (fn [request]
    (let [response (handler request)
          body (get response :body)]
      (assoc response :body (clojure.string/upper-case body)))))

Applying Middleware to Handlers

There are a bunch of different ways you can apply middleware to handlers with Gate.

First off, you can apply middleware to all the routes in a router by adding that middleware to the :middleware vector of a router's settings. In the following example the middleware upperware will be added to all of the routes in the router.

(defrouter your-router
  [{:path "/hello"
    :get  (fn [_] "Hello!")}
   {:path "/goodbye"
    :get  (fn [_] "Goodbye!")}]
  {:middleware [upperware]})

Many times, however, you don't want middleware to apply to all of your routes. In that case you can apply middleware to just a single route by adding a :middleware vector to that route. In the following example upperware will only be applied on the route /hello.

(defrouter your-router
  [{:path "/hello"
    :middleware [upperware]
    :get  (fn [_] "Hello!")}
   {:path "/goodbye"
    :get  (fn [_] "Goodbye!")}])

Middleware from a parent route is applied to all child routes. In the following example the route /hello/:name has upperware applied to it because the /hello/:name is a child route to /hello.

(defrouter your-router
  [{:path "/hello"
    :middleware [upperware]
    :get  (fn [_] "Hello!")
    :children [
               {:path "/:name"
                :get (fn [req]
                        (let [name (get-in req [:params :name])]
                          (str "Hello, " name "!")))}]}])

You can even restrict middleware to a certain method of a route. In the following example upperware is applied to GET requests, but not to POST requests.

(defrouter your-router
  [{:path "/hello"
    :get  {:handler (fn [_] "Hello!")
           :middleware [upperware]}
    :post (fn [_] "Nothing here!")}])

If more than one middleware is added to a middleware vector, they are applied in left-to-right/top-to-bottom order.


Combining Middleware

:middleware vectors accept middleware functions, as shown above, but they can also accept sequences of middleware functions. This makes it exceedingly simple to combine middlewares that need to be used together into a single middleware.

Compojure has a middleware compojure.handler/api that combines together three ring middlewares and is defined like this:

(defn api
  [routes]
  (-> routes
      wrap-keyword-params
      wrap-nested-params
      wrap-params))

Gate could define a similar middleware thusly:

(def api
  [wrap-params
   wrap-nested-params
   wrap-keyword-params])

Notice that unlike threaded middleware which are defined in reverse order of application, the vector of middleware are in order of application.

To use this combined middleware, you would just add it to a vector of middlewares like normal.

(defrouter your-router
  [{:path "/hello"
    :middleware [api]
    :get  (fn [_] "Hello!")}])