How I Built a Video Game Store with Medusa, Next.js, Stripe, and Algolia

How I Built a Video Game Store with Medusa, Next.js, Stripe, and Algolia

Medusa is a Node.js-based, open-source, ecommerce platform. Medusa allows developers to create scalable and sophisticated commerce setups with minimal effort and a positive developer experience. It is an open-source alternative to Shopify with a fantastic developer experience and limitless customization options for merchants looking to scale.

Medusa has essential ecommerce features such as orders, exchanges, returns, and much more. All of these features set Medusa apart from other headless Commerce CMS.

Earlier this month, Medusa has launched a hackathon offering contributors the opportunity to win excellent prizes such as free swag, great project opportunities, and a $1,500 cash prize. This is an example of a project you can build to participate in the hackathon.

In this tutorial, you’ll learn how to create a video store with Medusa using its Next.js storefront. You’ll be integrating with it Stripe and Algolia.

You can check out the full code in this GitHub repository.

video-storefront-demo.png

Prerequisites

Before you begin this tutorial, ensure you have the following requirements:

  • Node.js version 14 or later.
  • A Stripe account.
  • An Algolia account.
  • (Optional) MinIO is used as a file service plugin with Medusa. If you plan to use it as well, you must have MinIO installed and running.
  • Yarn is used throughout this tutorial. You can also use NPM instead.

Create a Medusa Server

With the above requirements, install the Medusa CLI tool by running the command below:

yarn global add @medusajs/medusa-cli

Then, create a Medusa application with the command below:

yarn create medusa-app

The above command will prompt you to choose the installation folder for this application. Enter gamestore as the project folder name, select medusa-starter-default as the starter for this project, and select Next.js Starter as the storefront starter to install.

Your selections should look like the ones in the screenshot below:

Untitled.png

Now, wait while Medusa creates the necessary folders and files to run the application. Once the installation is completed, Medusa will create three main folders in the gamestore folder: admin, backend, and storefront.

Next, open three different terminal tabs. Change the directory into these folders, and start each part of the application with the following commands. Run each command in its respective terminal simultaneously:

# Medusa server
cd gamestore/backend
yarn start

# Admin
cd gamestore/admin
yarn start

# Storefront
cd gamestore/storefront
yarn dev

Install Algolia Plugin

Algolia is a search engine service that enables developers to integrate advanced search features such as typo tolerance, recommended results, and quick responses into their websites.

Before you install the plugin, follow the steps here to get your Algolia Application ID, Search-Only API Key, and Admin API Key.

Then, install the Algolia plugin by running the command below in your Medusa backend root folder:

yarn add medusa-plugin-algolia

And add the following environment variables to your Medusa server:

ALGOLIA_APP_ID=<YOUR_APP_ID>
ALGOLIA_ADMIN_API_KEY=<YOUR_ADMIN_API_KEY>

<YOUR_APP_ID> and <YOUR_ADMIN_API_KEY> are the Application ID and Admin API keys respectively.

Finally, in medusa-config.js, add the following item into the plugins array:

const plugins = [
  //...
  {
    resolve: `medusa-plugin-algolia`,
    options: {
      application_id: process.env.ALGOLIA_APP_ID,
      admin_api_key: process.env.ALGOLIA_ADMIN_API_KEY,
      settings: {
        products: {
          searchableAttributes: ["title", "description"],
          attributesToRetrieve: [
            "id",
            "title",
            "description",
            "handle",
            "thumbnail",
            "variants",
            "variant_sku",
            "options",
            "collection_title",
            "collection_handle",
            "images",
          ],
        },
      },
    },
  },
];

The searchableAttributes are attributes that can be searched in a product, and attributesToRetrieve are the product attributes that should be returned in the search result.

Install the Stripe Plugin

Stripe is a tested-and-trusted transaction processing platform. Stripe provides you with all of the technical components required to handle transactions safely and the analytical features required to gain insight into your sales.

Install the Stripe Plugin with the command below:

yarn add medusa-payment-stripe

Next, add configurations for your Stripe plugin in the backend/medusa-config.js file:

const plugins = [
//...
  resolve: `medusa-payment-stripe`,
    options: {
      api_key: STRIPE_API_KEY,
      webhook_secret: STRIPE_WEBHOOK_SECRET,
    },
   },
]

Finally, add in the backend/.env file your api_key and webhook_secret keys to the STRIPE_API_KEY and STRIPE_WEBHOOK_SECRET environment variables.

STRIPE_API_KEY=<YOUR STRIPE_API_KEY>
STRIPE_WEBHOOK_SECRET=<YOUR STRIPE_WEBHOOK_SECRET>

Install File Service Plugin (MinIO)

MinIO is an open-source distributed object storage server written in Go that is intended for private cloud infrastructure and provides S3 storage functionality.

