Editing the Smartserve Prescript

The smartserve prescript can be used to run some JavaScript before any data collection or experience activation begins.

It allows you to implement a wide range of bespoke use cases, tailored to your specific requirements. Examples include:

  • Tracking experience shown events in your third party analytics solution
  • Preventing smartserve from firing based on specific conditions (e.g. a cookie value or URL parameter)
  • Synchronizing Qubit segment membership changes to a third party
  • Setting up a QProtocol data layer mapper

TIP: You can manage your script through the Qubit CLI or our in-app editor.

Pulling/pushing changes

Start by cloning the latest from the Qubit platform. If you know the property Id, you can provide it, otherwise, you will be prompted for the name of the property:

$ qubit pre clone
$ qubit pre clone 2499

This will create a folder named pre-{property-id} in your current directory, and will pull down two files: pre.js and package.json.

If you already have a prescript cloned locally, you can instead use the pull command to ensure you have the latest remote code:

$ cd {pre-folder}
$ qubit pre pull

You can now make changes the two prescript files. Once you are happy with them, you can check the difference between your local version and the remote draft, before pushing the changes up:

$ qubit pre diff
$ qubit pre push

Testing and previewing the draft

We maintain two revisions of the prescript: a draft version and a live version. When you push changes up, they will update the draft. You can test the draft out by using the preview version of smartserve. Simply preview any experience, and the draft pre script will be included.

Publishing the draft

Before you publish the draft, you might want to compare the two revisions to double check the code that is going live. To do this, make sure you have got the latest draft locally, and then use the diff command to compare to the remote:

$ qubit pre pull
$ qubit pre diff remote

If you are happy, then you can publish the draft. When you do, you will asked to enter a changelog—try and be descriptive!:

$ qubit pre publish

This will replace the live version with the draft, and a new draft will be created for future changes. The smartserve file will be republished, causing the new code to go live. As with experiences, it can take up to 10 minutes for this to take effect due to CDN cacheing.

API reference

Similar to experiences, the prescript is given an options API containing metadata and utility methods.

meta

The options.meta object contains a bunch of useful information about the smartserve file:

module.exports = function pre (options) {
  console.log(options.meta)
}

Attribute

Description

trackingId

The tracking id of the property on which the experience is executing

isPreview

Whether the variant is executing in preview mode (can be useful e.g. if you want to avoid sending data while previewing)

vertical

A code representing your industry sector. This affects what events you should send and their prefix

namespace

The value of your configured namespace if you have one. This allows you to define a custom schema

currency

The currency code of your property

dataLocation

The region that data is stored in for your property

uv

This is the interface for the Qubit QProtocol data layer. It allows you to both emit events—useful if you are building a mapper, or listen for events in order to sync them to a third party. More documentation on this can be found on GitHub.

module.exports = function pre (options) {
  options.uv.emit('ecView', {
    type: 'home'
  })

  options.uv.on('qubit.experience', (event) => {
    console.log(event)
  })
}

log

A stylish logger, useful for development. See https://github.com/QubitProducts/driftwood for more info:

module.exports = function pre (options) {
  options.log.info('Hello from pre script!')
}

on

Not to be confused with the options.uv API, this method allows you to listen for specific events that happen during the loading of smartserve. Because the prescript runs before data collection and experience activation begins, the various APIs for accessing things like segments and visitor data are not available yet. Therefore, if you need those things, for syncing with a third party, for example, you need to wait for them to be available. There are currently two events available - visitorReady and segmentsReady.

visitorReady

Fires when Qubit's visitor API is ready, allowing you to get the latest state and identifier for the current visitor:

module.exports = function pre (options) {
  options.on('visitorReady', (visitor) => {
    // Get Qubit's visitor ID
    console.log(visitor.visitorId)

    // Get the latest state for the visitor, including geolocation, IP address and aggregated metrics
    visitor.getVisitorState().then((state) => {
      console.log(state)
    })

    // Get the latest state of the browser for this visitor
    visitor.getBrowserState().then((state) => {
      console.log(state)
    })
  })
}

segmentsReady

Fires when Qubit's Segmentation Engine is ready, giving you the ability to see what segments someone is in, and subscribe to changes in membership:

module.exports = function pre (options) {
  options.on('segmentsReady', (segments) => {
    // Get the list of Qubit segment IDs that the visitor is a member of
    segments.getMemberships().then((segments) => {
      console.log(segments)
    })

    // Check if the visitor is a member of a specific segment
    segments.isMemberOf('SG-2499-a932kssks').then((isMember) => {
      if (isMember) {
        console.log('I\'m a member!')
      } else {
        console.log('I\'m not a member :(')
      }
    })

    // Get notified every time the visitors segment memberships change. This can also be achieved by
    // listening for `qubit.segmentMembershipChanged` events, however using this API gives you more
    // data.
    segments.onMembershipsChanged((payload) => {
      // `changeset` tells you which segments the user joined and left
      console.log(payload.changeset)
      // `segments` tells you which segments the user has remained in
      console.log(payload.segments)
    })
  })
}

Code examples

Send experience events to a third party

/* pre.js */
module.exports = function pre (options) {
  options.uv.on('qubit.experience', (payload) => {
    window.fooAnalytics.track('qubitExperienceFired', payload)
  })

  options.uv.on('qubit.goalAchived', (payload) => {
    window.fooAnalytics.track('qubitGoalAchieved', payload)
  })
}

Send custom experience and variation labels to third-party integrations such as Google Analytics

module.exports = function preScript (options) {
  const ga = window.ga
  options.uv.on('qubit.experience', (event) => {
    if (!ga) return
    ga('send', {
      hitType: 'event',
      eventCategory: 'Qubit Experience',
      eventAction: `Experience ${event.experienceId}`,
      eventLabel: `Variation ${event.variationMasterId}`,
      nonInteraction: true
    })
  })

Sync qubit membership changes to third party

/* pre.js */
module.exports = function pre (options) {
  options.on('segmentsReady', (segments) => {
    window.syncSegments(segments.getMemberships())
    segments.onMembershipsChanged((payload) => window.syncSegments(payload.segments))
  })
}

Do something with visitor state

/* pre.js */
module.exports = function pre (options) {
  options.on('visitorReady', (visitor) => {
    console.log(visitor.visitorId)
    visitor.getVisitorState().then((state) => console.log(state))
  })
}

Prevent smartserve from running - sync

INFO: Returning an object that includes execute with the value false will prevent smartserve from running.

/* pre.js */
var cm = require('cookieman')

module.exports = function pre (options) {
  return {
    execute: cm.get('country') === 'gb'
  }
}

/* package.json */
{
  "dependencies": {
    "cookieman": "*"
  }
}

Prevent smartserve from running - async

INFO: Returning an object that includes execute with the value false will prevent smartserve from running.

/* pre.js */
var getStuff = require('my-stuff-module')

module.exports = function pre (options) {
  return getStuff().then((data) => ({
    execute: data.thing === 'foo'
  }))
}

/* package.json */
{
  "dependencies": {
    "my-stuff-module": "*"
  }
}
Last updated: September 2021
Did you find this article useful?