Skip to content

Commit f1601ba

Browse files
committed
AJAX client
1 parent 6be86f4 commit f1601ba

File tree

2 files changed

+200
-0
lines changed

2 files changed

+200
-0
lines changed

assets-public/js/ajax_client.js

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import axios from 'axios'
2+
import addToast from 'sumocoders/addToast'
3+
4+
class AjaxClient {
5+
constructor () {
6+
const options = {
7+
transformRequest: [
8+
(data, header) => {
9+
if (this.instance.csrf_token) {
10+
data.csrf_token = this.instance.csrf_token
11+
}
12+
return data
13+
}, ...axios.defaults.transformRequest
14+
]
15+
}
16+
17+
this.instance = axios.create(options)
18+
this.configureDefaults()
19+
this.configureInterceptors()
20+
}
21+
22+
configureDefaults () {
23+
this.instance.defaults.timeout = 2500
24+
this.instance.defaults.headers.common = {
25+
'Accept': 'application/json',
26+
}
27+
}
28+
29+
configureInterceptors () {
30+
this.instance.interceptors.response.use(
31+
// Successful request
32+
function (response) {
33+
// Any status code that lie *within* the range of 2xx cause this function to trigger
34+
35+
if (response.data.disable_interceptor) {
36+
return response
37+
}
38+
39+
if (response.data.message) {
40+
addToast(response.data.message, 'success')
41+
}
42+
43+
return response
44+
},
45+
function (error) {
46+
// Any status codes that falls *outside* the range of 2xx cause this function to trigger
47+
48+
if (error.response.data.disable_interceptor) {
49+
return Promise.reject(error)
50+
}
51+
52+
console.error(error)
53+
54+
if (error.response.data.message) {
55+
addToast(error.response.data.message, 'danger')
56+
57+
return Promise.reject(error)
58+
}
59+
60+
if (error.message) {
61+
addToast(error.message, 'danger')
62+
63+
return Promise.reject(error)
64+
}
65+
66+
return Promise.reject(error)
67+
})
68+
}
69+
}
70+
71+
const instance = new AjaxClient()
72+
73+
export default instance.instance

docs/frontend/ajax-client.md

+127
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
# AJAX client
2+
3+
The AJAX client is a simple wrapper around [`Axios`](https://axios-http.com/).
4+
5+
## Default Axios Config
6+
7+
Some configuration is done by default. But it can be overruled.
8+
9+
* `timeout`: 2500
10+
* `headers.common`: `Accept: application/json`
11+
12+
## Usage
13+
14+
Is the AJAX Client is just an extended version of the Axios client all Axios
15+
documentation is still valid. For the full documentatuon see
16+
[https://axios-http.com/docs/intro](https://axios-http.com/docs/intro).
17+
18+
A simplified Stimulus controller that uses the AJAX Client:
19+
20+
```javascript
21+
import { Controller } from '@hotwired/stimulus'
22+
import ajaxClient from '../js/ajax_client.js'
23+
24+
export default class extends Controller {
25+
static values = {
26+
url: String,
27+
csrfToken: String
28+
}
29+
30+
test () {
31+
let data = {
32+
foo: 'bar',
33+
}
34+
35+
ajaxClient.csrf_token = this.csrfTokenValue
36+
ajaxClient.post(this.urlValue, data)
37+
.then(response => {
38+
// do something with the response
39+
...
40+
})
41+
.catch(error => {
42+
// do something with the error
43+
...
44+
})
45+
}
46+
}
47+
```
48+
49+
## Default toasts
50+
51+
Depending on HTTP status code and the provided data a toast will be shown. This
52+
is done by using [Interceptors](https://axios-http.com/docs/interceptors). So
53+
you can still use the promises of the Axios client.
54+
55+
### Success (HTTP Status 2XX)
56+
57+
If the response object contains a `message` key, a success toast will be shown.
58+
59+
You can return a response like this:
60+
61+
```php
62+
return new JsonResponse(
63+
[
64+
'message' => 'The action was successful.',
65+
],
66+
200
67+
);
68+
```
69+
70+
The actual JSON will be:
71+
72+
```json
73+
{
74+
"message": "The action was successful."
75+
}
76+
```
77+
78+
### Error (HTTP Status != 2XX)
79+
80+
If the response object contains a `message` key, an danger toast will be shown.
81+
If the message is not present the Exception message will be used.
82+
83+
```php
84+
return new JsonResponse(
85+
[
86+
'message' => 'Item not found.',
87+
],
88+
404
89+
);
90+
```
91+
92+
or
93+
94+
```php
95+
throw new \RuntimeException('Item not found.');
96+
```
97+
98+
### Disable this behavior
99+
100+
You can disable the toast by passing a `disable_interceptor: false` in the response data.
101+
102+
```json
103+
{
104+
"message": "The action was successful",
105+
"disable_interceptor": true
106+
}
107+
```
108+
109+
## CSRF token
110+
111+
A simple way to "protect" the AJAX calls is by using a CSRF token. This is done like below:
112+
113+
```javascript
114+
ajaxClient.csrf_token = this.csrfTokenValue
115+
ajaxClient.post(this.urlValue, data)
116+
...
117+
```
118+
119+
With this the csrf token is added to the payload of the request, with the key `csrf_token`.
120+
121+
In your controller you will need to check if the CSRF token is valid:
122+
123+
```php
124+
if (!$this->isCsrfTokenValid('this-is-our-csrf-token-id', $request->getPayload()->get('csrf_token'))) {
125+
throw new InvalidCsrfTokenException('Invalid CSRF token');
126+
}
127+
```

0 commit comments

Comments
 (0)