Medusa uses file service plugins to store files such as product images. It’s important to add a file service plugin to create products. You can use MinIO, S3, or DigitalOcean Spaces.

Install the MinIO plugin module with the command below:

yarn add medusa-file-minio

Next, open your backend/.env file, and add the following credentials:

MINIO_ENDPOINT=<ENDPOINT>
MINIO_BUCKET=<BUCKET>
MINIO_ACCESS_KEY=<ACCESS_KEY>
MINIO_SECRET_KEY=<SECRET_KEY>

<ENDPOINT> is the URL of your MinIO server, <BUCKET> is the name of your MinIO bucket, and <ACCESS_KEY> and <SECRET_KEY> are your access and secret keys.

Finally, add the following configurations to the array of plugins in the backend/medusa-config.js file:

const plugins = [
//...
  {
    resolve: `medusa-file-minio`,
    options: {
        endpoint: process.env.MINIO_ENDPOINT,
        bucket: process.env.MINIO_BUCKET,
        access_key_id: process.env.MINIO_ACCESS_KEY,
        secret_access_key: process.env.MINIO_SECRET_KEY,
    },
  },
]

Add Products Using Medusa Admin

The Medusa Admin is an application connected to the Medusa Server to allow you to manage the data in your applications. Here you’ll add some products to the store from the Medusa admin.

While your Medusa server is running, run the Medusa admin if it’s not already running:

yarn start

Then, navigate to http://localhost:7000 to view the Medusa Admin. You'll see the a login screen.

Untitled 1.png

Medusa seeded your database with a demo admin that you can login with. Use the following credentials to login:

  • Email: admin@medusa-test.com
  • Password: supersecret

Once logged in, you should have access to the Medusa dashboard where you can manage your application content (create products, manage orders, and more).

Untitled 2.png

On the product page, click the “Add New Product” button **to add some video game products. You can add as many as you want.

Untitled 3.png

Customize the Next.js Storefront

Now that we have added and created some video games in our admin, let’s customize the storefront to look like a games store application.

To do that, locate the storefront **directory and modify the following files.

You can download the images used in the storefront here on Github.

  • storefront/src/modules/home/components/featured-products/index.tsx with the code snippet below:
...
const FeaturedProducts = () => {
  ...
  return (
    <div className="py-12">
      <div className="content-container py-12">
        <div className="flex flex-col items-center text-center mb-16">
          <span className="text-base-regular text-gray-600 mb-6">
            Trending On Funplay
          </span>
          <p className="text-2xl-regular text-gray-900 max-w-lg mb-4">
            Top 4 games this week
          </p>
          <UnderlineLink href="/store">Start Playing Now</UnderlineLink>
        </div>
          ...
      </div>
    </div>
  )
}
..

This changes title of the featured products component as well as some of the text to match the video game concept of our store.

  • storefront/src/modules/home/components/hero/index.tsx :
...
const Hero = () => {
  return (
    <div className="h-[90vh] w-full relative">
      <div className="text-white absolute inset-0 z-10 flex flex-col justify-center items-center text-center small:text-left small:justify-end small:items-start small:p-32">
        <h1 className="text-2xl-semi mb-4 drop-shadow-md shadow-black">
          The best games on the planet
        </h1>
        <p className="text-base-regular max-w-[32rem] mb-6 drop-shadow-md shadow-black">
          We have the best games for you.
        </p>
        <UnderlineLink href="/store">Start Playing Now</UnderlineLink>
      </div>
      <Image
        src="/4db11d22-7f83-482d-920d-f0033b1e6306.jpeg"
        layout="fill"
        loading="eager"
        priority={true}
        quality={90}
        objectFit="cover"
        alt="Photo by @thevoncomplex https://unsplash.com/@thevoncomplex"
        className="absolute inset-0"
        draggable="false"
      />
    </div>
  )
}

...

This changes the text and image in the hero section of the store.

  • storefront/src/modules/layout/components/footer-cta/index.tsx :
...
const FooterCTA = () => {
  return (
    <div className="bg-amber-100 w-full">
      <div className="content-container flex flex-col-reverse gap-y-8 small:flex-row small:items-center justify-between py-16 relative">
        <div>
          <h3 className="text-2xl-semi">Play 3D Games at afforadable rates </h3>
          <div className="mt-6">
            <UnderlineLink href="/store">Start Playing Now</UnderlineLink>
          </div>
        </div>

        <div className="relative w-full aspect-square small:w-[35%] small:aspect-[28/36]">
          <Image
            src="/photo-1553481187-be93c21490a9.jpeg"
            alt=""
            layout="fill"
            objectFit="cover"
            className="absolute inset-0"
          />
        </div>
      </div>
    </div>
  )
}

...

