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>