Integrate Segment (Analytics) with Medusa

In this tutorial, you'll learn how to integrate Segment with Medusa to track events and analytics.

When you install a Medusa application, you get a fully-fledged commerce platform with a Framework for customization. Medusa's architecture facilitates integrating third-party services to customize Medusa's infrastructure for your business needs.

To track analytics in your Medusa application, you can integrate Segment, a service that collects analytics from multiple sources and sends them to various destinations. This tutorial will help you set up Segment in your Medusa application and track common events.

Summary#

By following this tutorial, you'll learn how to:

  • Install and set up Medusa.
  • Integrate Segment with your Medusa application.
  • Handle Medusa's order.placed event to track order placements.
  • Track custom events in your Medusa application with Segment.

You can follow this tutorial whether you're new to Medusa or an advanced Medusa developer.

Diagram showcasing the integration of Segment with Medusa

Example Repository
Find the full code of the guide in this repository.

Step 1: Install a Medusa Application#

Start by installing the Medusa application on your machine with the following command:

Terminal
npx create-medusa-app@latest

First, you'll be asked for the project's name. Then, when prompted about installing the Next.js Starter Storefront, choose "Yes."

Afterwards, the installation process will start, which will install the Medusa application in a directory with your project's name and the Next.js Starter Storefront in a separate directory named {project-name}-storefront.

Why is the storefront installed separately? The Medusa application is composed of a headless Node.js server and an admin dashboard. The storefront is installed or custom-built separately and connects to the Medusa application through its REST endpoints, called API routes. Learn more in Medusa's Architecture documentation.

Once the installation finishes successfully, the Medusa Admin dashboard will open with a form to create a new user. Enter the user's credentials and submit the form. Afterwards, you can log in with the new user and explore the dashboard.

Ran into Errors? Check out the troubleshooting guides for help.

Step 2: Create Segment Module Provider#

To integrate third-party services into Medusa, you create a custom module. A module is a reusable package with functionalities related to a single feature or domain.

Medusa's Analytics Module provides an interface to track events in your Medusa application. It delegates the actual tracking to the configured Analytics Module Provider.

In this step, you'll integrate Segment as an Analytics Module Provider. Later, you'll use it to track events in your Medusa application.

Note: Refer to the Modules documentation to learn more about modules in Medusa.

a. Install Segment Node SDK#

Before you create the Segment Module Provider, you'll install the Segment Node SDK to interact with Segment's API.

Run the following command in your Medusa application's directory:

You'll use the SDK in the next steps.

b. Create Module Directory#

A module is created under the src/modules directory of your Medusa application. So, create the directory src/modules/segment.

c. Create Segment Module's Service#

A module has a service that contains its logic. For Analytics Module Providers, the service implements the logic to track events in the third-party service.

To create the service of the Segment Analytics Module Provider, create the file src/modules/segment/service.ts with the following content:

src/modules/segment/service.ts
1import { 2  AbstractAnalyticsProviderService,3  MedusaError,4} from "@medusajs/framework/utils"5import { Analytics } from "@segment/analytics-node"6
7type Options = {8  writeKey: string9}10
11type InjectedDependencies = {}12
13class SegmentAnalyticsProviderService extends AbstractAnalyticsProviderService {14  private client: Analytics15  static identifier = "segment"16
17  constructor(container: InjectedDependencies, options: Options) {18    super()19    if (!options.writeKey) {20      throw new MedusaError(21        MedusaError.Types.INVALID_DATA,22        "Segment write key is required"23      )24    }25    this.client = new Analytics({ writeKey: options.writeKey })26  }27}28
29export default SegmentAnalyticsProviderService

An Analytics Module Provider's service must extend the AbstractAnalyticsProviderService class. It must also have an identifier static property with the unique identifier of the provider.

A module provider's constructor receives two parameters:

  • container: The module's container that contains Framework resources available to the module. In this tutorial, you don't need to resolve any resources.
  • options: Options that are passed to the module provider when it's registered in Medusa's configurations. You define the following option:

In the constructor, you create a Segment client using the Segment Node SDK. You pass the writeKey option to the client.

You'll use this client to implement the service's methods in the next sections.

Note: Refer to the Create Analytics Module Provider guide for detailed information about the methods.

d. Implement identify Method#

