@financial-times/n-ads

Bring ads to your page with the FT specific o-ads configuration

Usage no npm install needed!

<script type="module">
  import financialTimesNAds from 'https://cdn.skypack.dev/@financial-times/n-ads';
</script>

README

@financial-times/n-ads CircleCI

This package provides functionality to display ads and track user behaviour for ft.com.

Table of Contents

1. Install

2. Ads Data

3. Display Ads

4. Permutive

5. Client Side API

6. Server Side API

7. Monitoring

8. Third Party Scripts

9. Develop

10. Releasing

Install

This package is available on npm.

npm install --save @financial-times/n-ads

Ads Data

If you need to get some metadata about the page or user, you can use getAdsData().

How to use

import { getAdsData } from '@financial-times/n-ads'
getAdsData({
  user: true,             // fetch data about current user
  page: {                 // fetch data about current article or stream page
    type: 'article'       // 'article' or 'stream'
    id: 'id-of-the-page'  // id of the stream or article page. Usually found in appContext
  }
}).then(adsData => {
  // Do stuff with the data.
})

Developer note: You can also override the host and version of the ads-api endpoints which this function fetches from by providing a host property. This is useful if you want to test locally.

getAdsData({
  ...,
  host: 'http://local.ft.com:3002/v2'
})

The default host is https://ads-api.ft.com/v2

See Client-side API section for more details

Display Ads

Both client and server side components for displaying ads on a page.

Server side

You can define ad slots and pass data to the client for initialising the ads library

import ReactDOM from 'react-dom/server'
import { Slot, AdsOptionsEmbed } from '@financial-times/n-ads'

app.use(appContextMiddleware)

app.get('/my-page-with-ads', (req, res, next) => {

  const adOptions = {
    appName: res.locals.appContext.appName,
    adUnit: ['ft.com', `articles/${req.articleId}`],
    // page level targeting, added to every ad request on the page
    targeting: {
      user: {},
      article: {}, //or
      stream: {}
    },
    ...
  }

  const slotProps = {
    name: 'leaderboard',
    formatSmall: false,
    formatsLarge: 'SuperLeaderboard,Leaderboard,Responsive',
    formatsExtra: 'Billboard,SuperLeaderboard,Leaderboard,Responsive',
    // slot level targeting, added only for this ad slot
    targeting: {
      specialAdTargeting: true,
      pos: 'top'
    }
  }

  const adHTML = ReactDOM.renderToStaticMarkup(
    <AdsOptionsEmbed options={adOptions} />
    <Slot {...slotProps} />
  )
  res.render(adHTML)
})

Client side

If you haven't defined your ad slots on the server, you will first need to define one or more ad slots on the page. You can learn more about this here

Once you have your ad slots defined in your HTML, you will need to initialise the ads library.

Simple example

This is the minimum you need to get some ads.

import { displayAds } from '@financial-times/n-ads'
import * as flags from '@financial-times/anvil-ui-ft-flags'

const flagsClient = flags.init()
const options = {
  appName: 'my-app'
}
displayAds.init(options, flagsClient);

Pass in the rootid

On ft.com, we will want to pass in a page rootID to each ad call. n-ads exports some utility functions, amongst which a method getRootId() which can be called when initialising the ads.

import { displayAds, adsUtils } from '@financial-times/n-ads'
import * as flags from '@financial-times/anvil-ui-ft-flags'

const flagsClient = flags.init()

// For a pageKit app, we can get information from appContext
const { appName, abTestState } = appContext
const options = {
  appName,
  abTestState,
  rootId: adsUtils.getRootID()
}
// On ft.com we only show ads if the flag is enabled
if(flags.get('ads')) {
  displayAds.init(options, flagsClient);
}

Get ads data first

In most use cases on ft.com and the FT app, we will want to get some data about the user and the page and append this to our ad requests to provide more targeted ads.

import { adsData, displayAds } from '@financial-times/n-ads'
import * as flags from '@financial-times/anvil-ui-ft-flags'

const flagsClient = flags.init()
const { appName, contentId, abTestState } = appContext

if(flags.get('ads')) {
  getAdsData({
    user: true,
    page: {
      type: 'article',
      id: contentId
    }
  }).then(adsData => {
      const options = {
        appName,
        abTestState
        targeting: adsData.targeting,   // for all pages on ft.com
        adUnit: adsData.adUnit,         // for article or stream pages
        smartmatch: adsData.smartmatch  // for article pages
      };
      displayAds.init(options, flagsClient);
  });
}

