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

1. `:http/api` - For making requests to the application's API endpoints
2. `:http/auth-api` - For making authenticated requests that automatically include the user's access token
3. `: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.

```clojure
(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:

1. **Keywords**: When using a keyword like `:account/log-in`, it will be automatically converted to `/api/account/log-in`
2. **Strings**: You can also use full paths like `"/api/account/verify"`

```clojure
;; 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.

```clojure
(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:

```clojure
;; 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:

1. When a request returns a `401 Unauthorized` error
2. The system automatically attempts to refresh the token
3. If token refresh succeeds, the original request is retried with the new token
4. 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

```clojure
(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:

```clojure
(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

```clojure
(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

```clojure
(rf/dispatch
  [:http/api
   {:url :data/fetch
    :on-failure-n [[:data-fetch-error] 
                   [:toast/error {:message "Failed to fetch data"}]]}])
```

## Common Request Patterns

### Login

```clojure
(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

```clojure
(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

```clojure
(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:

```clojure
(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 handlers
* `src/cljs/saas/query.cljs`: Actual HTTP request implementation
* `src/cljs/saas/auth.cljs`: Authentication and token management


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://shipclojure.gitbook.io/shipclojure-docs/frontend/http-requests.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
