Customer Data Platform

An introduction to Segment middleware for Analytics.js

An introduction to middleware for Segment's Javascript SDK. This blog explains how middleware can be used to modify or customize the payloads of events in a scalable and efficient way.

Apr 25, 2023

By Glenn Vanderlinden, Kalyan Kola Cahill


Written in conjunction with Human37, a Segment and Mixpanel business partner, this article will provide both a theoretical and a practical introduction to middleware for Segment’s Javascript SDK. This article is intended for teams implementing Segment that are looking for a way to modify or customize the payloads of the events in a very efficient way.

The 1960s were a time of great innovation and exploration. Humans landed on the moon, Mary Van Brittan Brown invented the first home security system, and Middleware changed the way engineers work across applications in software.

The legacy of all three continues today. But this is a software blog, so let’s focus on that last one. Middleware allows for one or more kinds of communication between applications. Often called “software glue”, it enables easier connection between applications that weren’t initially designed to connect. When connected, middleware can make application development more efficient and speeds time to market. 

What is Segment Middleware?

In relation to Segment, Middlewares allow developers to extend Analytics.js with custom code which runs on every event. This code has full access to the DOM and Browser API, and helps customers enrich and transform event payloads.

Let’s dissect this definition:

  1. Middlewares allow developers to extend Analytics.js with custom code which runs on every event — If we combine this with the general concept that middleware is ‘software glue’, it means that the code can be controlled and customised by developers. Moreover middleware is scalable — it can be defined once and then reused multiple times. The purpose is to define it once after which all events that follow will be modified according to the middleware’s specifications. This means we can load middleware when the Segment SDK loads, and impact the code for all events (track, identify, page, group) that follow.

  2. Middleware code has full access to the DOM and Browser API, and helps customers enrich and transform event payloads — If something is missing in the payload but available in the DOM or browser API it can be added using middleware. For example, adding screensize to the Segment payload which isn’t included by default. We’ll explore that later in one of our examples.

It’s important to note that Segment Middleware and Plug-ins are not the same thing. While Middleware allows for more basic modifications, plug-ins allow for more advanced use cases, particularly when timeline control is of importance. More information on plug-ins can be found here.

Core Middleware use cases 

To understand how middleware can help, we need to share the difference between (generic) source middleware and destination (specific) middleware.

  • Source middleware can modify the payload of any or all events (including identify, track, page, and group) for a source. As a result, the payload is adapted for all destinations linked to that source. Hence the soft reference that we’re making to generic middleware.

  • Destination middleware can modify the payload of any or all events (including identify, track, page, and group) for a specific destination. As a result, the payload is adapted only for the destination that is specified in the middleware. The rest of the destinations receive the standard payload. 

Some important considerations to take into account:

  • Destination middleware only works if the destination is deployed in device-mode. For more information on the distinction between cloud and device-mode, check out this documentation.

  • Even though the documentation specifies a difference between source middleware and destination middleware, the middleware functions are the same for both source and destination functions. The difference lies in the functions used to add the middleware and the arguments that these can take. We’ll explore this in the next section. 

Here are a few use cases for using middleware with Segment. You may want to:

  • Add screen size to every behavioral payload for a specific source to have this available in the data warehouse for further analysis

  • Access specific consent states or preferences and add them to the payload

  • Add specific parameters for a specific destination (e.g. Amplitude’s sessionId)

  • Strip elements from the payload for a specific destination

A practical example

Every Segment Javascript implementation starts with applying the JavaScript snippet in the <head> tag of your website. This base implementation, as suggested in the Segment UI upon creation your Javascript source, looks as follows:

<!--SEGMENT CLIENT SIDE SNIPPET--> <script> !function () { var analytics = window.analytics = window.analytics || []; if (!analytics.initialize) if (analytics.invoked) window.console && console.error && console.error("Segment snippet included twice."); else { analytics.invoked = !0; analytics.methods = ["trackSubmit", "trackClick", "trackLink", "trackForm", "pageview", "identify", "reset", "group", "track", "ready", "alias", "debug", "page", "once", "off", "on", "addSourceMiddleware", "addIntegrationMiddleware", "setAnonymousId", "addDestinationMiddleware"]; analytics.factory = function (e) { return function () { var t = Array.prototype.slice.call(arguments); t.unshift(e); analytics.push(t); return analytics } }; for (var e = 0; e < analytics.methods.length; e++) { var key = analytics.methods[e]; analytics[key] = analytics.factory(key) } analytics.load = function (key, e) { var t = document.createElement("script"); t.type = "text/javascript"; t.async = !0; t.src = "https://cdn.segment.com/analytics.js/v1/" + key + "/analytics.min.js"; var n = document.getElementsByTagName("script")[0]; n.parentNode.insertBefore(t, n); analytics._loadOptions = e }; analytics._writeKey = "YOUR_WRITE_KEY";; analytics.SNIPPET_VERSION = "4.15.3"; analytics.load("YOUR_WRITE_KEY"); analytics.page(); } }(); </script>