Get ads data & enable permutive

Here is an example of using n-ads on the ft.com article page. This ties in all parts of n-ads, by first fetching some data, then enabling both the display ads and the behaviour tracking with permutive

import {
  adsData,
  adsUtils,
  displayAds,
  adsPermutive
} from '@financial-times/n-ads'
import * as flags from '@financial-times/anvil-ui-ft-flags'

// Assume pagekit app where appContext is defined.

const flagsClient = flags.init()

if(flags.get('ads')) {
  getAdsData({
    user: true,
    page: {
      type: 'article',  // or 'stream'
      id: appContext.get('contentId')
    }
  }).then(adsData => {
      // initialise ads
      displayAds.init({
        appName: appContext.get('appName'),
        abTestState: appContext.get('abTestState'),
        rootId: getRootId(),
        targeting: adsData.targeting,
        adUnit: adsData.adUnit,
        smartmatch: adsData.smartmatch
      }, flagsClient);

      if (flags.get('AdsPermutive')) {
        // Initialise permutive
        adsPermutive.setup();
        adsPermutive.loadPermutiveScript();

        // Identify the user
        adsPermutive.identifyUser({
          uuid: adsData.metadata.user.uuid,
          spoorId: adsData.metadata.user.spoorId
        })

        // Send a page view event
        const pageView = adsPermutive.fromAdsDataToPageView({
          ...adsData.metadata,
          rootId: getRootID(),
          type: appContext.get('appName')
        });
        adsPermutive.trackPageView(pageView);
      }
  });
}

Options

These are all the options you can pass to displayAds.init() server side via AdsOptionsEmbed

  • appName (required) <String> - Name of the app
  • adUnit: (optional) <Array> - An array to use for the GPT ad unit. Each item in the array will be concatenated with a /. E.g. options.adUnit = ['ft.com', 'UK'] becomes ft.com/UK. If not provided, will default to unclassified.
  • adsLayout: Used for setting the nLayout value in the ad call, useful for ad ops for targeting purposes, to test different types of ads formats and for setting up demos
  • abTestState (optional) <String> - The state of all the mvt tests running on the page. Usually found in appContext for pageKit apps.
  • rootId (optional) <String> - The root id of an ft.com page. You can get this using the getRootId() function.
  • targeting (optional) <Object> - An object containing the metadata from the ads data manager to be used as targeting for the ad calls.
  • sandbox (optional) <Boolean> - Determines wether to load test ads from the origami ad unit in Google Ad Manager
  • disableMonitoring (optional) <Boolean> - When true, n-ads will not send any ads lifecycle metrics to Spoor API.
  • formats (optional) <Object> - Object containing custom ad slot formats. For example:
    {
      MyAdSlot: { sizes: [[970,400], [970,250]] }
    }
    
  • lazyLoadMargins (optional) <Object> - Object containing lazy loading margins to apply below a certain viewport side. For example:
    {
      1000: '10%',  // if the viewport is smaller than 1000px then apply a 10% margin
      400: '5%'     // if the viewport is smaller than 400px then apply a 5% margin
    }
    

