Single-Page Apps & AJAX calls
Because Single-Page Applications (SPA) rely so heavily on AJAX calls, SPAs need a specific protection solution to defend them against bots as opposed to multi-page sites.
Below are the 2 steps needed to integrate DataDome protection into your SPA.
1 - Captcha Display
Ajax Listener
When DataDome detects an illegal use of the API, DataDome server-side module blocks the request.
DataDome JavaScript Tag handles the display of a Captcha on the client-side.
This can be done by simply adding the parameter ajaxListenerPath
in the JavaScript Tag:
- to true to listen to AJAX calls made on the same domain as the displayed page
- to any given string (or array of strings) to be matched against the requested URL, ex: 'domain.com/api' or '/api')
<script>
window.ddjskey = 'YOUR_DATADOME_JS_KEY';
window.ddoptions = { ajaxListenerPath : true };
</script>
<script src="https://js.datadome.co/tags.js" async></script>
In this example, each AJAX and fetch() call on a URL made on the same domain as the hosting page will be monitored by the JavaScript Tag.
If the API call gets blocked by the DataDome server-side module, the JavaScript Tag will hook the request and display a Captcha.
<script>
window.ddjskey = 'YOUR_DATADOME_JS_KEY';
window.ddoptions = { ajaxListenerPath : 'domain.com/api' };
</script>
<script src="https://js.datadome.co/tags.js" async></script>
In this example, each AJAX and fetch() call on a URL that contains the word domain.com/api
will be monitored by the JavaScript Tag.
The parameter value of ajaxListenerPath
will run an IndexOf
on the full API endpoint URL, including the schema.
<script>
window.ddjskey = 'YOUR_DATADOME_JS_KEY';
window.ddoptions = { ajaxListenerPath : ['domain.com/api', 'domain.com/other-api'] };
</script>
<script src="https://js.datadome.co/tags.js" async></script>
In this example, each AJAX and fetch() call on a URL that contains the word domain.com/api
or domain.com/other-api
will be monitored by the JavaScript Tag.
All the values of ajaxListenerPath
will run an IndexOf
on the full API endpoint URL, including the schema.
Please find below a working example using JQuery:
<html>
<head>
<script>
!function(a,b,c,d,e,f){a.ddjskey=e;a.ddoptions=f||null;var m=b.createElement(c),n=b.getElementsByTagName(c)[0];m.async=1,m.src=d,n.parentNode.insertBefore(m,n)}(window,document,"script","https://js.datadome.co/tags.js", "DATADOME_JS_KEY", { ajaxListenerPath : 'domain.com/api/' });
</script>
<script src="https://code.jquery.com/jquery-3.3.1.js"></script>
<script>
function callTest() {
var url = "api/1.0/endpoint";
$.ajax({
url: url,
dataType: 'json',
success: function() {
console.log('success');
}
});
}
</script>
</head>
<body>
<div onclick="callTest()">click !</div>
</body>
</html>
Cross-domain & CORS
In case your API and your SPA are on different domains, Cross-Origin Resource Sharing (CORS) needs to be setup accordingly.
Service workers
In case some API calls are handled by a service worker, you need to contact our support team to provide you with the code needed for monitoring the API calls made from service workers.
2 - Cookie support and request headers
In order to properly detect the Bot activity on the API used by the SPA, DataDome requires the SPA to support the DataDome cookie correctly. Please ensure that your SPA supports the DataDome session cookie for each call sent to the API.
DataDome automatically displays a Captcha when the SPA sends the content-type
= application/json
and accept
= application/json
request headers on API calls. HTML content type is also supported using the allowHtmlContentTypeOnCaptcha
option in the JavaScript Tag (cf Jquery example from Section 1).
If your application use none of the aforementioned content types, and you cannot change your SPA, please contact our support team in order to manually set it up.
Below are examples on how to support cookies and send the appropriate "Content-Type" and "Accept" request headers:
- On React using Fetch for API calls
- On Angular using the Angular HttpClient
- On Vue JS using the Vue.http.interceptors
- On JQuery using Ajax request
- On Javascript using Axios
fetch('https://example.com', {
credentials: 'include', // if API is on the same domain, use 'same-origin'
headers: {
"Accept":"application/json"
},
[...]
})
import { Observable } from 'rxjs';
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
@Injectable()
export class HttpService {
/**
@param httpClient
*/
public constructor(protected httpClient: HttpClient) { }
/**
Ref: https://angular.io/api/common/http/HttpRequest#withCredentials
*/
public request(): Observable<any> {
const headers = new HttpHeaders({
'Accept': 'application/json'
});
return this.httpClient.request(
'POST',
'https://example.com/api/endpoint',
{
headers,
body: { ..... },
withCredentials: true,
});
}
}
Vue.use(VueResource)
Vue.http.interceptors.push((request, next) => {
request.credentials = true
request.headers['Accept'] = 'application/json'
next()
})
$.ajax({
url: a_cross_domain_url,
accepts: {
json: 'application/json'
},
xhrFields: {
withCredentials: true
}
});
//Enable the cookie on all requests
axios.defaults.withCredentials = true
//OR
//Enable the cookie at the request level
const axiosInstance = axios.create({
headers: {
accept: 'application/json'
},
withCredentials: true
});
axiosInstance.get(a_cross_domain_url)
.then(function (response) {
// handle success
console.log(response);
})
.catch(function (error) {
// handle error
console.log(error);
});
FAQ
How to configure the listener to support multiple API URLs?
This can be achieved by passing an array of URLs instead of a single URL to the ajaxListenerPath
parameter:
<script>
window.ddjskey = 'YOUR_DATADOME_JS_KEY';
window.ddoptions = { ajaxListenerPath : ['domain1/api', 'domain2', 'domain3'] };
</script>
<script src="https://js.datadome.co/tags.js" async></script>
Updated 8 months ago