Let’s assume we wish to ensure the screen size is added to the Segment payload for every method. While Segment captures a number of contextual fields, such as IP address, device information, and user-agent, there may be additional contextual data you wish to capture that is not included out-of-the-box. One such example could be screen resolution for web users.

(At this stage we’re not concerned that we're deploying source or destination middleware. The first step is to build middleware which isn’t impacted by that. But we’ll dive into this distinction shortly.)

The following piece of code represents a middleware that we’ve called SMScreensize.

It takes 3 arguments as described in the Segment documentation:

  • Payload — represents the event payload sent by Analytics.js. To change the value of the payload, mutate the payload.obj object. (See code snippet below.)

  • Next — represents the next function to be called in the destination middleware chain. If the middleware provided does not call this function, then the event is dropped completely for the given destination.

  • Integration — is a string value representing the integration that this middleware is applied to. 

It’s important to note that the third argument — integration — is part of the middleware function but will only be of use when we use this in the context of a destination middleware solution.

var SMScreensize = function ({ payload, next, integrations }) { payload.obj.context.screensize = `${window.screen.width}x${window.screen.height}`; next(payload); };

The middleware snippet can be added right before the analytics.load statement. 

Now that the middleware has been defined, it’s time to add the middleware to the analytics.js execution chain. This is where the distinction between source middleware and destination middleware becomes important.

In this case, building a source middleware will result in the screen size being available in every payload. This means:

  • All destinations deployed in device-mode can make use of the screen size property.

  • All cloud-mode destinations can make use of the screen size property as Segment receives an adapted payload based on the middleware which in return is used to build the payloads for cloud-mode destinations.

To add the source middleware, we use the following code snippet:

analytics.addSourceMiddleware(SMScreensize);

This can be added right after defining the middleware but before the analytics.load command. 

If we decided to deploy a destination source middleware instead, for example, only Google Analytics to receive an adapted payload, we would deploy the following snippet:

analytics.addDestinationMiddleware('Google Analytics 4 Web', SMScreensize);

The full adapted code to deploy a source middleware would look like this:

<!--SEGMENT CLIENT SIDE SNIPPET--> <script> !function () { var analytics = window.analytics = window.analytics || []; if (!analytics.initialize) if (analytics.invoked) window.console && console.error && console.error("Segment snippet included twice."); else { analytics.invoked = !0; analytics.methods = ["trackSubmit", "trackClick", "trackLink", "trackForm", "pageview", "identify", "reset", "group", "track", "ready", "alias", "debug", "page", "once", "off", "on", "addSourceMiddleware", "addIntegrationMiddleware", "setAnonymousId", "addDestinationMiddleware"]; analytics.factory = function (e) { return function () { var t = Array.prototype.slice.call(arguments); t.unshift(e); analytics.push(t); return analytics } }; for (var e = 0; e < analytics.methods.length; e++) { var key = analytics.methods[e]; analytics[key] = analytics.factory(key) } analytics.load = function (key, e) { var t = document.createElement("script"); t.type = "text/javascript"; t.async = !0; t.src = "https://cdn.segment.com/analytics.js/v1/" + key + "/analytics.min.js"; var n = document.getElementsByTagName("script")[0]; n.parentNode.insertBefore(t, n); analytics._loadOptions = e }; analytics._writeKey = "YOUR_WRITE_KEY";; analytics.SNIPPET_VERSION = "4.15.3"; /*Segment Middleware*/ /*Segment Middleware Screensize*/ var SMScreensize = function ({ payload, next, integrations }) { payload.obj.context.screensize = `${window.screen.width}x${window.screen.height}`; next(payload); }; analytics.addSourceMiddleware(SMScreensize); /*Segment SDK load*/ analytics.load("YOUR_WRITE_KEY"); analytics.page(); } }(); </script>

The result of this implementation is that the screen size is added in the context object.

image1

Conclusion

Middleware is a lightweight but powerful way to adapt your payload for all destinations or for a selection of specific destinations. Developing the middleware functionality is agnostic to the fact if it will be used as a source or destination middleware. The difference lies in the function that is used to add your middleware to the execution chain where the destination middleware can take an additional argument to specify the destination to which the middleware applies.

The State of Personalization 2023

Our annual look at how attitudes, preferences, and experiences with personalization have evolved over the past year.

Get the report
SOP 2023

The State of Personalization 2023

Our annual look at how attitudes, preferences, and experiences with personalization have evolved over the past year.

Get the report
SOP 2023

Share article

Want to keep updated on Segment launches, events, and updates?

We’ll share a copy of this guide and send you content and updates about Twilio Segment’s products as we continue to build the world’s leading CDP. We use your information according to our privacy policy. You can update your preferences at any time.