A re-frame "effects handler" for performing Ajax tasks (via cljs-ajax)
Clone or download

README.md

GitHub license Circle CI

HTTP Effects Handler For re-frame

This re-frame library contains an HTTP Effect Handler.

Keyed :http-xhrio, it wraps the goog xhrio API of cljs-ajax.

IMPORTANT: This effect handler depends entirely on the API of cljs-ajax. Make sure you are familiar with the API for cljs-ajax, and especially with ajax-request before proceeding.

Quick Start Guide

Step 1. Add Dependency

Add the following project dependency:
Clojars Project

Requires re-frame >= 0.8.0

Step 2. Registration And Use

In the namespace where you register your event handlers, perhaps called events.cljs, you have 2 things to do.

First, add this "require" to the ns:

(ns app.core
  (:require
    ...
    [day8.re-frame.http-fx]   ;; <-- add this
    ...))

Because we never subsequently use this require, it appears redundant. But its existence will cause the :http-xhrio effect handler to self-register with re-frame, which is important to everything that follows.

Second, write a an event handler which uses this effect:

(reg-event-fx                             ;; note the trailing -fx
  :handler-with-http                      ;; usage:  (dispatch [:handler-with-http])
  (fn [{:keys [db]} _]                    ;; the first param will be "world"
    {:db   (assoc db :show-twirly true)   ;; causes the twirly-waiting-dialog to show??
     :http-xhrio {:method          :get
                  :uri             "https://api.github.com/orgs/day8"
                  :timeout         8000                                           ;; optional see API docs
                  :response-format (ajax/json-response-format {:keywords? true})  ;; IMPORTANT!: You must provide this.
                  :on-success      [:good-http-result]
                  :on-failure      [:bad-http-result]}}))

Look at the :http-xhrio line above. This library defines the "effects handler" which implements :http-xhrio.

The supplied value should be an options map as defined by the simple interface ajax-request see: api docs. Except for :on-success and :on-failure. All options supported by ajax-request should be supported by this library, as it is a thin wrapper over ajax-request.

Here is an example of a POST request. Note that :format also needs to be specified (unless you pass :body in the map).

(re-frame/reg-event-fx
  ::http-post
  (fn [_world [_ val]]
    {:http-xhrio {:method          :post
                  :uri             "https://httpbin.org/post"
                  :params          data
                  :timeout         5000
                  :format          (ajax/json-request-format)
                  :response-format (ajax/json-response-format {:keywords? true})
                  :on-success      [::good-post-result]
                  :on-failure      [::bad-post-result]}}))

N.B.: ajax-request is harder to use than the GET and POST functions cljs-ajax provides, but this gives you smaller code sizes from dead code elimination. In particular, you MUST provide a :response-format, it is not inferred for you.

Don't provide:

 :api     - the effects handler explicitly uses xhrio so it will be ignored.
 :handler - we substitute this with one that dispatches `:on-success` or `:on-failure` events.

You can also pass a list or vector of these options maps where multiple HTTPs are required.

To make multiple requests, supply a vector of options maps:

{:http-xhrio [ {...}
               {...}]}

Step 3. Handlers for :on-success and :on-failure

Provide normal re-frame handlers for :on-success and :on-failure. Your event handlers will get the result as the last arg of their event-v. Here is an example written as another effect handler to put the result into db.

(reg-event-db
  :good-http-result
  (fn [db [_ result]]
    (assoc db :api-result result)))

The result passed to your :on-failure is always a map with various xhrio details provided. See the fn ajax-xhrio-handler for details

TIP:

If you need additional arguments or identifying tokens in your handler, then include them in your :on-success and :on-failure event vector in Step 3. they will be passed along. Actual result will always be the last value.