Gain Insight into your Users with Twilio Segment and Next.js

Zero brings the greatest value to your team once you integrate with multiple 3rd party APIs. This article adds Twilio Segment analytics to the DigitalOcean Kubernetes + GitHub Actions project we set up in a previous article, with both the Segment and DigitalOcean keys fetched using the Zero secrets manager. A single Zero token is used to fetch both 3rd party API keys, meaning the application only needs to be configured with one secret rather than two — or N, if your application integrates with N APIs!

Segment is a cloud SaaS offering that provides a powerful and flexible way to track how customers use your application. Here's a high level overview of how Segment works:

  1. You instrument your web or mobile app with calls to the Segment SDK to identify the current user and record when that user performs specific actions, like viewing a featured deal or adding a product to their wish list.
  2. The Segment SDK seamlessly uploads the user & event data to the cloud.
  3. You connect Segment to one or more destinations like Salesforce, Intercom, or Google Universal Analytics. This gives your marketing, product, and development teams access to the data they need in the tools they already use.

A sample of the destinations that can be chosen in Segment

This article focuses on Step 1, adding Segment analytics to an application. Step 2 is performed automatically by the Segment SDK, and Step 3 will vary significantly depending on how you intend to use the data.

The full code for this example is available in the zerosecrets/examples GitHub repository.

Adding a Source in Segment

First things first, head over to the Segment website and create an account. When you sign up, you'll be prompted to add a source and a destination. Since we are focused on the data collection part of the process, we only need to add a source. Our Next.js application will send an event to Segment from the server side when the user clicks a button, so select Node.js for the source's type. On the next page, enter a name for the source, like nextjs-docker.

Once the source has been created, you'll be presented with its Write Key. The Write Key should be considered sensitive data since it allows uploading events to your Segment account. As such, we need to store it somewhere secure — like the Zero secrets manager.

A Zero token can store an arbitrary number of secrets, so we'll copy the Write Key into the existing Zero token that we created in the DigitalOcean article. Two APIs, one Zero token. When adding the secret, you can delete the SOURCE_ID key since we will not be using it.

A Zero token with two APIs added

Calling Segment from the Next.js App

Segment has an official Node.js SDK which can be installed by running npm install analytics-node. The SDK can be initialized like so:

1import Analytics from 'analytics-node'
2
3const analytics = new Analytics('your-write-key')

We'll wrap this in a getAnalytics function so that the rest of our application has easy access to Segment:

1export async function getAnalytics() {
2  // Reuse the same Segment client if one has already been created, so that we
3  // don't call Zero on every request
4  if (analytics) {
5    return analytics
6  }
7
8  if (!process.env.ZERO_TOKEN) {
9    throw new Error('Did you forget to set the ZERO_TOKEN environment variable?')
10  }
11
12  const secrets = await zero({
13    token: process.env.ZERO_TOKEN,
14    pick: ['segment'],
15  }).fetch()
16
17  if (!secrets.segment) {
18    throw new Error('Did not receive an API key for Segment.')
19  }
20
21  analytics = new Analytics(secrets.Segment.WRITE_KEY)
22
23  return analytics
24}

Adding an API Route

We want to call Segment from the server side, so let's define a Next.js API Route by creating the file pages/api/sendEvent.js. In this file, we'll tell Segment about the current user and record a fictitious "Purchased Product" event.

1export default async function sendEvent(req, res) {
2  const analytics = await getAnalytics()
3
4  // In a real app, you would only call `identify` when the user is first
5  // created, or when their traits change.
6  analytics.identify({
7    userId: '012345',
8    traits: {
9      name: 'Joe Bloggs',
10      email: 'jbloggs@example.com',
11    },
12  })
13
14  analytics.track({
15    userId: '012345',
16    event: 'Purchased Product',
17    properties: {
18      productName: 'AMD Ryzen 5 2600',
19    },
20  })
21
22  res.status(200).send('Event sent.')
23}

Feel free to add additional traits and properties to these calls to capture more data.

Connecting it to the UI

Let's create a simple UI in front of the API Route to bring it all together.

A button which records a Segment event

When the user clicks the button, we call the API using fetch:

1const [eventSentAt, setEventSentAt] = useState()
2const [error, setError] = useState()
3
4async function sendEvent() {
5  setEventSentAt(undefined)
6  const response = await fetch('/api/sendEvent', {method: 'POST'})
7
8  if (response.ok) {
9    setEventSentAt(new Date())
10    setError(undefined)
11  } else {
12    setError(`Server responded with status code ${response.status}.`)
13  }
14}

eventSentAt and error can be displayed in the body of the page so we know whether it worked, or you can simply use the browser DevTools to verify that the API returned a 200 response.

Testing it Out

For the /api/sendEvent call to succeed, you'll need to pass your Zero token as an environment variable when running the Next.js development server:

1ZERO_TOKEN='...' npm run dev

Now, open the application in your browser and click the button once or twice.

You can verify that Segment is receiving analytics data using Segment's Source Debugger. If it worked, you'll see several IDENTIFY and TRACK calls. Clicking on a call shows the full data, such as our custom productName property.

A button which records a Segment event

Deploying to Kubernetes

If you set up GitHub Actions as shown in the previous article, you can easily deploy a new container image to DigitalOcean Kubernetes by pushing to GitHub. That said, the Segment integration won't work just yet since our container now requires that the Zero token be available as an environment variable.

One approach for getting the Zero token to the container would be to pass the token into docker build as a --build-arg from our GitHub Actions workflow, since the workflow has access to the Zero token. However, this is not the most secure design since the Zero token could be extracted from the container image if it fell into the wrong hands.

A better approach is to use the secrets feature of Kubernetes. To create a zero-token secret in Kubernetes, run:

1kubectl create secret generic zero-token --from-literal=zero-token='YOUR-ZERO-TOKEN'

And to pass that secret to our container as an environment variable, execute:

1kubectl set env --from=secret/zero-token deployment/nextjs-docker

Kubernetes will infer that the environment variable should be named ZERO_TOKEN, so this is all the configuration that's needed. Now you can test out the app in the cloud by navigating to the load balancer's IP in your browser. The IP can be retrieved with

1doctl compute load-balancer list --format Name,Created,IP,Status

If everything is configured properly, you should see more events in the Segment Source Debugger.

💡 If adding secrets to a production Kubernetes cluster, make sure to follow the security recommendations from the Kubernetes documentation.

Wrapping Up

If you're deploying Segment for real, the next step would be to add a destination so that your customer data automatically flows to the analytics, marketing, or data aggregation tool of your choice.

In addition to showing you how to integrate Segment in a Node.js environment, this article showed how the Zero secrets manager streamlines configuration — now your application only needs to know one secret variable instead of many.

In a small startup environment, it's likely fine to consolidate your secrets in a single Zero token which everyone has access to. But as your organization grows, you'll need fine-grained control over who has access to which secrets. In a future blog post, we'll be showcasing Zero's teams feature, a flexible and secure system for controlling access to secrets.

Additional Resources

Author avatar

Sam Magura