How I built Nike’s Ecommerce Features with Medusa

shahednasser

Shahed Nasser

Posted on January 10, 2023

How I built Nike’s Ecommerce Features with Medusa

Nike is the world’s largest supplier and manufacturer of athletic shoes. Part of what makes Nike successful is its ecommerce website. Its net sales has crossed 12 billion USD in 2022. This makes up approximately 65% of Nike’s global direct-to-customer revenue in 2022 (18.73 billion USD).

Nike’s ecommerce website is rich with features that encourage customers to make a purchase. It shows a great understanding of what customers want, and how to convert first-time visitors to loyal customers.

Nike realized early-on that they needed to implement an omnichannel experience to provide their customers with the best user experience. This led them to build their own headless commerce platforms utilizing microservices and cloud technologies.

In this article, you’ll learn how to implement 5 core features Nike offers its customers using Medusa. These are:

  1. Digital Gift Cards
  2. Free shipping for members
  3. Search suggestions and autocomplete
  4. Member-only products
  5. Birthday deals

What is Medusa?

GitHub logo medusajs / medusa

Building blocks for digital commerce

Medusa logo

Medusa

Building blocks for digital commerce

Medusa is released under the MIT license. PRs welcome!

Follow @medusajs Discord Chat

Getting Started

Visit the Quickstart Guide to set up a server.

Visit the Docs to learn more about our system requirements.

What is Medusa

Medusa is a set of commerce modules and tools that allow you to build rich, reliable, and performant commerce applications without reinventing core commerce logic. The modules can be customized and used to build advanced ecommerce stores, marketplaces, or any product that needs foundational commerce primitives. All modules are open-source and freely available on npm.

Learn more about Medusa’s architecture and commerce modules in the Docs.

Roadmap, Upgrades & Plugins

You can view the planned, started and completed features in the Roadmap discussion.

Follow the Upgrade Guides to keep your Medusa project up-to-date.

Check out all available Medusa plugins.

Community & Contributions

The community and core team are available in GitHub Discussions, where you…




Using an open source ecommerce platform gives you many of the benefits of customization that a custom platform like Nike’s has. Medusa is built for developers that wants to build bespoke commerce experiences with a composable commerce platforms in Node.js.

Medusa’s architecture allows businesses and developers to build unique and on-brand experiences. Features such as omnichannel support, scalability, and customizability are at Medusa’s core.

Medusa also provides advanced ecommerce features related to automated RMA flows, sales channels, product and order management, and much more.

Medusa’s integrations system gives businesses more flexibility. You can integrate any third-party service into Medusa for rich CMS functionalities, payment gateways, customer support, and more. You can also create custom integrations specific to your brand.

If you like this article, please give Medusa a 🌟 on GitHub

Feature 1: Digital Gift Cards

Image description

Nike allows customers to customize and purchase gift cards. They can be sent as a gift to an email or can be shipped as an actual card to a shipping address.

Medusa allows creating gift cards with custom images, unlimited amounts, description, tags, and more.

Image description

Merchants can also specify for a specific denomination or amount the price for different currencies.

Image description

Customers that purchase this gift card will have it delivered to their email and can use it during checkout.

You can also customize the experience by allowing customers to deliver the gift card to another person’s email. Gift cards in Medusa (and all entities) have a metadata attribute. This attribute can be used to store custom data.

You can utilize it to store a custom recipient email or shipping address, which would allow customers to send the gift card to their family or friends.

metadata: {
    recipient_email: 'example@gmail.com'
}
Enter fullscreen mode Exit fullscreen mode

Feature 2: Free Shipping for Members

Image description

When taking a look at the features Nike provides, you’ll notice that their main focus is converting visitors and one-time customers to registered customers. This builds customer loyalty and increases the possibility of the customer making future purchases.

One way Nike encourages visitors to register and become members is offering free shipping for every order a member places. This makes registering worth the extra steps a customer have to make before placing an order, as they’ll be saving up on current and future orders.

In Medusa, this feature can be implemented with a combination of available features: Customer Groups, Discounts, and Subscribers.

Customer Groups

Customer groups can be used to group together customers that share a similar set of attributes. Then, you can apply a different set of prices, discounts, and rules for customers belonging to that group.

In this use case, you can use customer groups to create the “Members” customer group. You’ll see later how to add customers to that group automatically on registration.

Image description

Discounts