This changes the text and image in the section right before the footer.

  • storefront/src/modules/layout/components/footer-nav/index.tsx :
...

const FooterNav = () => {
 ...

  return (
    <div className="content-container flex flex-col gap-y-8 pt-16 pb-8">
      <div className="flex flex-col gap-y-6 xsmall:flex-row items-start justify-between">
        <div>
          <Link href="/">
            <a className="text-xl-semi uppercase">Funplay</a>
          </Link>
        </div>
        <div className="text-small-regular grid grid-cols-2 gap-x-16">
          <div className="flex flex-col gap-y-2">
            <span className="text-base-semi">Games</span>
            ...
        </div>
      </div>
      <div className="flex flex-col-reverse gap-y-4 justify-center xsmall:items-center xsmall:flex-row xsmall:items-end xsmall:justify-between">
        <span className="text-xsmall-regular text-gray-500">
          © Copyright 2022 Funplay
        </span>
        <div className="min-w-[316px] flex xsmall:justify-end">
          <CountrySelect />
        </div>
      </div>
    </div>
  )
}

...

This changes the text in the footer of the storefront.

  • storefront/src/modules/store/components/index.tsx :
//...
  return (
    <div>
      <div className="px-8 py-4  small:pr-0 small:pl-8 small:min-w-[250px]">
        <div className="flex gap-x-3 small:flex-col small:gap-y-3">
          <span className="text-base-semi">Games</span>
          {//...}
        </div>
      </div>
    </div>
  )
}

...

This changes the text in the store page.

  • storefront/src/pages/index.tsx :
...

const Home: NextPageWithLayout = () => {
  return (
    <>
      <Head
        title="Home"
        description="Shop all available games only at the Funplay. Worldwide Shipping. Secure Payment."
      />
      <Hero />
      <FeaturedProducts />
    </>
  )
}

...

This changes the metadata of the storefront.

Test Storefront

Make sure the Medusa server is still running. Then, run the following command to start the storefront:

yarn run dev

Now, navigate to http://localhost:8000 to preview the products in your Medusa store.

Untitled 4.png

Then click on the Store link in the navigation bar **to view the products in the store.

Untitled 5.png

Add Algolia Integration

The Algolia integration is built into the Next.js storefront by default. You only need to take three steps to get it to work.

First, enable the search feature in the store.config.json file:

{
  "features": {
    "search": true
  }
}

Then, add the necessary environment variables in the .env.local file:

If .env.local is not available, make sure to rename .env.template to .env.local.

NEXT_PUBLIC_SEARCH_APP_ID=<YOUR_APP_ID>
NEXT_PUBLIC_SEARCH_API_KEY=<YOUR_SEARCH_API_KEY>
NEXT_PUBLIC_SEARCH_INDEX_NAME=products

<YOUR APP ID> and <YOUR SEARCH API KEY> are the Application ID and Search-Only API Key on the API Keys page on Algolia respectively.

Finally, change the code in src/lib/search-client.ts to the code snippet below:

import algoliasearch from "algoliasearch/lite"

const appId = process.env.NEXT_PUBLIC_SEARCH_APP_ID || "" // You should add this to your environment variables

const apiKey = process.env.NEXT_PUBLIC_SEARCH_API_KEY || "test_key"

export const searchClient = algoliasearch(appId, apiKey)

export const SEARCH_INDEX_NAME =
  process.env.NEXT_PUBLIC_INDEX_NAME || "products"

If you run your Next.js storefront while the Medusa server is running, the search functionality will be available in your storefront by clicking the search icon in the navigation bar.

Add Stripe Integration

The last step in the tutorial is to integrate Stripe payment to allow customers to pay for their orders during checkout.

To do that, you’ll need to add Stripe to Regions in the Medusa Admin by following the steps below:

  1. Navigate to Settings > Regions.
  2. For each region, click the three dots at the top right of the first section on the right.
  3. Click on Edit Region Details from the dropdown.
  4. Under the providers section, select Stripe.
  5. Click Save.

Then in storefront/env.local add the following environment key:

NEXT_PUBLIC_STRIPE_KEY=<YOUR_PUBLISHABLE_KEY>

Where <YOUR_PUBLISHABLE_KEY> is your Stripe Publishable Key.

Test Stripe

Restart your Next.js storefront and make sure the Medusa server is still running. Then, try adding some products to the cart and proceed to checkout.

On checkout, in the Payment section you’ll be able to use Stripe to pay for the order.

Untitled 6.png

Conclusion

Throughout this tutorial, you’ve learned how to build a video game store with Medusa. Whether you’re using the Next.js storefront or a storefront of your own, you can integrate other services like Stripe and Algolia using Medusa’s plugin system.

To learn about what more you can do with Medusa, check out Medusa’s documentation for guides on different integrations, API reference, user guide, and much more.

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