The identify method is used to identify a user in Segment. It associates the user's ID with their profile information, such as name and email.

Add the identify method to the SegmentAnalyticsProviderService class:

src/modules/segment/service.ts
1// other imports...2import { ProviderIdentifyAnalyticsEventDTO } from "@medusajs/types"3
4class SegmentAnalyticsProviderService extends AbstractAnalyticsProviderService {5  // ...6  async identify(data: ProviderIdentifyAnalyticsEventDTO): Promise<void> {7    const anonymousId = data.properties && "anonymousId" in data.properties ? 8      data.properties.anonymousId : undefined9    const traits = data.properties && "traits" in data.properties ? 10        data.properties.traits : undefined11
12    if ("group" in data) {13      this.client.group({14        groupId: data.group.id,15        userId: data.actor_id,16        anonymousId,17        traits,18        context: data.properties19      })20    } else {21      this.client.identify({22        userId: data.actor_id,23        anonymousId,24        traits,25        context: data.properties26      })27    }28  }29}

Parameters

The identify method receives an object with the following properties:

  • actor_id: The ID of the user being identified.
  • group: Alternatively, the group being identified. If this property is present, the actor_id is ignored.
  • properties: Additional properties to associate with the user or group. This can include traits like name, email, and so on.
Note: The method receives other parameters, which you can find in the Create Analytics Module Provider guide.

Method Logic

In the method, if the group property is present, you call the group method of the Segment client to identify a group. Otherwise, you call the identify method to identify a user.

For both methods, you extract the anonymousId and traits from the properties object if they are present. You also pass the actor_id as the userId, and group.id for groups.

e. Implement track Method#

The track method is used to track events in Segment. It can track events like order placements, cart updates, and more.

Add the track method to the SegmentAnalyticsProviderService class:

src/modules/segment/service.ts
1// other imports...2import { ProviderTrackAnalyticsEventDTO } from "@medusajs/types"3
4class SegmentAnalyticsProviderService extends AbstractAnalyticsProviderService {5  // ...6  async track(data: ProviderTrackAnalyticsEventDTO): Promise<void> {7    const userId = "group" in data ? 8      data.actor_id || data.group?.id : data.actor_id9    const anonymousId = data.properties && "anonymousId" in data.properties ? 10      data.properties.anonymousId : undefined11
12    if (!userId && !anonymousId) {13      throw new MedusaError(14        MedusaError.Types.INVALID_DATA, 15        `Actor or group ID is required for event ${data.event}`16      )17    }18
19    this.client.track({20      userId,21      anonymousId,22      event: data.event,23      properties: data.properties,24      timestamp: data.properties && "timestamp" in data.properties ? 25        new Date(data.properties.timestamp) : undefined,26    })27  }28}

Parameters

The track method receives an object with the following properties:

  • actor_id: The ID of the user performing the event.
  • group: Alternatively, the group performing the event. If this property is present, the actor_id is ignored.
  • event: The name of the event being tracked.
  • properties: Additional properties associated with the event. This can include details like product ID, order ID, and so on.
Note: The method receives other parameters, which you can find in the Create Analytics Module Provider guide.

Method Logic

In the method, you set the user ID either to the actor or group ID. You also check if the anonymous ID is present in the properties to use it.

Next, you call the track method of the Segment client, passing it the user ID, anonymous ID, event name, properties, and timestamp (if present in the properties).

f. Implement shutdown Method#

The shutdown method is used to gracefully shut down the Segment client when the Medusa application is stopped. It allows you to send all pending events to Segment before the application exits.

Add the following method to the SegmentAnalyticsProviderService class:

src/modules/segment/service.ts
1class SegmentAnalyticsProviderService extends AbstractAnalyticsProviderService {2  // ...3  async shutdown(): Promise<void> {4    await this.client.flush({5      close: true,6    })7  }8}

Method Logic

In the method, you call the flush method of the Segment client with the close option set to true. This method will send all pending events to Segment and close the client connection.

g. Export Module Definition#

You've now finished implementing the necessary methods for the Segment Analytics Module Provider.

The final piece to a module is its definition, which you export in an index.ts file at the module's root directory. This definition tells Medusa the module's details, including its service.

To create the module's definition, create the file src/modules/segment/index.ts with the following content:

src/modules/segment/index.ts
1import SegmentAnalyticsProviderService from "./service"2import { 3  ModuleProvider, 4  Modules,5} from "@medusajs/framework/utils"6
7export default ModuleProvider(Modules.ANALYTICS, {8  services: [SegmentAnalyticsProviderService],9})

You use ModuleProvider from the Modules SDK to create the module provider's definition. It accepts two parameters:

  1. The name of the module that this provider belongs to, which is Modules.ANALYTICS in this case.
  2. An object with a required property services indicating the Module Provider's services.

h. Add Module Provider to Medusa's Configurations#

Once you finish building the module, add it to Medusa's configurations to start using it.

In medusa-config.ts, add a modules property:

medusa-config.ts
1module.exports = defineConfig({2  // ...3  modules: [4    {5      resolve: "@medusajs/medusa/analytics",6      options: {7        providers: [8          {9            resolve: "./src/modules/segment",10            id: "segment",11            options: {12              writeKey: process.env.SEGMENT_WRITE_KEY || "",13            },14          },15        ],16      },17    },18  ],19})

To pass an Analytics Module Provider to the Analytics Module, you add the modules property to the Medusa configuration and pass the Analytics Module in its value.

The Analytics Module accepts a providers option, which is an array of Analytics Module Providers to register. However, you can only register one analytics provider in your Medusa application.

To register the Segment Analytics Module Provider, you add an object to the providers array with the following properties:

  • resolve: The NPM package or path to the module provider. In this case, it's the path to the src/modules/segment directory.
  • id: The ID of the module provider. The Analytics Module Provider is then registered with the ID aly_{identifier}_{id}, where:
    • {identifier}: The identifier static property defined in the Module Provider's service, which is segment in this case.
    • {id}: The ID set in this configuration, which is also segment in this case.
  • options: The options to pass to the module provider. These are the options you defined in the Options interface of the module provider's service.

i. Set Option as Environment Variable#

Next, you'll set the Segment write key as an environment variable.

To retrieve the Segment write key:

  1. Log into your Segment account.
  2. Go to the Connections page and click the "Add More" button next to the "Sources" section.

Add more button in the Connections page

  1. In the "Choose a Source" step, select "Node.js" and click the "Next" button.

Select Node.js as the Source

  1. In the "Connect your Node.js Source" step, enter a name for the source and click the "Create Source" button. This will show you the write key to copy.

Copy the write key

You can skip the next step of testing out the source for now.

Then, add the following environment variable to your .env file:

Code
SEGMENT_WRITE_KEY=123...

Replace 123... with the write key you copied from Segment.

You'll test out the integration as you set up event tracking in the next steps.


Step 3: Track Order Placement Event#

You'll first track the order-placement event, which is triggered natively in the Medusa application.

Medusa's events system allows you to listen to events triggered by the Medusa application and execute custom logic asynchronously in a subscriber.

In the subscriber, you execute functionalities created in workflows. A workflow is a series of actions, called steps, that complete a task.

In this step, you'll create a workflow that tracks the order.placed event in Segment. Then, you'll create a subscriber that listens to this event and executes the workflow.

a. Create Track Event Step#

Before you create the workflow, you'll create a step that tracks an event in Segment. Later, you'll use this step in the workflows that track events, such as the order-placement event.

To create a step, create the file src/workflows/steps/track-event.ts with the following content:

src/workflows/steps/track-event.ts
1import { createStep } from "@medusajs/framework/workflows-sdk"2
3type TrackEventStepInput = {4  event: string5  userId?: string6  properties?: Record<string, unknown>7  timestamp?: Date8}9
10export const trackEventStep = createStep(11  "track-event",12  async (input: TrackEventStepInput, { container }) => {13    const analyticsModuleService = container.resolve(14      "analytics"15    )16
17    if (!input.userId) {18      // generate a random user id19      input.properties = {20        ...input.properties,21        anonymousId: Math.random().toString(36).substring(2, 15) + 22          Math.random().toString(36).substring(2, 15),23      }24    }25
26    await analyticsModuleService.track({27      event: input.event,28      actor_id: input.userId,29      properties: input.properties,30    })31  }32)

You create a step with createStep from the Workflows SDK. It accepts two parameters:

  1. The step's unique name, which is track-event.
  2. An async function that receives two parameters:
    • The step's input, which is in this case an object with the following properties:
      • event: The name of the event to track.
      • userId: The ID of the user performing the event.
      • properties: Additional properties associated with the event.
      • timestamp: The timestamp of the event (optional).
    • An object that has properties including the Medusa container, which is a registry of Framework and commerce tools that you can access in the step.
Note: The Medusa container is different from the module's container. Since modules are isolated, they each have a container with their resources. Refer to the Module Container documentation for more information.

In the step function, you resolve the Analytics Module's service from the Medusa container. This service is the interface to track events with the configured Analytics Module Provider, which is Segment in this case.

If the userId is not provided, you generate a random anonymous ID and add it to the properties. This is useful for tracking events from users who are not logged in.

Finally, you call the track method of the Analytics Module's service, passing it the event name, user ID, and properties.

b. Create Track Order Placed Workflow#

Next, you'll create the workflow that tracks the order placement event.

To create the workflow, create the file src/workflows/track-order-placed.ts with the following content:

src/workflows/track-order-placed.ts
1import { 2  createWorkflow,3  transform,4} from "@medusajs/framework/workflows-sdk"5import { useQueryGraphStep } from "@medusajs/medusa/core-flows"6import { trackEventStep } from "./steps/track-event"7
8type WorkflowInput = {9  id: string10}11
12export const trackOrderPlacedWorkflow = createWorkflow(13  "track-order-placed",14  ({ id }: WorkflowInput) => {15    // @ts-ignore16    const { data: orders } = useQueryGraphStep({17      entity: "order",18      fields: [19        "id",20        "email",21        "total",22        "currency_code",23        "items.*",24        "customer.id",25        "customer.email",26        "customer.first_name",27        "customer.last_name",28        "created_at",29      ],30      filters: {31        id,32      },33    })34
35    const order = transform({36      order: orders[0],37    }, ({ order }) => ({38      orderId: order.id,39      email: order.email,40      total: order.total,41      currency: order.currency_code,42      items: order.items?.map((item) => ({43        id: item?.id,44        title: item?.title,45        quantity: item?.quantity,46        variant: item?.variant,47        unit_price: item?.unit_price,48      })),49      customer: {50        id: order.customer?.id,51        email: order.customer?.email,52        firstName: order.customer?.first_name,53        lastName: order.customer?.last_name,54      },55      timestamp: order.created_at,56    }))57
58    trackEventStep({59      event: "order.placed",60      userId: order.customer?.id,61      properties: order,62    })63  }64)

You create a workflow using createWorkflow from the Workflows SDK. It accepts the workflow's unique name as a first parameter.

It accepts as a second parameter a constructor function, which is the workflow's implementation. The function can accept input, which in this case an object holding the ID of the order placed.

In the workflow's constructor function, you:

  1. Retrieve the Medusa order using the useQueryGraphStep helper step. This step uses Medusa's Query tool to retrieve data across modules. You pass it the order ID to retrieve.
  2. Use transform to prepare the tracking data, as direct data and variable manipulation isn't allowed in workflows. Learn more in the Data Manipulation documentation.
  3. Send the tracking event to Segment using the trackEventStep you created in the previous step.

You now have the workflow that tracks the order placement event.

Note: Refer to the Workflows documentation to learn more about workflows and steps.

c. Handle order.placed Event#

Next, you'll create a subscriber that listens to the order.placed event and executes the workflow you created in the previous step.

To create the subscriber, create the file src/subscribers/order-placed.ts with the following content:

src/subscribers/order-placed.ts
1import { SubscriberArgs, type SubscriberConfig } from "@medusajs/framework"2import { trackOrderPlacedWorkflow } from "../workflows/track-order-placed"3
4export default async function orderPlacedHandler({5  event: { data },6  container,7}: SubscriberArgs<{ id: string }>) {8  await trackOrderPlacedWorkflow(container)9    .run({10      input: {11        id: data.id,12      },13    })14}15
16export const config: SubscriberConfig = {17  event: "order.placed",18}

A subscriber file must export:

  1. An asynchronous function, which is the subscriber that is executed when the event is emitted.
  2. A configuration object that holds the name of the event that the subscriber listens to, which is order.placed in this case.

The subscriber function receives an object as a parameter that has the following properties:

  • event: An object that holds the event's data payload. The payload of the order.placed event is the ID of the order placed.
  • container: The Medusa container to access the Framework and commerce tools.

In the subscriber function, you execute the trackOrderPlacedWorkflow by invoking it, passing the Medusa container as a parameter. Then, you chain a run method, passing it the order ID from the event's data payload as input.

Note: Refer to the Events and Subscribers documentation to learn more about creating subscribers.

Test it Out#

You'll now test out the segment integration by placing an order using the Next.js Starter Storefront.

Reminder: 

The Next.js Starter Storefront was installed in a separate directory from Medusa. The directory's name is {your-project}-storefront.

So, if your Medusa application's directory is medusa-segment, you can find the storefront by going back to the parent directory and changing to the medusa-segment-storefront directory:

Terminal
cd ../medusa-segment-storefront # change based on your project name

First, run the following command in your Medusa application's directory to start the Medusa server:

Then, run the following command in your Next.js Starter Storefront's directory to start the storefront:

In the storefront, add a product to the cart and proceed to checkout. Once you place the order, open the Segment dashboard to view the order event:

  1. Go to Connections > Sources.
  2. Click on the Node.js source you created earlier.
  3. Click on the "Debugger" tab at the top of the page.
  4. You should see the order.placed event with the order details.
Note: The event may take a few seconds to appear in the debugger.

Order Placed Event in Segment Debugger


Track Custom Event#

In your Medusa application, you often need to track custom events that are relevant to your business use case. For example, a B2B business may want to track whenever a user requests a quote.

In Medusa, you can emit custom events in your workflows when an action occurs. Then, you can create a subscriber that listens to the custom event and executes a workflow to track it in Segment.

For example, if you have a createQuoteWorkflow, you can use Medusa's emitEventStep to emit a custom event after the quote is created:

src/workflows/create-quote.ts
1import { 2  createWorkflow,3} from "@medusajs/framework/workflows-sdk"4import {5  emitEventStep,6} from "@medusajs/medusa/core-flows"7
8const createQuoteWorkflow = createWorkflow(9  "create-quote",10  () => {11    // ...12
13    emitEventStep({14      eventName: "quote.created",15      data: {16        id: "123",17        // other data payload18      },19    })20  }21)

You can then create a subscriber that listens to the quote.created event and executes a workflow to track it in Segment:

src/subscribers/quote-created.ts
1import { SubscriberArgs, type SubscriberConfig } from "@medusajs/framework"2import { trackQuoteWorkflow } from "../workflows/track-order-placed"3
4export default async function orderPlacedHandler({5  event: { data },6  container,7}: SubscriberArgs<{ id: string }>) {8  await trackQuoteWorkflow(container)9    .run({10      input: {11        id: data.id,12      },13    })14}15
16export const config: SubscriberConfig = {17  event: "quote.created",18}

The above example assumes you have a trackQuoteWorkflow that tracks the quote creation event in Segment, similar to the trackOrderPlacedWorkflow you created earlier.


Next Steps#

You've now integrated Segment with your Medusa application and tracked common events like order placement. You can expand on the features in this tutorial to:

  • Track more events in your Medusa application, such as user sign-ups, cart additions, and more. You can refer to the Events Reference for a full list of events emitted by Medusa.
  • Emit custom events that are relevant for your business use case, and track them in Segment.
  • Add destinations to Segment to benefit from the data collected. Segment supports various destinations, such as Google Analytics, Metabase, and more.

If you're new to Medusa, check out the main documentation, where you'll get a more in-depth understanding of all the concepts you've used in this guide and more.

To learn more about the commerce features that Medusa provides, check out Medusa's Commerce Modules.

Troubleshooting#

If you encounter issues during your development, check out the troubleshooting guides.

Getting Help#

If you encounter issues not covered in the troubleshooting guides:

  1. Visit the Medusa GitHub repository to report issues or ask questions.
  2. Join the Medusa Discord community for real-time support from community members.
  3. Contact the sales team to get help from the Medusa team.
Was this page helpful?
Ask Anything
FAQ
What is Medusa?
How can I create a module?
How can I create a data model?
How do I create a workflow?
How can I extend a data model in the Product Module?
Recipes
How do I build a marketplace with Medusa?
How do I build digital products with Medusa?
How do I build subscription-based purchases with Medusa?
What other recipes are available in the Medusa documentation?
Chat is cleared on refresh
Line break