Discounts can be used to create discount codes that customers can use to save on their order. Discounts can be in the form of fixed discount amount, percentage discount amount, or free shipping.

Discounts can also have conditions. For example, you can specify which products this discount can be used on or which customer groups.

To implement the Free Shipping for Members feature, you can create a discount that gives customers belonging to the customer group “Members” free shipping on their orders.

Image description

Subscribers

The last step required is to automatically:

  1. Add customers to the Members customer group on registration.
  2. Add the free shipping discount to customers of the Members customer group.

In Medusa, important actions trigger an event. Developers can listen or subscribe to events and perform an automated task when an action occurs. They do that using subscribers.

Starting with adding customers to the Members customer group, you need to create a subscriber src/subscribers/members.ts on your Medusa server that subscribes to the customer.created event and adds the customer to the “Members” customer group:

import { CustomerGroupService, CustomerService, EventBusService } from "@medusajs/medusa"

type InjectedProperties = {
  eventBusService: EventBusService
  customerService: CustomerService
  customerGroupService: CustomerGroupService
}

class MembersSubscriber {
  customerService: CustomerService
  customerGroupService: CustomerGroupService

  constructor (container: InjectedProperties) {
    this.customerService = container.customerService
    this.customerGroupService = container.customerGroupService

    container.eventBusService.subscribe('customer.created', this.handleRegistration)
  }

  handleRegistration = async (data) => {
    //get Members customer group
    const membersGroup = await this.customerGroupService.list({
      name: 'Members'
    }, { take: 1 })

    if (!membersGroup.length) {
      //Members group doesn't exist
      return
    }

    //add customer to customer group
    await this.customerGroupService.addCustomers(membersGroup[0].id, data.id)
  }
}

export default MembersSubscriber
Enter fullscreen mode Exit fullscreen mode

Next, you need to create a subscriber that automatically adds the free shipping discount to members. The subscriber should subscribe to the events cart.created and cart.customer_updated. The first event is triggered when a new cart is created, and the second one is triggered whenever the customer associated with the cart is changed.

import { CartService, EventBusService } from "@medusajs/medusa"

type InjectedDependencies = {
  eventBusService: EventBusService
  cartService: CartService
}

class MemberDiscountSubscriber {
  cartService: CartService
  discountCode: string

  constructor (container: InjectedDependencies) {
    this.cartService = container.cartService
    this.discountCode = 'MEMBERSFREESHIP'

    container.eventBusService.subscribe('cart.created', this.handleDiscount)
    container.eventBusService.subscribe('cart.customer_updated', this.handleDiscount)
  }

  handleDiscount = async (data) => {
    const cartId = typeof data === 'string' ? data : data.id

    this.cartService.update(cartId, {
      discounts: [
        {
          code: this.discountCode
        }
      ]
    })
  }
}

export default MemberDiscountSubscriber
Enter fullscreen mode Exit fullscreen mode

Notice that in the code above, the discount code is hardcoded. This behavior can be changed to instead include it as an option or retrieve it in other ways using the DiscountService.

Feature 3: Search Suggestions and Auto Complete

Image description

An essential and often overlooked feature that ecommerce websites must perfect is the search functionalities. Customers generally visit an ecommerce website either exactly knowing what they want, or with an idea of what they want.

Nike does a great job at providing results for both types of customers. The moment the customer starts entering a search query, they can see top query suggestions and the best products that match the query.

If the customer knows what they’re looking for, they’ll find the product in the immediate results. If not, they can use the help of the top suggestions.

In Medusa, advanced search functionalities can be implemented by integrating the server to third-party services like Algolia or MeiliSearch. This removes the need to implement these features from scratch, and allows utilizing best-in-brand services in your store.

Image description

Both Algolia and MeiliSearch provide advanced features such as instantaneous search results, typo tolerance, search suggestions, autocomplete and more. You can integrate one of them by installing the service’s plugin on the Medusa server. Then, you can customize the UI on the storefront as you see fit for your customers.

Feature 4: Member-only Products

Image description

Similar to the free shipping feature, Nike also provides products that are only available for members or registered customers. This further encourages visitors to register and check out exclusive products.

In Medusa, this can be implemented with a combination of features: Customer Groups, Sales Channels, and Subscribers.

Customer Groups

As explained in an earlier section, members can be represented by a Customer Group called “Members”. You would then need to implement automatically assigning the Members customer groups to registered customers, as explained earlier.

