Apollo Hapi

DataDome Apollo Hapi module detects and protects against bot activity

This module is created to be used inside a Hapi server with an Apollo-v2 server integration.

Before the regular request processing, the module sends requests to the DataDome server. Depending on the API response, the module either blocks the request or proceeds with the regular request life cycle.

The module has been developed to protect the visitors' user experience: if any errors were to occur during the process, or if the timeout is reached, the module will automatically disable its blocking process and allow the regular process to proceed.

How to install and embed the module

The module is distributed as an npm package. You can install it in your process and you will need to slightly modify the code.

The first step is installing it into an application (using npm) with the following command:

npm i @datadome/apollo-hapi

After the module is installed, you need to integrate it into your server configuration. First, let’s see an example of a basic configuration of an Apollo-v2 server integrated into a Hapi server:

const Hapi = require('hapi');
const { ApolloServer, gql } = require('apollo-server-hapi');
const { typeDefs, resolvers } = require('./schema');

async function StartServer() {
    const apollo_server = new ApolloServer({ 
        typeDefs,
        resolvers,
    });

    const hapi_server = new Hapi.server({
        port: 4000,
    });

    await apollo_server.applyMiddleware({
        app: hapi_server,
    });

    await hapi_server.start();
    console.log('Server running on %s', hapi_server.info.uri);
}

StartServer();

To integrate the DataDome module you need to add an extension for the Hapi server through the applyMiddleware method of the ApolloServer class. It should look similar to the following:

const Hapi = require('hapi');
const { ApolloServer, gql } = require('apollo-server-hapi');
const { typeDefs, resolvers } = require('./schema');
const DataDome = require('@datadome/apollo-hapi');

async function StartServer() {
    const apollo_server = new ApolloServer({ 
        typeDefs,
        resolvers,
        playground: false,
        // DataDome Apollo-Hapi module
        // is not compatible with GraphQL Playgroung
    });

    const hapi_server = new Hapi.server({
        port: 4000,
    });

    const datadomeClient = new DataDome('YOUR_API_KEY', 'api.datadome.co');

    const datadomeExtension = {
        // extension point for the main API call
        'onPreAuth': {
            method: async function(request, reply) {
                let DataDomePromise = new Promise((resolve) => {
                    let response = reply.response();
                    datadomeClient.authCallback(request, response, function() {
                        resolve(reply.continue);
                    }, function() {
                        resolve(response.takeover());
                    })
                })
                return await DataDomePromise;
            }
        },
        // extension point to add DataDome headers to response
        'onPreResponse': {
            method: async function(request, reply) {
                datadomeClient.addDataDomeHeaders(request);
                return (reply.continue);
            }
        }
    }

    await apollo_server.applyMiddleware({
        app: hapi_server,
        route: {
            ext: datadomeExtension,
        }
    });

    await hapi_server.start();
    console.log('Server running on %s', hapi_server.info.uri);
}

StartServer();

The idea behind the changes above is to plug the application logic to the valid event triggered by the module.
This module also emits blocked event with original request. But keep in mind that the the module will deliver the captcha page to the user before it emits the event.

🚧

API call must be onPreAuth

The extension point for the main API call must be onPreAuth. Using any other extension point will cause an undefined behavior of the module.

Options and events

You can also customize the behavior of the module by adding an object as third argument with parameters:

OptionDefault valueDescription
ssltrueDoes the module use HTTPS
port443The port to connect on ApiServer
path'/validate-request/'The endpoint on the ApiServer
timeout150Timeout in ms, after which request will be passed
uriRegexnullRegex that should be matched to process the request over ApiServer. null means accept all requests that don't match uriRegexExclusion.
uriRegexExclusion/\.(js|css|jpg|jpeg|png|ico|gif|tiff|svg|woff|woff2|ttf|eot|mp4|otf)$/Regex that should not be matched to process the request over ApiServer. null means accept all requests it tested after uriRegex.

An example of a call with default options:

...
const datadomeClient = new DataDome('some_key', 'api.datadome.co', {
    ssl: true,
    port: 443,
    path: '/validate-request/',
    timeout: 150,
    uriRegex: null,
    uriRegexExclusion: /\.(js|css|jpg|jpeg|png|ico|gif|tiff|svg|woff|woff2|ttf|eot|mp4|otf)$/
});
...