Permutive {#permutive}

Permutive is a behaviour tracking tool that is used to segment people based on their activity on the site. See adsPermutive for more details. It is proxied here by n-ads for convenience. Here's an example:

Basic example

import { adsPermutive } from '@financial-times/n-ads';

adsPermutive.setup();
adsPermutive.loadPermutiveScript();
adsPermutive.identifyUser({
  guid: '1234',
  spoorId: '12345'
});
adsPermutive.trackPageView({
  page: {
    type: 'article',
    rootId: '1234'
  }
});

FT.com helper function

For ft.com, there is a helper function that calls all the functions, with some data about the page and user (usually fetched from getAdsData()), type and rootId.

import { adsUtils } from '@financial-times/n-ads';
adsUtils.enablePermutiveFtCom({
  metadata: { user: {...}, page: {...} }, // data from `getAdsData()`
  type: 'article',
  rootId: adsUtils.getRootID()
})

Client-side API

displayAds

import { displayAds } from '@financial-times/n-ads

displayAds.init(options: nAdsOptions, flags: FlagsClient)

This function initalises o-ads, loads the required third party scripts, and scans the page for ad slots to initialise

displayAds.getUserTargeting(userData)

This function takes the user response from the getAdsData() call, and returns an object of key => value pairs that can be added as targeting to an ad call.

displayAds.getArticleTargeting(articleData)

This function takes the article response from the getAdsData() call, and returns an object of key => value pairs that can be added as targeting to an ad call.

displayAds.getSmartmatchTargeting(smartmatch)

This function takes the smartmatch response from the getAdsData() call, and returns an object of key => value pairs that can be added as targeting to an ad call.

displayAds.validateTraffic()

Check weather the request is deemed as valid traffic (using an external script from moat). Talks directly to the global window.googletag object to set an extra targeting parameter m_data = 0 (valid) or 1 (invalid) for every ad call. Filtering of ads is done on the Google Ad Manager side based on these targeting parameters. There is a race condition between displayAds.validateTraffic() and displayAds.init() so the first ad call may not have the right value for the m_data parameter.

adsUtils

import { adsUtils } from '@financial-times/n-ads

adsUtils.getRootId()

Returns the root id of the current ft.com page

adsUtils.enablePermutiveFtCom()

A helper function to run all the necessary functions for loading and sending data to permutive on ft.com pages.

getAdsData(options: { user, page: { type, id }, host }) {#data-manager}

import { getAdsData } from '@financial-times/n-ads

Returns a data object in the following format:

{
  metadata: {
    user: {
      // key -> value user props
    },
    article: {
      // key -> value article page props
    }
    stream: {
      // key -> value stream page props
    }
  },
  adUnit: []  // ad unit for stream or article pages,
  smartmatch: {} // smartmatch object for article pages (if available)
}

adsPermutive

import { adsPermutive } from '@financial-times/n-ads

Exposes all the public functions from adsPermutive

Server-side API

AdsOptionsEmbed

AdsOptionsEmbed is a JSX component that renders a script with the passed options embedded in a json blob, they are then retrieved automatically client side by n-ads

Props

Slot

Slot is a JSX component that renders an Ad slot compatible with o-ads

Props

  • name <String>: Ad slot name
  • formats <Array|String>: Available formats to use
  • targeting <Object>: Slot level targeting object ex: { pos: 'top', mvtGroup: 'specialChristmasAd'...}
  • formatsDefault <Array|String> List of formats to use as a default
  • formatsSmall <Array|String> List of formats to use for small sized screens
  • formatsMedium <Array|String> List of formats to use for medium sized screens
  • formatsLarge <Array|String> List of formats to use for large sized screens
  • formatsExtra <Array|String> List of formats to use for extra large sized screens
  • classNames <Array|String> CSS class names
  • style <String>: Custom CSS styles

presets

:warning: not yet available. Will contain preset Slots props for the most common ads on FT sites such as Leaderboard, mid, right handrail

Monitoring

The following o-ads events will be tracked:

  • page-initialised
  • slot-requested
  • slot-rendered
  • slot-collapsed

This tracking can be disabled through the disableMonitoring option. Each of those o-ads events will be sent as an oTracking event to Spoor API and will contain several time marks as specified in the metrics config object alongside additional properties to identify the associated slot. The events will be sent to Spoor for all users.

Third party scripts

Ads tracking relies on third party scripts to add extra tracking, reporting, counter bot-traffic measure and more.

GPT

It's the client site google library that o-ads is built on top of to load ads on the site

Where? GPT is inserted by o-ads, which is a dependency of this package

Why? It is necessary to load ads

Moat

Moat is an analytics tool that has an invalid traffic functionality, brand safety and extra tracking that google doesn't get

Where? Moat is inserted on the page when nAds.init() is called

Why? As part of our ad offering, these functionalities are required

Permutive

Permutive is the behaviour tracking software that segments users based on their actions on site.

Where? permutive is inserted by @financial-times/adsPermutive if the adsPermutive flag is on

Why? As part of our ad offering, these functionalities are required

Demos

If you are developing on n-ads, you may find it useful to run the demo page. This mocks a consuming app where you would normally import n-ads from.

make demo - run a demo server on http make demo-https - run the demos on https. This is useful if you want to test under https://local.ft.com to make sure there are no cross origin request errors.

There are two demos available:

  • promise-demo.html
  • async-await-demo.html

These are examples of how to implement n-ads using async/await method or promises.

Releasing

In order to release a new version please refer to the page-kit release guidelines