HTTP Requests with Re-frame
This document explains how to make HTTP requests in the application using Re-frame events and effects.
Overview
The application provides several ways to make HTTP requests:
:http/api- For making requests to the application's API endpoints:http/auth-api- For making authenticated requests that automatically include the user's access token:http/request- For making requests to any URL, not just the application's API
These are implemented as Re-frame effects that can be used in your event handlers.
Basic API Requests
The :http/api effect is the primary way to make requests to your application's API endpoints.
(rf/dispatch
[:http/api
{:method :post
:url :account/sign-up
:body {:email "user@example.com"
:password "password123"}
:on-success [:sign-up-success]
:on-failure [:sign-up-failure]}])
API Endpoint Specification
API endpoints can be specified in two ways:
- Keywords: When using a keyword like
:account/log-in, it will be automatically converted to/api/account/log-in - Strings: You can also use full paths like
"/api/account/verify"
;; These are equivalent:
{:url :account/log-in}
{:url "/api/account/log-in"}
Authenticated Requests
The :http/auth-api event is used for making authenticated requests. It automatically includes the user's access token in the Authorization header.
(rf/dispatch
[:http/auth-api
{:url :user/profile
:method :get
:on-success [:profile-loaded]
:on-failure [:profile-load-error]}])
Behind the scenes, this is converted to an :http/api call with the token included:
;; What happens internally:
{:http/api (assoc params :token (get-in db [:auth :access-token]))}
Token Refresh Mechanism
A key feature of the HTTP request system is automatic token refresh when authentication fails:
- When a request returns a
401 Unauthorizederror - The system automatically attempts to refresh the token
- If token refresh succeeds, the original request is retried with the new token
- If token refresh fails, the user is redirected to the login page
This mechanism is transparent to your application code - you don't need to handle token refresh explicitly.
Request Configuration Options
When making requests, you can specify the following options:
| Option | Type | Description |
|---|---|---|
:url | Keyword/String | The API endpoint to request |
:method | Keyword | The HTTP method to use (:get, :post, :put, :delete, :head) |
:body | Map/String | The request payload |
:headers | Map | Additional HTTP headers |
:query-params | Map | URL query parameters |
:token | String | Authorization token (added automatically for :http/auth-api) |
:success-path | Vector | Path within response to extract data from |
:content-type | Keyword | Request content type (defaults to :transit-json) |
:accept | Keyword | Response content type to accept (defaults to :transit-json) |
Handling Responses
There are several ways to handle responses from HTTP requests:
On Success
(rf/dispatch
[:http/api
{:url :data/fetch
:on-success [:data-fetched]}])
;; Define the success handler
(rf/reg-event-db
:data-fetched
(fn [db [_ response]]
(assoc db :data response)))
Multiple Success Handlers
You can trigger multiple events on success:
(rf/dispatch
[:http/api
{:url :account/log-in
:method :post
:body credentials
:on-success-n [[:auth/login-success]
[:router/navigate {:route ::routes/dashboard}]]}])
On Failure
(rf/dispatch
[:http/api
{:url :data/fetch
:on-failure [:data-fetch-error]}])
;; Define the failure handler
(rf/reg-event-db
:data-fetch-error
(fn [db [_ error]]
(assoc db :error error)))
Multiple Failure Handlers
(rf/dispatch
[:http/api
{:url :data/fetch
:on-failure-n [[:data-fetch-error]
[:toast/error {:message "Failed to fetch data"}]]}])
Common Request Patterns
Login
(rf/reg-event-fx
::login
(fn [_ [_ credentials]]
{:http/api {:method :post
:url :account/log-in
:body credentials
:on-success-n [[::auth-success]
[:router/navigate {:route ::routes/dashboard}]]
:on-failure [::auth-failure]}}))
Authenticated Data Fetch
(rf/reg-event-fx
::fetch-profile
(fn [_ _]
{:http/auth-api {:url :user/profile
:on-success [::profile-loaded]
:on-failure [::profile-load-error]}}))
Form Submission with Loading State
(rf/reg-event-fx
::save-settings
(fn [{:keys [db]} [_ settings]]
{:db (assoc db :saving-settings? true)
:http/auth-api {:method :post
:url :user/settings
:body settings
:on-success [::settings-saved]
:on-failure [::settings-save-error]}}))
(rf/reg-event-db
::settings-saved
(fn [db [_ result]]
(-> db
(assoc :settings result)
(dissoc :saving-settings?))))
(rf/reg-event-db
::settings-save-error
(fn [db [_ error]]
(-> db
(assoc :settings-error error)
(dissoc :saving-settings?))))
Content Types
The API supports several content types:
:transit-json(default) - Transit format encoded as JSON:json- Standard JSON:form-encoded- URL-encoded form data:text- Plain text:html- HTML:edn- EDN (Clojure data format)
To specify a content type:
(rf/dispatch
[:http/api
{:url :data/fetch
:content-type :json
:accept :json
:on-success [:data-fetched]}])
CSRF Protection
For non-GET requests, a CSRF token is automatically included in the headers. The token is retrieved from a hidden form field with the ID __anti-forgery-token.
Error Handling
By default, responses with status codes >= 400 are treated as errors. The error handler will receive either the error object or the response body.
Implementation Details
The HTTP request system is implemented in:
src/cljs/saas/db/events.cljs: Re-frame event and effect handlerssrc/cljs/saas/query.cljs: Actual HTTP request implementationsrc/cljs/saas/auth.cljs: Authentication and token management