Sales Channels

Sales channels allow customizing how and what products you sell for different channels in your store. Channels can be different platforms, such as website or mobile. It can also be different types of customers, such as B2B customers or members.

To implement the Member-only products feature, you need to create a sales channel for Members. Then, you can make only specific products available in that sales channel and not available in the default channel.

Image description

Subscribers

The last step is to create two subscribers that handle the following:

  1. Add customers to the Members customer group on registration.
  2. Assign the Members sales channel to members’ carts.

The first one’s implementation was explained earlier, and you can find the code block in a previous section.

As for the second, you need to create a subscriber that subscribes to the cart.created and the cart.customer_updated events. If the customer associated with the cart belongs to the Members customer group, the Members sales channel is associated with the cart. Otherwise, the default channel is associated with the cart.

import { CartService, EventBusService, SalesChannel, SalesChannelService } from "@medusajs/medusa"

type InjectedDependencies = {
  eventBusService: EventBusService
  cartService: CartService
  salesChannelService: SalesChannelService
}

class MemberDiscountSubscriber {
  cartService: CartService
  salesChannelService: SalesChannelService

  constructor (container: InjectedDependencies) {
    this.cartService = container.cartService
    this.salesChannelService = container.salesChannelService

    container.eventBusService.subscribe('cart.created', this.handleCreate)
    container.eventBusService.subscribe('cart.customer_updated', this.handleCreate)
  }

  handleCreate = async (data) => {
    const cartId = typeof data === 'string' ? data : data.id

    //retrieve default sales channel
    const defaultChannel = await this.salesChannelService.retrieveByName('Default Sales Channel') as SalesChannel

    //retrieve sales channel
    const membersChannel = await this.salesChannelService.retrieveByName('Members') as SalesChannel

    if (!membersChannel) {
      //sales channel does not exist
      return
    }

    //retrieve cart
    const cart = await this.cartService.retrieve(cartId, {
      relations: ['customer', 'customer.groups']
    })

    const isMember = cart.customer?.groups.some((group) => group.name === 'Members')

    await this.cartService.update(cartId, {
      sales_channel_id: isMember ? membersChannel.id : defaultChannel.id
    })
  }
}

export default MemberDiscountSubscriber
Enter fullscreen mode Exit fullscreen mode

Feature 5: Birthday Deals

Image description

Another member benefit that Nike offers is birthday deals. If a member registers before their birthday, they’ll receive a discount on their birthday. Nike emails them with details about the discount.

This can be implemented in Medusa using a combination of features: Customer Groups, and Scheduled Jobs. The usage of customer groups is as explained in earlier sections.

Scheduled Jobs

Medusa allows developers to schedule a task to run at a specific time using scheduled jobs. In this context, a scheduled job can be used to run every day at midnight. Then, it checks which customers’ birthday is set to today. For those customers, an email is sent with the deal details.

You can make use of the metadata field to store custom fields such as birthday. You’ll need to save it when the customer registers or allow the customer to set their birthday in their profile.

const checkBirthdaysJob = async (container, options) => {
  const jobSchedulerService = container.resolve("jobSchedulerService");
  jobSchedulerService.create("check-birthdays", {}, "0 0 * * *", async () => {
    //job to execute
    const customerService = container.resolve("customerService");
    const customers = await this.customerService.list({
      metadata: {
        birthday: Date.now().toString()
      }
    })

        customers.forEach((customer) => {
            //create discount/gift card
            //and send email
        })
  })
}

export default checkBirthdaysJob;
Enter fullscreen mode Exit fullscreen mode

The deal can be implemented either using Discounts or Custom Gift Cards. For the first option, you create a discount with a random code and send it to the customer. For the second option, you create a custom gift card with an amount, and send the gift card’s code to the customer.

To send the email to the customer, you’ll need to integrate a third-party plugin such as SendGrid.

Do More with Medusa

The features explored in this article aim to provide a better user experience for customers. This is what sets big brands like Nike apart and keep their customers loyal.

Although Nike has developed its own headless commerce platform from scratch, using a platform like Medusa can provide a similar amount of features, extensibility, and scalability.

Check out the documentation for more details on what Medusa is and how to get started.

Should you have any issues or questions related to Medusa, then feel free to reach out to the Medusa team via Discord.

💖 💪 🙅 🚩
shahednasser
Shahed Nasser

Posted on January 10, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related