<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Shahed Nasser</title>
    <description>The latest articles on Forem by Shahed Nasser (@shahednasser).</description>
    <link>https://forem.com/shahednasser</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F507096%2Fbed24dc1-46fd-4a49-aea4-582f11a2cc48.jpg</url>
      <title>Forem: Shahed Nasser</title>
      <link>https://forem.com/shahednasser</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/shahednasser"/>
    <language>en</language>
    <item>
      <title>Medusa Community Highlights: Strapi Integration, SES Plugin, and More!</title>
      <dc:creator>Shahed Nasser</dc:creator>
      <pubDate>Mon, 15 May 2023 11:42:02 +0000</pubDate>
      <link>https://forem.com/medusajs/medusa-community-highlights-strapi-integration-ses-plugin-and-more-3mim</link>
      <guid>https://forem.com/medusajs/medusa-community-highlights-strapi-integration-ses-plugin-and-more-3mim</guid>
      <description>&lt;p&gt;&lt;a href="https://medusajs.com/readme/"&gt;Changing the way commerce is built&lt;/a&gt; is no easy undertaking. Therefore, we are incredibly excited about our Medusa community, their support, and their amazing contributions that make it easier for others to move to a more modular way of building commerce.&lt;/p&gt;

&lt;p&gt;This article is part of an ongoing series that periodically highlights community plugins, storefront starters, quotes, and more. Join our community of over 5,000 developers on &lt;a href="https://discord.com/invite/medusajs"&gt;Discord&lt;/a&gt; to stay updated with all community-related news and discussions. You can also visit our &lt;a href="https://medusajs.com/plugins/"&gt;Plugin Library&lt;/a&gt; to browse many community contributions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Latest community contributions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Medusa and Strapi Integration
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8Q7OzgXP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/likv86z70of5lm9x7x1v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8Q7OzgXP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/likv86z70of5lm9x7x1v.png" alt="Medusa and Strapi Integration" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/SGFGOV"&gt;Govind Diwakar&lt;/a&gt; has created a &lt;a href="https://github.com/SGFGOV/medusa-strapi-repo"&gt;Strapi monorepository&lt;/a&gt; with all the components necessary for you to set up and use Strapi with Medusa. Within this monorepository, you’ll find a Strapi project, a Medusa plugin for Strapi, a Strapi plugin for Medusa, and other components that will make your integration between Medusa and Strapi successful.&lt;/p&gt;

&lt;p&gt;By integrating Strapi and Medusa, you can utilize rich CMS features that Strapi provides while using Medusa’s powerful commerce features.&lt;/p&gt;

&lt;p&gt;Check out the &lt;a href="https://github.com/SGFGOV/medusa-strapi-repo"&gt;monorepository&lt;/a&gt; for more details about the project and how to set it up.&lt;/p&gt;

&lt;h3&gt;
  
  
  Medusa SES Plugin
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/pevey"&gt;Lacey Pevey&lt;/a&gt; created a plugin that allows you to use AWS Simple Email Service (SES) as a notification provider in your Medusa backend. By using this plugin, you can send emails to the customer when an order is placed, a password reset has been requested, a gift card has been created, and more using AWS SES.&lt;/p&gt;

&lt;p&gt;The templates used for the emails are stored within your Medusa backend in a &lt;code&gt;data&lt;/code&gt; directory, and they’re based on handlebars. So, they’re compatible with SendGrid templates as well.&lt;/p&gt;

&lt;p&gt;Check out the &lt;a href="https://medusajs.com/plugins/medusa-plugin-ses/"&gt;Medusa SES Plugin&lt;/a&gt; for more details about its features and how to install it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Medusa SvelteKit Client
&lt;/h3&gt;

&lt;p&gt;We also got a much-requested SvelteKit client that lets you interact with your Medusa backend from a SvelteKit storefront. With this client, you can easily build a SvelteKit storefront or admin for your Medusa backend.&lt;/p&gt;

&lt;p&gt;SvelteKit enables you to combine the fluid user experience of client-side reactivity with the ability to handle logic on the server when desired. This keeps your Medusa backend firewall-protected and accessible only to your storefront server, which provides an additional layer of security compared to directly exposing your backend.&lt;/p&gt;

&lt;p&gt;Check out the &lt;a href="https://medusajs.com/plugins/sveltekit-medusa-client/"&gt;Medusa SvelteKit Client&lt;/a&gt; for more details about its features and how to install it. Thanks to &lt;a href="https://github.com/pevey"&gt;Lacey Pevey&lt;/a&gt; for another great contribution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Community Radar
&lt;/h2&gt;


&lt;blockquote class="ltag__twitter-tweet"&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--EuKXF30E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://pbs.twimg.com/profile_images/1240743119232086022/qIt0glr6_normal.jpg" alt="Ante Primorac profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        Ante Primorac
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        &lt;a class="mentioned-user" href="https://dev.to/anteprimorac"&gt;@anteprimorac&lt;/a&gt;
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kDgU_xDI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev.to/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      &lt;a href="https://twitter.com/sebrindom"&gt;@sebrindom&lt;/a&gt; &lt;a href="https://twitter.com/medusajs"&gt;@medusajs&lt;/a&gt; Undoubtedly, the new Modules feature is the most remarkable addition to Medusa. It offers unparalleled customization options for the core functionality and the unique capability of running each module as a standalone service, representing a significant step towards greater… &lt;a href="https://t.co/2wOavsbvUX"&gt;twitter.com/i/web/status/1…&lt;/a&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      09:44 AM - 25 Apr 2023
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=1650797861192323072" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OXOJJiQT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev.to/assets/twitter-reply-action-238fe0a37991706a6880ed13941c3efd6b371e4aefe288fe8e0db85250708bc4.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=1650797861192323072" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--foTp-unf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev.to/assets/twitter-retweet-action-632c83532a4e7de573c5c08dbb090ee18b348b13e2793175fea914827bc42046.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/like?tweet_id=1650797861192323072" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SFHqU4bF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev.to/assets/twitter-like-action-1ea89f4b87c7d37465b0eb78d51fcb7fe6c03a089805d7ea014ba71365be5171.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Created something cool with Medusa? Let us know!
&lt;/h2&gt;

&lt;p&gt;We would love to hear about all the cool projects you’ve created with Medusa. Whether it’s plugins, storefront starters, your own store built with Medusa, or anything similar, we’d love to see it and hopefully share it in our next Community Highlights!&lt;/p&gt;

&lt;p&gt;You can share all your cool projects in the Showcase channel on our &lt;a href="https://discord.com/invite/medusajs"&gt;Discord&lt;/a&gt;. You can also learn how to publish your plugins on our Plugin Library using this guide in our &lt;a href="https://docs.medusajs.com/development/plugins/publish"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>programming</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Modular building blocks: The case of Amazon</title>
      <dc:creator>Shahed Nasser</dc:creator>
      <pubDate>Thu, 04 May 2023 14:07:14 +0000</pubDate>
      <link>https://forem.com/medusajs/modular-building-blocks-the-case-of-amazon-3164</link>
      <guid>https://forem.com/medusajs/modular-building-blocks-the-case-of-amazon-3164</guid>
      <description>&lt;p&gt;Amazon Web Services (AWS) is a giant in cloud computing. It provides a variety of services that facilitate managing and hosting infrastructure on the cloud. AWS is used widely by both companies and individuals to build or host any type of computer application. This includes, but not limited to, web servers, serverless applications, file and object storage, and much more.&lt;/p&gt;

&lt;p&gt;AWS have become a go-to for both developers and businesses as they’re guaranteed a reliable service for all the computing needs. Although nowadays, there are many clouding hosting services that may provide similar features, it wasn’t the case before Amazon started building AWS.&lt;/p&gt;

&lt;p&gt;Building AWS was a remarkable foresight from Amazon that understood what businesses and developers needed to continue innovating without limitations. This article explores the story of how Amazon built AWS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Early Beginning of AWS
&lt;/h2&gt;

&lt;p&gt;In the early 2000s, Amazon (named &lt;a href="http://Amazon.com"&gt;Amazon.com&lt;/a&gt; back then) was only focused on the retail industry. Although it had a software development team internally that built the Amazon store, it wasn’t considered a technology company and didn’t garner any interest from developers.&lt;/p&gt;

&lt;p&gt;In 2002, Tim O’Reilly, a renowned author and founder of O’Reilly Media, visited Jeff Bezos to show him a tool he created called Amarank. This tool automatically opened Amazon’s store every few hours and scanned the ranking of O’Reilly Media books and its competitors’ books on Amazon’s ranking for books. It did this using screen scraping. This tool allowed O’Reilly to keep track of the rankings without having to manually check the Amazon store.&lt;/p&gt;

&lt;p&gt;O’Reilly then suggested to Bezos that Amazon exposes its data, such as products or rankings, through APIs. This would allow other companies and developers to build powerful tools on top of Amazon.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Companies need to think not just what they can get for themselves from new technologies, but how they can enable others” — Tim O’Reilly&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After O’Reilly’s visit, Bezos discussed this issue with the top Amazon executives. It was then pointed out that an engineer named Rob Frederick, the founder of a mobile commerce startup called Convergence that Amazon acquired, was working on a similar project inside Amazon. He was creating APIs that would allow mobile devices to access Amazon’s store.&lt;/p&gt;

&lt;p&gt;So, Bezos decided to add Frederick to an Associates group with the mission to create APIs that allow developers to build on top of Amazon’s store. These APIs would eventually allow developers to access and perform operations such as publishing products or using payments systems.&lt;/p&gt;

&lt;p&gt;After building these APIs, Amazon held its first developer conference, making developers an essential asset to Amazon, similar to customers of the Amazon store. Then, the group that was run by Rob Frederick and the Associates group was named “Amazon Web Services”.&lt;/p&gt;

&lt;h2&gt;
  
  
  Amazon’s Struggle with Infrastructure
&lt;/h2&gt;

&lt;p&gt;Around the same time Amazon was building the stepping stones of AWS, Amazon was facing troubles with their infrastructure. As their business was growing in traffic, and as they were adding more features into the Amazon store, their monolithic architecture built in the mid 90s was no longer sufficient.&lt;/p&gt;

&lt;p&gt;So, Amazon decided to transition their infrastructure to a service-oriented architecture. This allowed them to rebuild their systems into independent but connected components. This transition took 3 years.&lt;/p&gt;

&lt;p&gt;During this transition, Amazon teams struggled to access Amazon’s resources and infrastructure while building or testing new features. Although Amazon was moving into a more flexible architecture, its resources were still managed by a single team in the Seattle headquarters.&lt;/p&gt;

&lt;p&gt;This problem delayed the roll-out of new features and frustrated both software developers at the company and the top executives, including Jeff Bezos. It was a recurring and visible problem, as teams would often present their new features to the executives, but admit they never got the chance to actually test it out.&lt;/p&gt;

&lt;p&gt;Jeff Bezos then got inspired by a book he was reading at the time called Creation by Steve Grand. The book gave him the idea to build the essential building blocks of computing that would allow Amazon’s developers to freely explore, innovate, and be creative. This wouldn’t only solve Amazon’s infrastructure problems, but similar problems other companies and developers were evidently experiencing as well.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Developers are alchemists and our job is to do everything we can to get them to do their alchemy” — Jeff Bezos&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The first steps to building these building blocks was to write down the list of “primitives” of computing. Those included storage, bandwidth, payments, among other primitives. Then, Amazon started putting together teams to develop each of these primitives as services.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building S3 and EC2
&lt;/h2&gt;

&lt;p&gt;The first two services, which are essential services that developers use now, are Simple Storage Service, or S3, and Elastic Compute Cloud, or EC2.&lt;/p&gt;

&lt;p&gt;The team assigned to build S3 worked closely with Jeff Bezos, who was highly interested in the development of these services. Bezos had big ideas and expectations of S3 that the team working on it struggled to keep up with.&lt;/p&gt;

&lt;p&gt;While the team looked over details related to service growth and downtime, Bezos insisted that the service needs to be built from the start ready to scale infinitely and with no downtime.&lt;/p&gt;

&lt;p&gt;On the other hand, EC2 was built in isolation by a team of two, Chris Pinkham and Chris Brown, working remotely from South Africa. The idea behind building EC2 was to allow developers to build and run their application on Amazon’s servers, regardless of what type of application it was. The plan was to build EC2 on top of an open source tool called &lt;a href="https://xenproject.org/"&gt;Xen&lt;/a&gt; which made it possible to run several applications on one physical server.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Launch of Amazon Web Services
&lt;/h2&gt;

&lt;p&gt;S3 was launched in March 2006. It allowed developers and websites to store files such as photos and documents on Amazon’s servers. Although S3 is a popular service now, at the time it hardly got any traffic and was overlooked by developers and companies.&lt;/p&gt;

&lt;p&gt;A few months later, EC2 was in public beta and available for developers to use. Developers rushed to get access to EC2 and start building their applications using Amazon’s servers. This ultimately lead to more traffic and usage of S3 as well.&lt;/p&gt;

&lt;p&gt;These services were highly unanticipated by rival companies such as Google and Microsoft. Amazon was the only company dabbling with cloud computing and providing developers with services that allowed them to build with no limitations. As these services rose in popularity, rival companies started noticing what they were missing out on, which lead them to launch their own similar services (Microsoft announced Azure in 2010, and Google announced Compute Engine in 2012).&lt;/p&gt;

&lt;p&gt;However, Amazon was already many steps ahead at that point. Other teams at Amazon were already working on and launching other services, such as Flexible Payment Services and Amazon CloudSearch.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Impact of AWS
&lt;/h2&gt;

&lt;p&gt;AWS transformed both Amazon and software development as we know it. Inside Amazon, teams started relying on AWS and testing it even when services were in early stages. This allowed them to find issues in AWS early on, and facilitated building and rolling out new feature. It solved a huge hurdle that the engineering teams within Amazon were facing.&lt;/p&gt;

&lt;p&gt;Companies and startups also started relying heavily on AWS. It removed all restrictions they previously faced with building and creating applications regardless of the sector they worked in. AWS pioneered a new era powering innovation and creativity. Amazon was finally considered a technology company following their launch of AWS.&lt;/p&gt;

&lt;p&gt;Amazon’s revolutionary work with AWS spawned from the need for flexibility within its rigid architecture at the time. It limited their innovation and ability to grow as an ecommerce business. Amazon realized something essential, and arguably ahead of its time: monolithic architecture hinders a business’s growth, and modularity facilitates it.&lt;/p&gt;

&lt;p&gt;Amazon’s transition in their architecture and motivation behind building AWS was to provide developers, both within the company and globally, with the necessary tooling to build without limitations. As Bezos indicated at the time, trying to guess what a developer would need when building an application was a thing from the past.&lt;/p&gt;

&lt;p&gt;Amazon's forward-thinking strategy of empowering developers through APIs and infrastructure services was a game changer for their cloud offering. As we look to the future of ecommerce, the role of developers in shaping the industry is critical.&lt;/p&gt;

&lt;p&gt;While there is still progress to be made in optimizing developer experience, at &lt;a href="https://medusajs.com/"&gt;Medusa&lt;/a&gt; we aim to follow in Amazon's footsteps by providing ecommerce developers with the necessary building blocks to build, scale, and innovate. With the right primitives in place, we believe that developers can build the Amazons of tomorrow.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>programming</category>
      <category>architecture</category>
      <category>aws</category>
    </item>
    <item>
      <title>Announcing Medusa’s Community Publication</title>
      <dc:creator>Shahed Nasser</dc:creator>
      <pubDate>Tue, 02 May 2023 10:18:47 +0000</pubDate>
      <link>https://forem.com/medusajs/announcing-medusas-community-publication-e3f</link>
      <guid>https://forem.com/medusajs/announcing-medusas-community-publication-e3f</guid>
      <description>&lt;p&gt;At Medusa, we’re introducing our new community publication, &lt;a href="https://medusa-community.hashnode.dev/welcome-to-medusas-community-publication"&gt;Medusa Community&lt;/a&gt;, hosted on Hashnode. This publication gives writers interested in writing about Medusa a platform where they can be recognized and paid for their work.&lt;/p&gt;

&lt;p&gt;Keep reading below to learn more about this publication and how you can join!&lt;/p&gt;

&lt;h2&gt;
  
  
  Why create a community publication?
&lt;/h2&gt;

&lt;p&gt;Last year, we created a Writers Program at Medusa, where we were able to work with over 200 writers in producing content related to Medusa. This has allowed us to create diverse content of different types and levels based on the different skills the community writers provided.&lt;/p&gt;

&lt;p&gt;As the Writers Program continued to grow and gain interest, we realized how important it is to highlight the community’s work and make it easier for more people to contribute and join. The community publication aims to solve a lot of the limitations we had with the Writers Program, while also providing creative and useful content for the community, by the community.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does it work?
&lt;/h2&gt;

&lt;p&gt;We share all the details about the new publication in our new &lt;a href="https://dev.to/74a2bf43b4ce43eeba200382f599321a"&gt;Write for Us page&lt;/a&gt;. To sum it up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can join as a writer using &lt;a href="https://ky5eo2x1u81.typeform.com/to/J6lDIwtJ"&gt;this form&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;You’ll then receive an email with the details to join the Hashnode publication and any other necessary resources.&lt;/li&gt;
&lt;li&gt;That’s it! You can then start writing and submitting content to the community publication.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Important notes
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;It’s important to sign up again as a writer using the form mentioned above, even if you were previously part of the Writers Program.&lt;/li&gt;
&lt;li&gt;Your article must match our &lt;a href="https://dev.to/8f3be54b95654fe8bfbb45503e7eed8a"&gt;writing guidelines&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Articles are not instantly published on the publication, as we might specify a schedule on how and when we decide to post the article.&lt;/li&gt;
&lt;li&gt;You’ll receive payment if your article is published or scheduled.&lt;/li&gt;
&lt;li&gt;Although you can generally write about anything related to Medusa, we also have guidelines on what topics we are against and some things to keep in mind when writing. You can learn more &lt;a href="https://dev.to/8f3be54b95654fe8bfbb45503e7eed8a"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Benefits of joining the Community Publication
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Your content will be recognized by the Medusa community. We’ll also often share articles we find insightful or creative across our channels.&lt;/li&gt;
&lt;li&gt;You’ll get paid for your work. We also offer bonus payments if your article gets featured on Hashnode or other publications.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Start writing for Medusa
&lt;/h2&gt;

&lt;p&gt;Interested in the Community Publication? You can join the publication through &lt;a href="https://ky5eo2x1u81.typeform.com/to/J6lDIwtJ"&gt;this form&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Check out the Medusa Community Publication and follow it on Hashnode to stay updated with all community content.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>news</category>
      <category>showdev</category>
      <category>writing</category>
    </item>
    <item>
      <title>Extending Medusa Example: Build an Open Source Marketplace</title>
      <dc:creator>Shahed Nasser</dc:creator>
      <pubDate>Wed, 19 Apr 2023 10:45:46 +0000</pubDate>
      <link>https://forem.com/medusajs/extending-medusa-example-build-an-open-source-marketplace-18km</link>
      <guid>https://forem.com/medusajs/extending-medusa-example-build-an-open-source-marketplace-18km</guid>
      <description>&lt;p&gt;&lt;a href="https://medusajs.com/" rel="noopener noreferrer"&gt;Medusa&lt;/a&gt; is the set of building blocks that developers can use to create their custom use case. Instead of forcing developers to use a standardized solution, leading them to enforce hacky-workarounds, Medusa embraces customization and extendability.&lt;/p&gt;

&lt;p&gt;This tutorial gives an example of that by giving an overview look of how you can create a marketplace with Medusa. While the steps in this tutorial may not lead to a fully-fledged marketplace, they will give you an idea of how it can be implemented by building the foundation. You’ll also find other resources at the end of the tutorial that can guide you through the rest of your marketplace development.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Last year, we wrote a blog about how to create a marketplace with Medusa and Medusa Extender. At the time, Medusa did not provide the necessary functionalities to extend its core, which required developers to use Medusa Extender. With the release of &lt;a href="https://medusajs.com/recap/" rel="noopener noreferrer"&gt;v1.8 of Medusa&lt;/a&gt;, the marketplace can now be built with Medusa only. This tutorial illustrates how to do that.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The code of this tutorial is available in &lt;a href="https://github.com/shahednasser/medusa-1.8-marketplace-tutorial" rel="noopener noreferrer"&gt;this &lt;/a&gt;&lt;a href="https://github.com/shahednasser/medusa-1.8-marketplace-tutorial" rel="noopener noreferrer"&gt;GitHub Repository&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You’ll be Building
&lt;/h2&gt;

&lt;p&gt;By following along the steps in this tutorial, you’ll be implementing the following logic:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Every time a user is created, a new store associated with that user is created.&lt;/li&gt;
&lt;li&gt;When the user retrieves the store’s details, their store’s details will be retrieved.&lt;/li&gt;
&lt;li&gt;Every time a product is created, it is associated with the store of the logged-in user.&lt;/li&gt;
&lt;li&gt;The user will only be able to retrieve products from their store.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;This article assumes you already have a Medusa backend with v1.8 of Medusa installed. If not, you can learn how to install it &lt;a href="https://docs.medusajs.com/development/backend/install" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Extend the Store Entity
&lt;/h2&gt;

&lt;p&gt;In this section, you’ll extend the Store entity so that you can later add new relations to it. Extending an entity requires extending its repository as well.&lt;/p&gt;

&lt;p&gt;You can learn more about extending an entity in &lt;a href="https://docs.medusajs.com/development/entities/extend-entity" rel="noopener noreferrer"&gt;our documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Create the file &lt;code&gt;src/models/store.ts&lt;/code&gt; with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Entity&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;typeorm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;Store&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;MedusaStore&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@medusajs/medusa&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Entity&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Store&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;MedusaStore&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// TODO add relations&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This imports the &lt;code&gt;Store&lt;/code&gt; entity from the Medusa core package as &lt;code&gt;MedusaStore&lt;/code&gt; and creates a new &lt;code&gt;Store&lt;/code&gt; entity that extends it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Extend the StoreRepository
&lt;/h3&gt;

&lt;p&gt;Next, create the file &lt;code&gt;src/repositories/store.ts&lt;/code&gt; with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Store&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../models/store&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="nx"&gt;dataSource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@medusajs/medusa/dist/loaders/database&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;StoreRepository&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;MedusaStoreRepository&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@medusajs/medusa/dist/repositories/store&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;StoreRepository&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;dataSource&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRepository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Store&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;MedusaStoreRepository&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Store&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;StoreRepository&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This imports the &lt;code&gt;StoreRepository&lt;/code&gt; from the Medusa core package as &lt;code&gt;MedusaStoreRepository&lt;/code&gt;, then extends the repository to target the new &lt;code&gt;Store&lt;/code&gt; entity you created.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create index.d.ts
&lt;/h3&gt;

&lt;p&gt;Finally, to ensure TypeScript is aware of the new type you’re creating, create the file &lt;code&gt;src/index.d.ts&lt;/code&gt; with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;declare&lt;/span&gt; &lt;span class="kr"&gt;module&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@medusajs/medusa/dist/models/store&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kr"&gt;declare&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Store&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// TODO add relations&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You’ll later add relations to the &lt;code&gt;Store&lt;/code&gt; interface as you add them to the &lt;code&gt;Store&lt;/code&gt; entity. You’ll also be using the same file when extending other entities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Extend the User Entity
&lt;/h2&gt;

&lt;p&gt;Next, you’ll extend the user entity to add a new column and relation to the &lt;code&gt;Store&lt;/code&gt; entity. The steps are the same as the ones described in the previous section.&lt;/p&gt;

&lt;p&gt;Start by creating the &lt;code&gt;src/models/user.ts&lt;/code&gt; file with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Entity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JoinColumn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ManyToOne&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;typeorm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;MedusaUser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@medusajs/medusa&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Store&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./store&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Entity&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;MedusaUser&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;UserStoreId&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;nullable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="nx"&gt;store_id&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ManyToOne&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;Store&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;members&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;JoinColumn&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;store_id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;referencedColumnName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;Store&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This imports the &lt;code&gt;User&lt;/code&gt; entity from the Medusa core package as &lt;code&gt;MedusaUser&lt;/code&gt; and creates a new entity &lt;code&gt;User&lt;/code&gt; that extends it. In the entity, you add a new column &lt;code&gt;store_id&lt;/code&gt; and a relation &lt;code&gt;store&lt;/code&gt; to the &lt;code&gt;Store&lt;/code&gt; entity.&lt;/p&gt;

&lt;p&gt;Then, in the &lt;code&gt;Store&lt;/code&gt; entity created in &lt;code&gt;src/models/store.ts&lt;/code&gt;, add the following relation to the entity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Entity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;OneToMany&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;typeorm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// other imports...&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Entity&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Store&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;MedusaStore&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;OneToMany&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;members&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might see some TypeScript errors in your editor now. This can be resolved by replacing the content of the &lt;code&gt;src/index.d.ts&lt;/code&gt; with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;declare&lt;/span&gt; &lt;span class="kr"&gt;module&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@medusajs/medusa/dist/models/store&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kr"&gt;declare&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Store&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;members&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;declare&lt;/span&gt; &lt;span class="kr"&gt;module&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@medusajs/medusa/dist/models/user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kr"&gt;declare&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;store_id&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;store&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;Store&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This informs TypeScript that the &lt;code&gt;User&lt;/code&gt; entity now has &lt;code&gt;store_id&lt;/code&gt; and &lt;code&gt;store&lt;/code&gt; properties, and the &lt;code&gt;Store&lt;/code&gt; entity has a &lt;code&gt;members&lt;/code&gt; property. You can learn more &lt;a href="https://www.typescriptlang.org/docs/handbook/declaration-merging.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Extend the UserRepository
&lt;/h3&gt;

&lt;p&gt;Next, create the file &lt;code&gt;src/repositories/user.ts&lt;/code&gt; with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../models/user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="nx"&gt;dataSource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@medusajs/medusa/dist/loaders/database&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;UserRepository&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;MedusaUserRepository&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@medusajs/medusa/dist/repositories/user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;UserRepository&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;dataSource&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRepository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;MedusaUserRepository&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;UserRepository&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This imports the &lt;code&gt;UserRepository&lt;/code&gt; from the Medusa core package as &lt;code&gt;MedusaUserRepository&lt;/code&gt; and creates a new &lt;code&gt;UserRepository&lt;/code&gt; that extends it but targets the new &lt;code&gt;User&lt;/code&gt; entity.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create Migration for the User Entity
&lt;/h3&gt;

&lt;p&gt;Since you’re adding a new column &lt;code&gt;store_id&lt;/code&gt; to the &lt;code&gt;User&lt;/code&gt; entity, you must add a new migration that reflects that column in your database.&lt;/p&gt;

&lt;p&gt;You can learn more about migrations in &lt;a href="https://docs.medusajs.com/development/entities/migrations/overview" rel="noopener noreferrer"&gt;our documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;First, run the following command in the root directory of your backend:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx typeorm migration:create src/migrations/add-user-store-id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a new file under &lt;code&gt;src/migrations&lt;/code&gt;. The file’s name should be of the format &lt;code&gt;&amp;lt;TIMESTAMP&amp;gt;_UserChanged.ts&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In the file, there’s a migration class with two methods: &lt;code&gt;up&lt;/code&gt; and &lt;code&gt;down&lt;/code&gt;. Replace the methods with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;up&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queryRunner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;QueryRunner&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;queryRunner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`ALTER TABLE "user" ADD "store_id" character varying`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;queryRunner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`CREATE INDEX "UserStoreId" ON "user" ("store_id") `&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;down&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queryRunner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;QueryRunner&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;queryRunner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`DROP INDEX "public"."UserStoreId"`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;queryRunner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`ALTER TABLE "user" DROP COLUMN "store_id"`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before you run the migrations, run the &lt;code&gt;build&lt;/code&gt; command which transpiles the files under the &lt;code&gt;src&lt;/code&gt; directory into the &lt;code&gt;dist&lt;/code&gt; directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, run the migration command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;medusa migrations run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will add the new &lt;code&gt;store_id&lt;/code&gt; column to the &lt;code&gt;user&lt;/code&gt; table.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create Middleware to Register Logged-In User
&lt;/h2&gt;

&lt;p&gt;To get access to the logged-in user across your services, you need to register it through a middleware. This middleware will run on all endpoints under the &lt;code&gt;/admin&lt;/code&gt; path, except for the &lt;code&gt;/admin/auth&lt;/code&gt; endpoints.&lt;/p&gt;

&lt;p&gt;You can learn more about middlewares in &lt;a href="https://docs.medusajs.com/development/endpoints/add-middleware" rel="noopener noreferrer"&gt;our documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Start by creating the file &lt;code&gt;src/api/middlewares/logged-in-user.ts&lt;/code&gt; with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;UserService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@medusajs/medusa&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../../models/user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;registerLoggedInUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;loggedInUser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 
      &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;userService&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;UserService&lt;/span&gt;
    &lt;span class="nx"&gt;loggedInUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;userService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;retrieve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;loggedInUser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;loggedInUser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="p"&gt;},&lt;/span&gt;
   &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This retrieves the logged-in user from &lt;code&gt;req.user.userId&lt;/code&gt; and, if available, registers it in Medusa’s dependency container under &lt;code&gt;loggedInUser&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can learn more about the dependency container and injection in &lt;a href="https://docs.medusajs.com/development/fundamentals/dependency-injection" rel="noopener noreferrer"&gt;our documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Next, create the file &lt;code&gt;src/api/index.ts&lt;/code&gt; with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Router&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;configLoader&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@medusajs/medusa/dist/loaders/config&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="nx"&gt;registerLoggedInUser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./middlewares/logged-in-user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; 
  &lt;span class="nx"&gt;authenticate&lt;/span&gt; 
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@medusajs/medusa/dist/api/middlewares/authenticate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;cors&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cors&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rootDirectory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Router&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;configLoader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rootDirectory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;adminCors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;projectConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;admin_cors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;admin&lt;/span&gt;&lt;span class="se"&gt;\/[^&lt;/span&gt;&lt;span class="sr"&gt;(auth)&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;.*/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nf"&gt;cors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;adminCors&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nf"&gt;authenticate&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nx"&gt;registerLoggedInUser&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This registers the middleware on all paths starting with &lt;code&gt;/admin&lt;/code&gt; except for &lt;code&gt;/admin/auth&lt;/code&gt; paths. Notice that you also add the &lt;code&gt;authenticate&lt;/code&gt; middleware before the &lt;code&gt;registerLoggedInUser&lt;/code&gt; middleware. The &lt;code&gt;authenticate&lt;/code&gt; middleware authenticates the user and sets &lt;code&gt;req.user&lt;/code&gt;. If not added, the &lt;code&gt;registerLoggedInUser&lt;/code&gt; middleware will not be able to access the logged-in user.&lt;/p&gt;

&lt;p&gt;You’ll see the middleware’s work in action when you start customizing services in the upcoming sections.&lt;/p&gt;

&lt;h2&gt;
  
  
  Extend Store Service
&lt;/h2&gt;

&lt;p&gt;In this section, you’ll extend the &lt;code&gt;Store&lt;/code&gt; service to change the logic behind retrieving a store. In the Medusa core package, it’s assumed there’s one store and so the first store is retrieved. You’ll be changing that to retrieve the store of the logged-in user.&lt;/p&gt;

&lt;p&gt;You can learn more about extending services in &lt;a href="https://docs.medusajs.com/development/services/extend-service" rel="noopener noreferrer"&gt;the documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Create the file &lt;code&gt;src/services/store.ts&lt;/code&gt; with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Lifetime&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;awilix&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="nx"&gt;FindConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;StoreService&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;MedusaStoreService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Store&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@medusajs/medusa&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;StoreService&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;MedusaStoreService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="nx"&gt;LIFE_TIME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Lifetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SCOPED&lt;/span&gt;
  &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;loggedInUser_&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// @ts-expect-error prefer-rest-params&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loggedInUser_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loggedInUser&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// avoid errors when backend first runs&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;retrieve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;FindConfig&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Store&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Store&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loggedInUser_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;retrieve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;retrieveForLoggedInUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;retrieveForLoggedInUser &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;FindConfig&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Store&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;storeRepo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;manager_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withRepository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storeRepository_&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;storeRepo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findOne&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;relations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;relations&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;members&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loggedInUser_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;store_id&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Unable to find the user store&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;StoreService&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You import the &lt;code&gt;StoreService&lt;/code&gt; from the Medusa core package as &lt;code&gt;MedusaStoreService&lt;/code&gt;. You then create a new &lt;code&gt;StoreService&lt;/code&gt; class that extends &lt;code&gt;MedusaStoreService&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In the class, you set the &lt;code&gt;LIFE_TIME&lt;/code&gt; of the service to &lt;code&gt;Lifetime.SCOPED&lt;/code&gt;. This is necessary for the service to access the registered &lt;code&gt;loggedInUser&lt;/code&gt;. You can learn more about it in &lt;a href="https://docs.medusajs.com/development/services/create-service#service-life-time" rel="noopener noreferrer"&gt;the documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You also add a new class attribute &lt;code&gt;loggedInUser_&lt;/code&gt; and set its value in the constructor. Notice that you wrap the initialization of &lt;code&gt;loggedInUser_&lt;/code&gt; in the constructor with a try-catch block to avoid errors when the &lt;code&gt;loggedInUser&lt;/code&gt; is not registered in the Medusa container.&lt;/p&gt;

&lt;p&gt;You then override the &lt;code&gt;retrieve&lt;/code&gt; method. In that method:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You check if the &lt;code&gt;loggedInUser_&lt;/code&gt; is set. If not, you call the &lt;code&gt;retrieve&lt;/code&gt; method of the service from the core package.&lt;/li&gt;
&lt;li&gt;If the &lt;code&gt;loggedInUser_&lt;/code&gt; is set, you retrieve the store using a new method &lt;code&gt;retrieveForLoggedInUser&lt;/code&gt;. This method retrieves the store using the value of &lt;code&gt;store_id&lt;/code&gt; of the &lt;code&gt;loggedInUser_&lt;/code&gt; and expands the &lt;code&gt;members&lt;/code&gt; relation.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Before you test out this method, you should create the logic that associate a new user with a new store.&lt;/p&gt;

&lt;h2&gt;
  
  
  Extend the User Service
&lt;/h2&gt;

&lt;p&gt;In this section, you’ll implement the logic behind creating a store for every new user. To do this, you’ll extend the &lt;code&gt;UserService&lt;/code&gt; from the Medusa core to override the &lt;code&gt;create&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;Create the file &lt;code&gt;src/services/user.ts&lt;/code&gt; with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Lifetime&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;awilix&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;UserService&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;MedusaUserService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@medusajs/medusa&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../models/user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CreateUserInput&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;MedusaCreateUserInput&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@medusajs/medusa/dist/types/user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;StoreRepository&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../repositories/store&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;CreateUserInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;store_id&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;MedusaCreateUserInput&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserService&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;MedusaUserService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="nx"&gt;LIFE_TIME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Lifetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SCOPED&lt;/span&gt;
  &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;loggedInUser_&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
  &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;storeRepository_&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;StoreRepository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// @ts-expect-error prefer-rest-params&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storeRepository_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storeRepository&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loggedInUser_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loggedInUser&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// avoid errors when backend first runs&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CreateUserInput&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;store_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;storeRepo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;manager_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withRepository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storeRepository_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;newStore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;storeRepo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="nx"&gt;newStore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;storeRepo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newStore&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;store_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;newStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;UserService&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this file you:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Extend the core’s &lt;code&gt;UserService&lt;/code&gt; which is imported as &lt;code&gt;MedusaUserService&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;You change the value of the &lt;code&gt;LIFE_TIME&lt;/code&gt; attribute of the service. This is explained in the Extend Store Service section.&lt;/li&gt;
&lt;li&gt;You add a new &lt;code&gt;loggedInUser_&lt;/code&gt; attribute and initialize it in the constructor.&lt;/li&gt;
&lt;li&gt;You override the &lt;code&gt;create&lt;/code&gt; method. In the method, you first check if the &lt;code&gt;user&lt;/code&gt; does not have a &lt;code&gt;store_id&lt;/code&gt;. This allows you to specifically set the store of the user in other places if necessary. This can be helpful if you’re creating a team of users in one store. If the user doesn’t have &lt;code&gt;store_id&lt;/code&gt; set, a new store is created and the &lt;code&gt;store_id&lt;/code&gt; property of the user is set to the ID of the new store. The user is saved then using the logic implemented in the core service.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Test Store-User Relation
&lt;/h2&gt;

&lt;p&gt;Time to test everything you’ve implemented so far. To do that, run the &lt;code&gt;build&lt;/code&gt; and the &lt;code&gt;start&lt;/code&gt; commands in your Medusa backend:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run build &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once your Medusa backend starts, login with any admin user that you have using the &lt;a href="https://docs.medusajs.com/api/admin#tag/Auth/operation/PostAuth" rel="noopener noreferrer"&gt;User Login endpoint&lt;/a&gt;. If you’ve seeded your database with demo data, you should have the following login credentials:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"admin@medusa-test.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"supersecret"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that when you log in, the &lt;code&gt;store_id&lt;/code&gt; of the user is set to &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then, try to get the store’s data using the &lt;a href="https://docs.medusajs.com/api/admin#tag/Store/operation/GetStore" rel="noopener noreferrer"&gt;Get Store Details endpoint&lt;/a&gt;. The default store will be returned, which has an empty &lt;code&gt;members&lt;/code&gt; array.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8aownxpnf8n0zhxjxjlf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8aownxpnf8n0zhxjxjlf.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, try to create a new user using the &lt;a href="https://docs.medusajs.com/api/admin#tag/Users/operation/PostUsers" rel="noopener noreferrer"&gt;Create a User endpoint&lt;/a&gt;. You should see that the new user has a set &lt;code&gt;store_id&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7fu7r8o76nlsodjar884.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7fu7r8o76nlsodjar884.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, try to login with your new user, then try to retrieve the store’s data using the Get Store Details endpoint as mentioned earlier. You’ll see now that a different store is retrieved than the first time. This is the store that was created for this new user. It also has the new user as part of its &lt;code&gt;members&lt;/code&gt; array.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsxxuyptz880b3f70bei0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsxxuyptz880b3f70bei0.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The user-store relation is now established and ready for use. Next, you’ll be working on the product-store relation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Extend the Product Entity
&lt;/h2&gt;

&lt;p&gt;In this section, you’ll extend the &lt;code&gt;Product&lt;/code&gt; entity to add a new column and relation to the &lt;code&gt;Store&lt;/code&gt;. The process will be very similar to that of extending the &lt;code&gt;User&lt;/code&gt; entity.&lt;/p&gt;

&lt;p&gt;Start by creating the file &lt;code&gt;src/models/product.ts&lt;/code&gt; with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Entity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JoinColumn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ManyToOne&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;typeorm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;Product&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;MedusaProduct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@medusajs/medusa&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Store&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./store&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Entity&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;MedusaProduct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ProductStoreId&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;nullable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="nx"&gt;store_id&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ManyToOne&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;Store&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;JoinColumn&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;store_id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;referencedColumnName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;Store&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This imports the &lt;code&gt;Product&lt;/code&gt; entity from the Medusa core package as &lt;code&gt;MedusaProduct&lt;/code&gt; and creates a new entity &lt;code&gt;Product&lt;/code&gt; that extends it. In the entity, you add a new column &lt;code&gt;store_id&lt;/code&gt; and a relation &lt;code&gt;store&lt;/code&gt; to the &lt;code&gt;Store&lt;/code&gt; entity.&lt;/p&gt;

&lt;p&gt;Then, in the &lt;code&gt;Store&lt;/code&gt; entity created in &lt;code&gt;src/models/store.ts&lt;/code&gt;, add the following relation to the entity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Product&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./product&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// other imports...&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Entity&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Store&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;MedusaStore&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// other relation...&lt;/span&gt;

    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;OneToMany&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might see some TypeScript errors in your editor now. This can be resolved by adding the &lt;code&gt;products&lt;/code&gt; relation to the &lt;code&gt;Store&lt;/code&gt; interface and adding a new declaration for the &lt;code&gt;Product&lt;/code&gt; in &lt;code&gt;index.d.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;declare&lt;/span&gt; &lt;span class="kr"&gt;module&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@medusajs/medusa/dist/models/store&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kr"&gt;declare&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Store&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;members&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="nl"&gt;products&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// user declaration...&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;declare&lt;/span&gt; &lt;span class="kr"&gt;module&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@medusajs/medusa/dist/models/product&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kr"&gt;declare&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Product&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;store_id&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;store&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;Store&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Extend the ProductRepository
&lt;/h3&gt;

&lt;p&gt;Next, create the file &lt;code&gt;src/repositories/product.ts&lt;/code&gt; with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Product&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../models/product&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="nx"&gt;dataSource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@medusajs/medusa/dist/loaders/database&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;ProductRepository&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;MedusaProductRepository&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@medusajs/medusa/dist/repositories/product&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ProductRepository&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;dataSource&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRepository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;MedusaProductRepository&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Product&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;ProductRepository&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This imports the &lt;code&gt;ProductRepository&lt;/code&gt; from the Medusa core package as &lt;code&gt;MedusaProductRepository&lt;/code&gt; and creates a new &lt;code&gt;ProductRepository&lt;/code&gt; that extends it but targets the new &lt;code&gt;Product&lt;/code&gt; entity.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create Migration for the Product Entity
&lt;/h3&gt;

&lt;p&gt;Since you’re adding a new column &lt;code&gt;store_id&lt;/code&gt; to the &lt;code&gt;Product&lt;/code&gt; entity, you must add a new migration that reflects that column in your database.&lt;/p&gt;

&lt;p&gt;First, run the following command in the root directory of your backend:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx typeorm migration:create src/migrations/ProductChanged
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a new file under &lt;code&gt;src/migrations&lt;/code&gt;. The file’s name should be of the format &lt;code&gt;&amp;lt;TIMESTAMP&amp;gt;_ProductChanged.ts&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In the file, there’s a migration class with two methods: &lt;code&gt;up&lt;/code&gt; and &lt;code&gt;down&lt;/code&gt;. Replace the methods with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;up&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queryRunner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;QueryRunner&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;queryRunner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`ALTER TABLE "product" ADD "store_id" character varying`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;queryRunner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`CREATE INDEX "ProductStoreId" ON "product" ("store_id") `&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;down&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queryRunner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;QueryRunner&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;queryRunner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`DROP INDEX "public"."ProductStoreId"`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;queryRunner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`ALTER TABLE "product" DROP COLUMN "store_id"`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before you run the migrations, run the &lt;code&gt;build&lt;/code&gt; command which transpiles the files under the &lt;code&gt;src&lt;/code&gt; directory into the &lt;code&gt;dist&lt;/code&gt; directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, run the migration command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;medusa migrations run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will add the new &lt;code&gt;store_id&lt;/code&gt; column to the &lt;code&gt;product&lt;/code&gt; table.&lt;/p&gt;

&lt;h2&gt;
  
  
  Extend the Product Service
&lt;/h2&gt;

&lt;p&gt;In this section, you’ll extend the product service to do two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Change the &lt;code&gt;save&lt;/code&gt; logic to attach the product to the logged-in user’s store.&lt;/li&gt;
&lt;li&gt;Change the methods used to retrieve products including the &lt;code&gt;list&lt;/code&gt;, &lt;code&gt;listAndCount&lt;/code&gt;, and &lt;code&gt;retrieve&lt;/code&gt; methods to retrieve the products only associated with the logged-in user’s store.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Create the file &lt;code&gt;src/services/product.ts&lt;/code&gt; with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Lifetime&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;awilix&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="nx"&gt;ProductService&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;MedusaProductService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@medusajs/medusa&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CreateProductInput&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;MedusaCreateProductInput&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;FindProductConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ProductSelector&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;MedusaProductSelector&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@medusajs/medusa/dist/types/product&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ProductSelector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;store_id&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;MedusaProductSelector&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;CreateProductInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;store_id&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;MedusaCreateProductInput&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProductService&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;MedusaProductService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="nx"&gt;LIFE_TIME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Lifetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SCOPED&lt;/span&gt;
  &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;loggedInUser_&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// @ts-expect-error prefer-rest-params&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loggedInUser_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loggedInUser&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// avoid errors when backend first runs&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ProductSelector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;FindProductConfig&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;store_id&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loggedInUser_&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;store_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;store_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loggedInUser_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;store_id&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;select&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;store_id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;relations&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;store&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;listAndCount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ProductSelector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;FindProductConfig&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;store_id&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loggedInUser_&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;store_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;store_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loggedInUser_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;store_id&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;select&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;store_id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;relations&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;store&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listAndCount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;retrieve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;FindProductConfig&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;relations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;...(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;relations&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[]),&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;store&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;retrieve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loggedInUser_&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;store_id&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loggedInUser_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;store_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Throw error if you don't want a product to be accessible to other stores&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Product does not exist in store.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productObject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CreateProductInput&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;productObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;store_id&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loggedInUser_&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;store_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;productObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;store_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loggedInUser_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;store_id&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productObject&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;ProductService&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this file you:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Extend the core’s &lt;code&gt;ProductService&lt;/code&gt; which is imported as &lt;code&gt;MedusaProductService&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;You change the value of the &lt;code&gt;LIFE_TIME&lt;/code&gt; attribute of the service. This is explained in the Extend Store Service section.&lt;/li&gt;
&lt;li&gt;You add a new &lt;code&gt;loggedInUser_&lt;/code&gt; attribute and initialize it in the constructor.&lt;/li&gt;
&lt;li&gt;You override the &lt;code&gt;list&lt;/code&gt; and &lt;code&gt;listAndCount&lt;/code&gt; methods to filter the retrieved products by the logged-in user’s store ID. You also ensure that the &lt;code&gt;store_id&lt;/code&gt; attribute is retrieved as part of the product and that the &lt;code&gt;store&lt;/code&gt; relation is expanded.&lt;/li&gt;
&lt;li&gt;You override the &lt;code&gt;retrieve&lt;/code&gt; method to check if the retrieved product belongs to the logged-in user’s store. If not, you throw an error that the product does not exist.&lt;/li&gt;
&lt;li&gt;You override the &lt;code&gt;create&lt;/code&gt; method to check if the logged-in user has a store and, if so, associate the product’s store ID with that store.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;Notice that you didn’t implement the &lt;code&gt;save&lt;/code&gt; mechanism as part of the repository this time. This is because the repository does not have access to the Medusa container. So, you won’t be able to access the logged-in user in it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Test the Product-Store Relation
&lt;/h2&gt;

&lt;p&gt;You can now test the relation between the product and the store.&lt;/p&gt;

&lt;p&gt;Start by running the &lt;code&gt;build&lt;/code&gt; and &lt;code&gt;start&lt;/code&gt; commands in the root of your Medusa backend:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run build &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, log in as a user who has a store as explained in the previous testing section.&lt;/p&gt;

&lt;p&gt;After that, retrieve the available products by sending a request to the &lt;a href="https://docs.medusajs.com/api/admin#tag/Products/operation/GetProducts" rel="noopener noreferrer"&gt;List Products&lt;/a&gt; endpoint. You’ll see that there are no products returned, even if you previously had products in your store. This is because these products are not associated with the user’s store.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgq3uzxm48wm49v4k5pn6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgq3uzxm48wm49v4k5pn6.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then, try creating a new product using the &lt;a href="https://docs.medusajs.com/api/admin#tag/Products/operation/PostProducts" rel="noopener noreferrer"&gt;Create Product&lt;/a&gt; endpoint. You’ll see that the created product has a &lt;code&gt;store&lt;/code&gt; attribute. The &lt;code&gt;id&lt;/code&gt; of that store is the same ID of the user’s store.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxhk14d3py82w5bjbhmpi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxhk14d3py82w5bjbhmpi.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you try retrieving the list of products again, you’ll find your newly-created product in the returned array.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffb3b713yejt9r4q4ni89.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffb3b713yejt9r4q4ni89.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Note About Super-Admin User
&lt;/h3&gt;

&lt;p&gt;So far in your implementation you’ve taken into accoun utsers that don’t have a set store ID. These are considered “super-admin” users. These users would be able to view all products in the store and the default store’s details.&lt;/p&gt;

&lt;p&gt;If this logic does not work for you, make sure to change the implementation to require a store ID for a user in the different locations we’ve used it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s Next
&lt;/h2&gt;

&lt;p&gt;In this tutorial, you learned how to implement the foundation of a marketplace: having multiple stores, different users within those stores, and associating products with a store.&lt;/p&gt;

&lt;p&gt;A marketplace has a variety of other features, each depending on your use case. Some of them are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create order-store relation: This requires a similar implementation as what you’ve done in this tutorial with products and users. You need to extend the &lt;code&gt;Order&lt;/code&gt; entity to include a relation to the store. You can learn more about extending entities in &lt;a href="https://docs.medusajs.com/development/entities/extend-entity" rel="noopener noreferrer"&gt;the documentation&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;List orders by stores: This requires a similar implementation as what you’ve done in this tutorial with products. You need to extend the &lt;code&gt;OrderService&lt;/code&gt; to override the methods used to retrieve orders. You can learn more about extending services in &lt;a href="https://docs.medusajs.com/development/services/extend-service" rel="noopener noreferrer"&gt;the documentation&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Associate an order to a store: This requires listening to the &lt;code&gt;order.created&lt;/code&gt; event in a subscriber. The implementation can include creating child orders of an order if in your use case you support have products from multiple stores in one product. In this case, you’d also need to extend the order entity to create a parent-child relation. You can learn more about subscribers in &lt;a href="https://docs.medusajs.com/development/events/subscribers" rel="noopener noreferrer"&gt;the documentation&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Implement teams within a store: You can implement a team within a store by extending the &lt;code&gt;Invite&lt;/code&gt; entity to associate it with a store ID, then associate the user created from the invite with that store ID.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are just some ideas and direction for your development of the marketplace. For further help in your development, make sure to check out the available guides in &lt;a href="https://docs.medusajs.com/" rel="noopener noreferrer"&gt;our documentation&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>javascript</category>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
    <item>
      <title>The History of Ecommerce: 1979 to 2023</title>
      <dc:creator>Shahed Nasser</dc:creator>
      <pubDate>Thu, 09 Mar 2023 11:46:55 +0000</pubDate>
      <link>https://forem.com/medusajs/the-history-of-ecommerce-1979-to-2023-2k9n</link>
      <guid>https://forem.com/medusajs/the-history-of-ecommerce-1979-to-2023-2k9n</guid>
      <description>&lt;p&gt;Ecommerce has evolved a lot since its start. It hasn’t always been well-designed stores with extensive features and carefully-planned user experiences. A lot of what is fundamental and basic today was revolutionary when discovered at its time.&lt;/p&gt;

&lt;p&gt;This article goes down the memory lane of ecommerce, uncovering some old-but-gold ecommerce websites, and highlighting the main difference from ecommerce today.&lt;/p&gt;

&lt;h2&gt;
  
  
  1979: Michael Aldrich and the First Attempt at Ecommerce
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8dx9zu6m86c56ly5u0xk.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8dx9zu6m86c56ly5u0xk.gif" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In 1979, &lt;a href="https://www.aldricharchive.co.uk/" rel="noopener noreferrer"&gt;Michael Aldrich&lt;/a&gt; invented online shopping through televisions. This was way before the inventions of the Internet and the World Wide Web (WWW). He invented it by connecting a modified domestic TV to a transaction processing computer through a domestic telephone line.&lt;/p&gt;

&lt;p&gt;Michael Aldrich believed that &lt;a href="https://en.wikipedia.org/wiki/Videotex" rel="noopener noreferrer"&gt;Videotex&lt;/a&gt; was a revolutionary communication medium that was universally applicable and participative, and the first of its kind since the invention of the telephone.&lt;/p&gt;

&lt;p&gt;Following his invention, &lt;a href="https://www.aldricharchive.co.uk/internet-online-shopping" rel="noopener noreferrer"&gt;two businesses used Michael’s invention&lt;/a&gt;: Gateshead SIS with Tesco and Bradford Centrepoint with Wm. Morrison. This allowed consumers to &lt;a href="https://www.bbc.com/news/magazine-24091393" rel="noopener noreferrer"&gt;open their TV and choose groceries&lt;/a&gt; from a local supermarket. The order would then be sent through the phone line to a local Tesco, where the ordered items would be packaged and sent to the consumer.&lt;/p&gt;

&lt;h2&gt;
  
  
  1984: CompuServe’s Electronic Mall
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs0vz2aklt2kcyuw86kbz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs0vz2aklt2kcyuw86kbz.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=k-oBJml1mL0" rel="noopener noreferrer"&gt;&lt;em&gt;Image Source&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;CompuServe was one of the first businesses getting close to an ecommerce experience. It was a popular networking service that provided users with emails, message boards, and more. &lt;/p&gt;

&lt;p&gt;In 1984, CompuServe added a service called “Electronic Mall” that allowed users to buy items &lt;a href="https://money.howstuffworks.com/history-e-commerce.htm" rel="noopener noreferrer"&gt;from over 100 online retailers&lt;/a&gt; through a simple CLI interface where customers could browse different vendors, product categories and even insert credit information for later purchases.&lt;/p&gt;

&lt;p&gt;You can see a demo of it all &lt;a href="https://www.youtube.com/watch?v=k-oBJml1mL0" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  1992: Book Stacks Unlimited, the Amazon Predecessor
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe6br9v3pc18w5gzoh805.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe6br9v3pc18w5gzoh805.jpeg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.timetoast.com/timelines/el-comercio-electronico-35fa3c79-6531-4038-97cd-48fcde70e856" rel="noopener noreferrer"&gt;&lt;em&gt;Image Source&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Contrary to popular belief, Amazon was not the first online bookstore. Book Stacks Unlimited was created in 1992, three years before Amazon. It began as a dial-up bulletin board. They then launched the website Books.com in 1994, which was eventually &lt;a href="https://en.wikipedia.org/wiki/Book_Stacks_Unlimited" rel="noopener noreferrer"&gt;acquired by Barnes &amp;amp; Noble&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Not only did Book Stacks Unlimited offer 500K titles, but they provided features such as editorial book recommendations, book summaries, RealAudio interviews with authors, book search, and more.&lt;/p&gt;

&lt;h2&gt;
  
  
  1994: NetMarket and the First Sale on the Internet
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwae0beq3y8cio9namf30.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwae0beq3y8cio9namf30.gif" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://people.apache.org/~jim/NewArchitect/webrevu/1998/05_29/strategists/05_29_98_2.html" rel="noopener noreferrer"&gt;&lt;em&gt;Image Source&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In 1994, Dan Kohn created NetMarket, which would sell a variety of CDs and books. He then marked the &lt;a href="https://en.wikipedia.org/wiki/NetMarket" rel="noopener noreferrer"&gt;first sale of a product over the internet using a credit card&lt;/a&gt;: a Ten Summoner’s Tale CD by Sting.&lt;/p&gt;

&lt;p&gt;According to the &lt;a href="https://www.nytimes.com/1994/08/12/business/attention-shoppers-internet-is-open.html" rel="noopener noreferrer"&gt;New York Times&lt;/a&gt;, which covered the story back then, this was the first retail transaction on the Internet using a readily available version of powerful data encryption software designed to guarantee privacy. Dan Kohn &lt;a href="https://www.nytimes.com/1994/08/12/business/attention-shoppers-internet-is-open.html" rel="noopener noreferrer"&gt;said about the purchase&lt;/a&gt; that “even if the N.S.A. was listening in, they couldn't get his credit card number.”&lt;/p&gt;

&lt;h2&gt;
  
  
  1994-1999: The Launch of Ecommerce Giants
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1994: PizzaNet (PizzaHut)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzalenh71dhziulammgck.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzalenh71dhziulammgck.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://web.archive.org/web/20140123215657/https://www.pizzahut.com/assets/pizzanet/home.html" rel="noopener noreferrer"&gt;&lt;em&gt;Image Source&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In 1994, PizzaHut created PizzaNet, their online ordering service. Not only did it allow customers to order from the menu, but it also allowed them to &lt;a href="https://www.latimes.com/archives/la-xpm-1994-08-25-fi-31168-story.html" rel="noopener noreferrer"&gt;customize the pizza&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the skepticism of how the website might be used, the local restaurant would always call the customer to verify the order to minimize the risk of pranksters.&lt;/p&gt;

&lt;h3&gt;
  
  
  1995: Amazon
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz719afe5wy2gcuhjxgru.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz719afe5wy2gcuhjxgru.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.webdesignmuseum.org/gallery/amazon-1995" rel="noopener noreferrer"&gt;&lt;em&gt;Image Source&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Amazon launched in 1995 as a book store. Although the design wasn’t too appealing, there were features that were advanced for its time: a virtual shopping cart, secure way of entering credit card details, an internal search engine, product reviews, and more.&lt;/p&gt;

&lt;p&gt;In the very beginning, Amazon’s engineers used &lt;a href="https://en.wikipedia.org/wiki/C_(programming_language)" rel="noopener noreferrer"&gt;C&lt;/a&gt; to code a lot of the features of the website, and used &lt;a href="https://en.wikipedia.org/wiki/Berkeley_DB" rel="noopener noreferrer"&gt;Berkeley DB&lt;/a&gt; to store the website’s data.&lt;/p&gt;

&lt;h3&gt;
  
  
  1996: eBay
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxojti9an41z39pogf8io.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxojti9an41z39pogf8io.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.webdesignmuseum.org/gallery/ebay-1996" rel="noopener noreferrer"&gt;&lt;em&gt;Image Source&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;eBay (initially Auction Web) was launched in 1996. It was one of its kind, being the first online auction website providing a &lt;a href="https://www.cs.brandeis.edu/~magnus/ief248a/eBay/history.html" rel="noopener noreferrer"&gt;person-to-person marketplace&lt;/a&gt;. Buyers would present their products, and customers bid on the items to eventually purchase it.&lt;/p&gt;

&lt;p&gt;eBay was free of charge at the start. However, as their traffic hugely increased, &lt;a href="https://en.wikipedia.org/wiki/EBay" rel="noopener noreferrer"&gt;eBay’s founders were informed&lt;/a&gt; that they needed to upgrade their hosting account. This led them to take commissions on bids to fund the website.&lt;/p&gt;

&lt;h3&gt;
  
  
  1997: Apple
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftfocr8c9ooq19zzcmrle.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftfocr8c9ooq19zzcmrle.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.versionmuseum.com/history-of/apple-website" rel="noopener noreferrer"&gt;&lt;em&gt;Image Source&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Steve Jobs returned to Apple in 1997. His first line-of-action was to improve Apple’s presence in the ecommerce world. Apple launched their new-and-improved ecommerce website at the end of 1997 aimed at &lt;a href="https://en.wikipedia.org/wiki/Apple_Store" rel="noopener noreferrer"&gt;improving customer experience&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  1998-1999: Nike
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fas7ieldpisjm194dlntn.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fas7ieldpisjm194dlntn.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://theecommmanager.com/ecommerce/ecommerce-sites-from-history-old-screenshots/" rel="noopener noreferrer"&gt;&lt;em&gt;Image Source&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nike created their first website in 1997. However, it was mainly focused on teaching their consumers more about Nike and their products, rather than selling these products online.&lt;/p&gt;

&lt;p&gt;In 1998, the Nike website improved to provide consumers the ability to &lt;a href="https://www.shoepalace.com/blogs/all/the-history-of-nike" rel="noopener noreferrer"&gt;customize their own Nike shoes&lt;/a&gt;. It wasn’t until 1999 that Nike moved from editorial content to an online store.&lt;/p&gt;

&lt;h2&gt;
  
  
  2000-2005: Dot-Com Crash
&lt;/h2&gt;

&lt;p&gt;In the 1990s, the Internet garnered heavy attention and adoption. Web-based startups gained a lot of funding, and all companies suddenly needed to “move to the internet”. Later followed what is now known as the Dot-Com Crash in March 2000 where the Nasdaq index slipped ~77% over less than three years.&lt;/p&gt;

&lt;p&gt;The dot-com crash took down many ecommerce and internet-based companies that gained success during the 1990s. This affected even big companies like Amazon. Still, some companies like &lt;a href="https://www.itbusiness.ca/news/how-ebay-canada-survived-the-dot-com-bust/5053" rel="noopener noreferrer"&gt;eBay thrived despite the headwinds&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  SnowDevil and Shopify
&lt;/h3&gt;

&lt;p&gt;In 2004, SnowDevil was launched, which ended up being &lt;a href="https://producthabits.com/shopify-grew-snowboard-shop-10b-commerce-ecosystem/" rel="noopener noreferrer"&gt;the spark that ignited Shopify&lt;/a&gt;. Tobias Lütke and Scott Lake wanted to sell snowboards online, but couldn’t find a software that would help them do that.&lt;/p&gt;

&lt;p&gt;They initially used a variety of tools and services such as Miva, OsCommerce, and Yahoo stores. However, they frequently ran into trouble when trying to bring their vision to life. &lt;a href="https://signalvnoise.com/posts/2378-qa-with-tobias-ltke-of-shopify" rel="noopener noreferrer"&gt;According to Lütke&lt;/a&gt; they had a “great CSS-based layout done with all these new fanged ‘web standards’”. However, the lack of customization capability that Yahoo provided barely allowed him to change the background color of the top frame.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwn1vp4udxee30pw9up3p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwn1vp4udxee30pw9up3p.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, Lütke and Lake set out to create a solution that would not only solve their problem, but solve the problems of businesses across the globe. They built Shopify in 2007 and transformed SnowDevil to a Shopify demo store, bringing their vision to life.&lt;/p&gt;

&lt;p&gt;Shopify now powers over &lt;a href="https://trends.builtwith.com/shop/Shopify" rel="noopener noreferrer"&gt;4.5 million websites&lt;/a&gt; worldwide.&lt;/p&gt;

&lt;h3&gt;
  
  
  Note on 2000s Designs
&lt;/h3&gt;

&lt;p&gt;When looking back at some of the designs from the early 2000s, you’ll notice that there was a significant advancement from the HTML, almost only text and disarrayed images design that dominated that 1990s.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fssjjhfb6fp6tnre2q31p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fssjjhfb6fp6tnre2q31p.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.webdesignmuseum.org/timeline/amazon-2001" rel="noopener noreferrer"&gt;&lt;em&gt;Image Source&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Even though the design was still clunky and crammed, there was a clear usage of layouts and different colors. Ecommerce websites at the time also started using some elements that are now basics to ecommerce, such as a navigation menu and a sidebar for categories.&lt;/p&gt;

&lt;p&gt;During that period, tech giants like Amazon and eBay continued to innovate with new features that are now core ecommerce features. Amazon added features like &lt;a href="https://press.aboutamazon.com/2003/10/amazon-com-announces-sales-impact-from-new-search-inside-the-book-feature" rel="noopener noreferrer"&gt;Search Inside the Book&lt;/a&gt;, allowing customers to search for words inside books. eBay also added a Live Chat feature to provide support for customers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffegfl25j28ht4vg2col3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffegfl25j28ht4vg2col3.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.webdesignmuseum.org/timeline/ebay-2002" rel="noopener noreferrer"&gt;&lt;em&gt;Image Source&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2006-2010: Rise of Ecommerce Platforms
&lt;/h2&gt;

&lt;p&gt;After the dot-com crash, ecommerce companies and stores steadily regained momentum, and new ecommerce platforms emerged.&lt;/p&gt;

&lt;p&gt;In 2007, Magento was created by Varien, and it was later &lt;a href="https://en.wikipedia.org/wiki/Magento" rel="noopener noreferrer"&gt;acquired by eBay&lt;/a&gt;. In the beginning, Varien were building their platform on osCommerce. However, they found it &lt;a href="https://www.icecubedigital.com/blog/what-is-magento-its-history-facts-usage-more/" rel="noopener noreferrer"&gt;incapable of accommodating all the features&lt;/a&gt; and functionalities they had in mind. So, they decided to build a new open-source solution from scratch.&lt;/p&gt;

&lt;p&gt;With the passing years, Magento grew to be one of the top open source ecommerce platforms at its time. In its first year, it was downloaded over 500K times. Magento’s popularity is due to the customizability capabilities it provided, as well as its open source nature. This allowed businesses to break away from proprietary solutions and take full ownership of their store.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftlkfijenlsnx9h2pnxd9.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftlkfijenlsnx9h2pnxd9.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://commons.wikimedia.org/wiki/File:Magento_Admin_Panel_screenshot.jpg" rel="noopener noreferrer"&gt;&lt;em&gt;Image Source&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;BigCommerce was launched in 2009, after its founders met in a &lt;a href="https://signalvnoise.com/posts/2727-bootstrapped-profitable-proud-bigcommerce" rel="noopener noreferrer"&gt;programming online chat&lt;/a&gt;. BigCommerce initially wanted to provide a shopping-cart-only solution called Interspire. However, their consumers found it difficult to manually find, install, and update their software on a server, which lead to the change from Interspire to BigCommerce.&lt;/p&gt;

&lt;p&gt;BigCommerce gained popularity among businesses that didn’t want the hassle of handling the technical parts of ecommerce, but still wanted to have the customizability options that allowed them to grow and scale. By 2011, BigCommerce was powering almost 10K stores.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh3jee9avc5lcytzsfapt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh3jee9avc5lcytzsfapt.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://signalvnoise.com/posts/2727-bootstrapped-profitable-proud-bigcommerce" rel="noopener noreferrer"&gt;&lt;em&gt;Image Source&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2010-2020: The Platform Puzzle
&lt;/h2&gt;

&lt;p&gt;As the 2010s started, ecommerce saw a rapid uptake, and over the decade, &lt;a href="https://tblocks.com/how-e-commerce-has-changed-over-the-last-10-years" rel="noopener noreferrer"&gt;global ecommerce sales grew 8-fold&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;During this period, ecommerce saw a lot of shifts in user behavior as new platforms emerged, effectively reshaping how users interacted with brands online.&lt;/p&gt;

&lt;h3&gt;
  
  
  Design Changes
&lt;/h3&gt;

&lt;p&gt;The 2010s show a big improvement in the design and functionalities that ecommerce stores provided. Many of the features that were carefully implemented in the 1990s and early 2000s became fundamentals that every ecommerce store had.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1p3d0n12gc17mmaal3fd.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1p3d0n12gc17mmaal3fd.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With more businesses moving towards the ecommerce landscape, they each needed to stand out through unique branding and design. Ecommerce platforms that started emerging in the late 2000s provided more customizable options for the storefront.&lt;/p&gt;

&lt;p&gt;This also lead to newer technologies, concepts, and approaches rising that would reshape websites in general, including ecommerce storefronts. Concepts like &lt;a href="https://www.netlify.com/press/unite-modern-web-ecosystem-at-jamstack-conf-2022" rel="noopener noreferrer"&gt;Jamstack&lt;/a&gt;, coined by Netlify in 2015, and the launch of static-site generator frameworks like Gatsby or Next.js, allowed businesses to have more control over designing their storefront and provide a better user experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mobile Commerce
&lt;/h3&gt;

&lt;p&gt;The 2010s also marked the rise of mobile commerce. Ecommerce businesses started rolling out mobile apps that allowed their consumers to access the same web functionalities from their smartphones. This went through its own timeline of evolution, starting from a simple design and advancing to more sleek and user-experience tailored design.&lt;/p&gt;

&lt;p&gt;This elevated the development of ecommerce stores from only catering to a website to multiple platforms. Developers needed to design and build APIs within their existing systems to cater for mobile development.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3q8d6e1s4d5xr0l0c5m2.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3q8d6e1s4d5xr0l0c5m2.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.talkandroid.com/43842-ebay-app-gets-updated-you-can-now-sell-items-from-your-phone/" rel="noopener noreferrer"&gt;&lt;em&gt;Image Source&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Social Commerce
&lt;/h3&gt;

&lt;p&gt;In the mid 2010s, social commerce started emerging. In 2015, Pinterest rolled out its first shopping capabilities through &lt;a href="https://techcrunch.com/2015/06/02/pinterest-unveils-buyable-pins-a-way-to-purchase-things-directly-from-pinterest/" rel="noopener noreferrer"&gt;Buyable Pins&lt;/a&gt;. Users were able to purchase products from Pinterest. &lt;a href="https://www.cnbc.com/2018/09/17/instagram-launches-new-shopping-features.html" rel="noopener noreferrer"&gt;Instagram also announced Instagram Shop in 2018&lt;/a&gt; which allows users to browse and purchase products from businesses without leaving Instagram.&lt;/p&gt;

&lt;p&gt;With the introduction of social commerce, businesses had now another platform to attract new customers. Businesses invested in social marketing strategies to ensure potential customers can discover their products through these platforms and, in turn, convert to purchasing customers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff8cewedj8ybvi6v153ax.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff8cewedj8ybvi6v153ax.gif" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://techcrunch.com/2015/06/02/pinterest-unveils-buyable-pins-a-way-to-purchase-things-directly-from-pinterest/" rel="noopener noreferrer"&gt;&lt;em&gt;Image Source&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Stripe and the Emergence of New Ecommerce Infrastructure
&lt;/h3&gt;

&lt;p&gt;While ecommerce platforms evolved, a new type of infrastructure started to emerge to further improve the commerce experience. From analytics and backend-focused tools, to new solutions solving complicated issues related to areas such as payment and distribution. &lt;/p&gt;

&lt;p&gt;The brothers Patrick and John Collison founded one of the most notable infrastructure solutions in this new commerce era in 2010. Through their side projects, they found it difficult to accept payment online and wanted to fix that. So, they developed a quick solution in &lt;a href="https://www.zippia.com/stripe-careers-39818/history/" rel="noopener noreferrer"&gt;2 weeks&lt;/a&gt; and were able to process their first transaction. After making several iterations over the next few months, they launched Stripe under the name dev/payments.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjh134l4yuwrl63cfi12k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjh134l4yuwrl63cfi12k.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://vator.tv/news/2016-12-29-when-stripe-was-young-the-early-years" rel="noopener noreferrer"&gt;&lt;em&gt;Image Source&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As indicated by its initial name, Stripe was aimed towards developers at first. The founders then realized that Stripe solves problems the entire internet faced. &lt;a href="https://www.fastcompany.com/1813087/inside-stripe-paypal-competitor-backed-paypal-founders-peter-thiel-elon-musk" rel="noopener noreferrer"&gt;According to Patrick&lt;/a&gt;, existing solutions like PayPal and Google Checkout were “these confusing things,” and they wanted to solve what these existing solutions couldn’t.&lt;/p&gt;

&lt;p&gt;Stripe reshaped the payment landscape, making it easy for anyone to start selling online, while providing a payment infrastructure that facilitated building custom setups. A lot of startups afterward took it upon themselves to cover other areas of the ecommerce infrastructure with similar developer-first approaches.&lt;/p&gt;

&lt;h2&gt;
  
  
  2020s and Ecommerce Modularization
&lt;/h2&gt;

&lt;p&gt;The ecommerce landscape has evolved to include not only web as a sales channel, but also mobile apps, social apps, and more. This spawned the need for ecommerce solutions that provide &lt;a href="https://medusajs.com/blog/why-headless-commerce-is-the-perfect-solution-for-omnichannel-support/" rel="noopener noreferrer"&gt;omnichannel&lt;/a&gt; support, frontend flexibility, and an extensive feature offering.&lt;/p&gt;

&lt;p&gt;To accommodate those needs, the 2020s saw new and existing ecommerce solutions start to adopt &lt;a href="https://medusajs.com/blog/headless-architecture/" rel="noopener noreferrer"&gt;headless&lt;/a&gt; and &lt;a href="https://medusajs.com/blog/composable-commerce/" rel="noopener noreferrer"&gt;composable&lt;/a&gt; architectures.  As the demands for ecommerce heavily evolved, the previous methods used to create ecommerce applications, which were initially monolithic and tightly coupled, are now limiting. Developers now seek modular solutions that are less rigid and open to customizations. This would remove the restrictions on their businesses and the need for hacky workarounds.&lt;/p&gt;

&lt;p&gt;The rise of point solutions that focus on specific domains within ecommerce has enabled this move towards modular solutions. Developers can combine these services and create a modern product stack that meets businesses’ exact requirements without compromising scalability.&lt;/p&gt;

&lt;p&gt;As ecommerce, and the technology sector as a whole, is a rapidly growing landscape, new trends will continue to rise and fall. However, one thing remains consistent throughout the years: businesses will continue to strive to exceed their current potential. To ensure they can innovate and build unique experiences, ecommerce solutions must adopt a modular approach ensuring scalability and flexibility.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>webdev</category>
      <category>computerscience</category>
    </item>
    <item>
      <title>Discovery Days at Medusa: How I created Cross Post tool from Notion</title>
      <dc:creator>Shahed Nasser</dc:creator>
      <pubDate>Thu, 16 Feb 2023 13:44:01 +0000</pubDate>
      <link>https://forem.com/medusajs/discovery-days-at-medusa-how-i-created-cross-post-tool-from-notion-491j</link>
      <guid>https://forem.com/medusajs/discovery-days-at-medusa-how-i-created-cross-post-tool-from-notion-491j</guid>
      <description>&lt;p&gt;I’m a technical writer at &lt;a href="https://medusajs.com/" rel="noopener noreferrer"&gt;Medusa&lt;/a&gt;. Part of my work includes managing the content on our blog and across platforms. Typically, we publish an article on our &lt;a href="https://medusajs.com/blog/" rel="noopener noreferrer"&gt;blog&lt;/a&gt; (which is built with &lt;a href="https://www.stackbit.com/" rel="noopener noreferrer"&gt;Stackbit&lt;/a&gt;), then cross-post that article across platforms such as Dev.to, Hashnode, and Medium.&lt;/p&gt;

&lt;p&gt;This process has become tedious and time-consuming. We publish at least 2 articles per week, and each article can take more than 30 minutes to be published across our blog and the 3 mentioned platforms. This includes adding the content in the format that each platform supports, uploading images if there are any, setting meta attributes such as description and meta tags, and more.&lt;/p&gt;

&lt;p&gt;So, I decided to build a cross posting tool that would automate this task. &lt;a href="https://github.com/shahednasser/cross-post-notion" rel="noopener noreferrer"&gt;cross-post-notion&lt;/a&gt; now allows us to publish an article from Notion to a GitHub repository (which holds the content of our main blog), Dev.to, Hashnode, and Medium in less than a minute. I decided to build this tool as part of our Discovery Days at Medusa.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are Discovery Days?
&lt;/h2&gt;

&lt;p&gt;Every Friday, the Medusa team members can dedicate half or their full day towards doing something different from their typical daily work. You can use this day to learn something new, read a book specific to your field, try out an interesting tool, or build a new tool.&lt;/p&gt;

&lt;p&gt;As I realized this tool would heavily reduce the time I spend on publishing content to our blog, I dedicated the past 4 Discovery Days (Fridays) to building this tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it Works
&lt;/h2&gt;

&lt;p&gt;You can use &lt;code&gt;cross-post-notion&lt;/code&gt; either as an NPX command, as a global CLI tool, or just by cloning the &lt;a href="https://github.com/shahednasser/cross-post-notion#notion" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Then, after adding the configurations necessary (more on this later), you can publish to all the different platforms using one command. You can also choose specific platforms to publish to.&lt;/p&gt;

&lt;h3&gt;
  
  
  Publishing to GitHub
&lt;/h3&gt;

&lt;p&gt;Publishing to GitHub can be useful for any blogging platform that uses Markdown files hosted in a GitHub repository. This includes Stackbit, GitHub Pages, 11ty, Hugo, Docusaurus, etc…&lt;/p&gt;

&lt;p&gt;Articles are pushed to GitHub as Markdown files with front matter metadata. The front matter includes by default the &lt;code&gt;title&lt;/code&gt; and &lt;code&gt;date&lt;/code&gt; of the article. The values of these fields are extracted from your Notion document, and you can specify the properties to extract these fields from.&lt;/p&gt;

&lt;p&gt;In addition, you can specify custom front matter fields to add and from which properties to extract them from Notion. You can also specify front matter fields with fixed values. This is helpful if you have front matter fields that you need to always add to your articles.&lt;/p&gt;

&lt;p&gt;If an article has images, the images are pushed to the GitHub repository as well. You can specify the path to upload the images to and the prefix to use within the article.&lt;/p&gt;

&lt;h3&gt;
  
  
  Publishing to Dev.to
&lt;/h3&gt;

&lt;p&gt;You can publish articles to dev.to with this tool, either on your personal account, or in an organization. Similar to posting to GitHub, the values of dev.to fields such as the title, tags, or series can all be extracted from Notion properties.&lt;/p&gt;

&lt;p&gt;One limitation to publishing to dev.to is that you can’t upload the images in the article using their APIs. So, you’ll still have to manually go in and upload any images in your article.&lt;/p&gt;

&lt;h3&gt;
  
  
  Publishing to Hashnode
&lt;/h3&gt;

&lt;p&gt;You can publish articles to Hashnode with this tool, either on your personal blog or in a specific publication. Similar to the other platforms, the values of Hashnode fields are extracted from Notion properties.&lt;/p&gt;

&lt;p&gt;One limitation to publishing to Hashnode is that it’s not possible to publish an article as a draft with their APIs. It’s only possible to choose to hide it from Hashnode’s feed. So, the article will automatically be available to your followers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Publishing to Medium
&lt;/h3&gt;

&lt;p&gt;You can publish articles to Medium with this tool, either on your personal account or in a specific publication. Similar to the other platforms, the values of Medium fields are extracted from Notion properties.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using this Tool
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Requirements
&lt;/h3&gt;

&lt;p&gt;This tool requires an Internal Notion Integration created and installed in your Notion workspace. The integration must have access to the pages you want to publish as well. You can learn more about this in &lt;a href="https://developers.notion.com/docs/create-a-notion-integration#step-1-create-an-integration" rel="noopener noreferrer"&gt;Notion’s documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install the Tool
&lt;/h3&gt;

&lt;p&gt;There are three ways you can use this tool:&lt;/p&gt;

&lt;p&gt;1. With NPX, which does not directly install the tool on your machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx cross-post-notion
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2. Install as a global CLI tool with NPM or Yarn:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# using NPM&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; cross-post-notion

&lt;span class="c"&gt;# using Yarn&lt;/span&gt;
yarn add cross-post-notion global
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3. Clone the GitHub repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/shahednasser/cross-post-notion.git
&lt;span class="nb"&gt;cd &lt;/span&gt;cross-post-notion
npm &lt;span class="nb"&gt;install
&lt;/span&gt;npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Set Environment Variables
&lt;/h3&gt;

&lt;p&gt;You need to set the following environment variables before you can use the tool. The requirements of the environment variables depends on which platforms you want to publish to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Notion:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;NOTION_TOKEN&lt;/code&gt;: (required for all platforms) Token on a Notion internal integration&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;GitHub:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;GH_TOKEN&lt;/code&gt;: (required) GitHub personal token&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GH_OWNER&lt;/code&gt;: (required) GitHub username&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GH_REPO&lt;/code&gt;: (required) GitHub Repository name&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GH_BRANCH&lt;/code&gt;: Branch name. Default is &lt;code&gt;master&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Dev.to:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;DEVTO_API_KEY&lt;/code&gt;: (required) Your personal dev.to API key. Learn how to retrieve it &lt;a href="https://developers.forem.com/api/v0" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DEVTO_ORG_ID&lt;/code&gt;: The ID of the organization to publish the article under. You can retrieve it either from the organization dashboard page, where the ID is the last part of the URL (&lt;code&gt;https://dev.to/dashboard/organization/ORG_ID&lt;/code&gt;). Alternatively, you can use Dev.to's &lt;a href="https://developers.forem.com/api/v0#tag/organizations/operation/getOrgUsers" rel="noopener noreferrer"&gt;List Organizations&lt;/a&gt; endpoint to find the ID.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Hashnode:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;HASHNODE_TOKEN&lt;/code&gt;: (required) Hashnode personal token&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;HASHNODE_PUB_ID&lt;/code&gt;: The ID of the publication to publish the article under. You can retrieve it either from the publication's dashboard page, where the ID is the second part of the URL (&lt;code&gt;https://hashnode.com/PUB_ID/dashboard&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Medium:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;MEDIUM_TOKEN&lt;/code&gt;: (required) Medium Integration Token. Can be retrieved from &lt;a href="https://medium.com/me/settings/security" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;MEDIUM_PUB_NAME&lt;/code&gt;: The name of the Medium publication. Must be the exact name as it is on Medium.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Add Configurations
&lt;/h3&gt;

&lt;p&gt;Configurations are used to customize how the article is published on the different platforms. They can be used to define what properties to extract data from in Notion, whether the article should be published or added as a draft, and more. You only need to set these configurations the first time you use the tool.&lt;/p&gt;

&lt;p&gt;By default, configurations are loaded from the file &lt;code&gt;config/default.json&lt;/code&gt; relative to the current directory you’re running the command from. Alternatively, you can pass the &lt;a href="https://github.com/shahednasser/cross-post-notion#config" rel="noopener noreferrer"&gt;path to the config file as an option&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The file can have the following configurations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"config"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"notion"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="s2"&gt;"options"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"skip_block_types"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;
            &lt;span class="s2"&gt;"toggle"&lt;/span&gt;
        &lt;span class="o"&gt;]&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;,
    &lt;span class="s2"&gt;"github"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="s2"&gt;"options"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"image_path"&lt;/span&gt;: &lt;span class="s2"&gt;"public"&lt;/span&gt;,
        &lt;span class="s2"&gt;"image_prefix"&lt;/span&gt;: &lt;span class="s2"&gt;"/"&lt;/span&gt;,
        &lt;span class="s2"&gt;"article_path"&lt;/span&gt;: &lt;span class="s2"&gt;"content"&lt;/span&gt;,
        &lt;span class="s2"&gt;"properties"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
          &lt;span class="s2"&gt;"title"&lt;/span&gt;: &lt;span class="s2"&gt;"Title for Blog"&lt;/span&gt;,
          &lt;span class="s2"&gt;"date"&lt;/span&gt;: &lt;span class="s2"&gt;"Publishing Date"&lt;/span&gt;,
          &lt;span class="s2"&gt;"slug"&lt;/span&gt;: &lt;span class="s2"&gt;"Slug"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;,
        &lt;span class="s2"&gt;"add_default_frontmatter"&lt;/span&gt;: &lt;span class="nb"&gt;true&lt;/span&gt;,
        &lt;span class="s2"&gt;"frontmatter_labels"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
          &lt;span class="s2"&gt;"title"&lt;/span&gt;: &lt;span class="s2"&gt;"title"&lt;/span&gt;,
          &lt;span class="s2"&gt;"date"&lt;/span&gt;: &lt;span class="s2"&gt;"date"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;,
        &lt;span class="s2"&gt;"extra_frontmatter"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
          &lt;span class="s2"&gt;"excerpt"&lt;/span&gt;: &lt;span class="s2"&gt;"this is description"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;,
        &lt;span class="s2"&gt;"extra_frontmatter_mapper"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
          &lt;span class="s2"&gt;"excerpt"&lt;/span&gt;: &lt;span class="s2"&gt;"Description"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;,
    &lt;span class="s2"&gt;"devto"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="s2"&gt;"options"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"should_publish"&lt;/span&gt;: &lt;span class="nb"&gt;false&lt;/span&gt;,
        &lt;span class="s2"&gt;"properties"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
          &lt;span class="s2"&gt;"title"&lt;/span&gt;: &lt;span class="s2"&gt;"Title for Dev.to"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;,
    &lt;span class="s2"&gt;"hashnode"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="s2"&gt;"options"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"should_publish"&lt;/span&gt;: &lt;span class="nb"&gt;true&lt;/span&gt;,
        &lt;span class="s2"&gt;"should_notify_followers"&lt;/span&gt;: &lt;span class="nb"&gt;false&lt;/span&gt;,
        &lt;span class="s2"&gt;"properties"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
          &lt;span class="s2"&gt;"title"&lt;/span&gt;: &lt;span class="s2"&gt;"Title for Hashnode"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;,
    &lt;span class="s2"&gt;"medium"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="s2"&gt;"options"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"should_publish"&lt;/span&gt;: &lt;span class="nb"&gt;false&lt;/span&gt;,
        &lt;span class="s2"&gt;"should_notify_followers"&lt;/span&gt;: &lt;span class="nb"&gt;false&lt;/span&gt;,
        &lt;span class="s2"&gt;"properties"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
          &lt;span class="s2"&gt;"title"&lt;/span&gt;: &lt;span class="s2"&gt;"Title for Medium"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can learn what each of these fields are and what more fields can be accepted in &lt;a href="https://github.com/shahednasser/cross-post-notion#available-configurations" rel="noopener noreferrer"&gt;the GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Run Post Command
&lt;/h3&gt;

&lt;p&gt;Grab the URL of a Notion document that includes an article you want to publish. It should be an internal URL which you can obtain from the Notion document by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click on Share at the top right.&lt;/li&gt;
&lt;li&gt;Click on “Copy link” at the bottom of the pop-up.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then, run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# using NPX&lt;/span&gt;
npx cross-post-notion post &amp;lt;url&amp;gt;

&lt;span class="c"&gt;# using CLI tool&lt;/span&gt;
cross-post-notion post &amp;lt;url&amp;gt;

&lt;span class="c"&gt;# using cloned repository&lt;/span&gt;
npm start post &amp;lt;url&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where &lt;code&gt;&amp;lt;url&amp;gt;&lt;/code&gt; is the URL to the Notion document.&lt;/p&gt;

&lt;p&gt;If all environment variables are set correctly, you should see logged in your terminal that the article was published on each of the platforms.&lt;/p&gt;

&lt;p&gt;You can also specify which platforms you want to publish the article on using the &lt;code&gt;-p&lt;/code&gt; option:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx cross-post-notion post &amp;lt;url&amp;gt; &lt;span class="nt"&gt;-p&lt;/span&gt; devto hashnode
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This would publish the article only on &lt;a href="http://Dev.to" rel="noopener noreferrer"&gt;Dev.to&lt;/a&gt; and Hashnode.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn More about this Tool
&lt;/h2&gt;

&lt;p&gt;You can learn more about this tool and its limitations by checking out the &lt;a href="https://github.com/shahednasser/cross-post-notion" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;. If you use it, let me know if it helps out! Although I’m mainly focused on using it for publishing articles at Medusa, feel free to open issues and contribute to further improve the tool.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Be sure to check out &lt;a href="https://github.com/medusajs/medusa" rel="noopener noreferrer"&gt;Medusa, a composable ecommerce platform, on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>discuss</category>
    </item>
    <item>
      <title>8 Tips to Optimize Internal Search in Ecommerce Websites</title>
      <dc:creator>Shahed Nasser</dc:creator>
      <pubDate>Thu, 02 Feb 2023 13:56:24 +0000</pubDate>
      <link>https://forem.com/medusajs/8-tips-to-optimize-internal-search-in-ecommerce-websites-1h8a</link>
      <guid>https://forem.com/medusajs/8-tips-to-optimize-internal-search-in-ecommerce-websites-1h8a</guid>
      <description>&lt;p&gt;While the value of external search optimization (SEO) is widely known, many ecommerce websites overlook the value of the hidden gem that is internal search engines. &lt;a href="https://neilpatel.com/blog/site-search-killing-your-conversion/" rel="noopener noreferrer"&gt;Only 15% of businesses&lt;/a&gt; dedicate resources into perfecting the site search. This is worrying as &lt;a href="https://www.forrester.com/report/Googleize+Your+SiteSearch+Experience/-/E-RES124541" rel="noopener noreferrer"&gt;68% of customers&lt;/a&gt; do not return to an ecommerce website that provided a bad search experience.&lt;/p&gt;

&lt;p&gt;When building an internal search engine, businesses should provide a search experience that matches their products and audience. This requires configuring your search engine’s ranking rules, synonyms, faceted search, and more.&lt;/p&gt;

&lt;p&gt;When we designed our Search APIs at &lt;a href="https://github.com/medusajs/medusa" rel="noopener noreferrer"&gt;Medusa&lt;/a&gt;, we abstracted the search functionalities to allow developers and businesses to either implement custom logic, or, more commonly, use search services like &lt;a href="https://docs.medusajs.com/add-plugins/meilisearch" rel="noopener noreferrer"&gt;MeiliSearch&lt;/a&gt; or &lt;a href="https://docs.medusajs.com/add-plugins/algolia" rel="noopener noreferrer"&gt;Algolia&lt;/a&gt;. These services continuously optimize their search functionalities to ensure that your ecommerce website provides a good search experience.&lt;/p&gt;

&lt;p&gt;This article explains some tips for configuring and optimizing your search engine to provide a better search experience for your customers.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What is Medusa?&lt;/strong&gt; &lt;a href="https://medusajs.com/" rel="noopener noreferrer"&gt;Medusa&lt;/a&gt; provides bespoke, modular and open commerce infrastructure for developers. Medusa’s different components and APIs are built in abstract layers, making it easier for developers to customize the core functionalities for different business use cases.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Some Search Statistics
&lt;/h2&gt;

&lt;p&gt;If you’re unsure of the importance of internal search engines, here are some statistics that emphasize their effect on ecommerce sales:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.forrester.com/report/MustHave+eCommerce+Features/-/E-RES89561" rel="noopener noreferrer"&gt;43% of customers&lt;/a&gt; on retail ecommerce websites go straight to the internal search engine.&lt;/li&gt;
&lt;li&gt;Internal search engines are widely used by the most valuable customers; customers that use the internal search engine are &lt;a href="https://resources.algolia.com/ecommerce/40-stats-on-e-commerce-search-and-kpis" rel="noopener noreferrer"&gt;2.4 times more likely to make a purchase&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Customers that use the internal search engine &lt;a href="https://resources.algolia.com/ecommerce/40-stats-on-e-commerce-search-and-kpis" rel="noopener noreferrer"&gt;spend 2.6 more times than those who don’t search&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.thinkwithgoogle.com/marketing-resources/micro-moments/meet-needs-i-want-to-buy-moments/" rel="noopener noreferrer"&gt;39% of customers&lt;/a&gt; are influenced to make purchase by relevant search results.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Tips to Optimize Internal Search Engines
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Ranking Rules&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Ranking rules are used to decide which search result shows first. A basic approach would show the results having the same exact words in the name of the product first. Although this is fine, but in a lot of cases you must take into account other attributes such as the category of the product or its stock amount.&lt;/p&gt;

&lt;p&gt;You might also want to boost the ranking of some products over others due to a marketing strategy. For example, you might want to show products having a discount higher than those without a discount.&lt;/p&gt;

&lt;p&gt;Defining your ranking rules can help customers find exactly what they need and influence them to make a purchase.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Typo Tolerance&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Customers often make typos while searching. If a search engine shows them results based on their exact query, they’ll typically land on no results pages, which leads them to making no purchase.&lt;/p&gt;

&lt;p&gt;An internal search engine must be optimized to detect and resolve typos, providing the customer with the results they’re looking for. Most search engine services utilize machine learning to ensure typo tolerance and provide a better search experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Search Analytics&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Search analytics can be a minefield of valuable information about your customers. It can help you understand what are the most searched for items. Coupled with other analytics you have related to purchases, you can figure out which of these items result from search results and, in turn, boost their ranking.&lt;/p&gt;

&lt;p&gt;Search analytics can also help you figure out which queries lead to no search results. This can be useful to understand what type of products your customers are looking for in your ecommerce website. You can also utilize this to define related results for these search queries.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Synonyms&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Often, multiple words have a similar meaning. For example, some customers might use “shirt” and “t-shirt” interchangeably. This can also be affected by different customer cultures or locations.&lt;/p&gt;

&lt;p&gt;A good search engine should provide synonyms that would allow the customers to land on the same search results, despite which synonym they use. Obviously, it would be difficult to cater to every possible synonym. You can utilize here the search analytics mentioned earlier to figure out what synonyms are necessary to define.&lt;/p&gt;

&lt;h3&gt;
  
  
  Parsing Search Queries
&lt;/h3&gt;

&lt;p&gt;According to &lt;a href="https://www.thinkwithgoogle.com/consumer-insights/consumer-trends/trending-visual-stories/2022-year-in-search/" rel="noopener noreferrer"&gt;Google’s 2022 Year in Review&lt;/a&gt;, there’s a +75% year-on-year (YOY) increase in search queries related to saving money, such as “cheap electric cars”. Some customers might use other search queries, such as “shirts under $20”.&lt;/p&gt;

&lt;p&gt;Although it might not be highly necessary to account for these search queries, parsing them and showing the customer exactly what they need can show customers that you understand them. Consequently, it can lead to a better search experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Faceted Search&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Faceted search enables users to narrow down their search queries by providing filters that allow them to be more specific.&lt;/p&gt;

&lt;p&gt;For instance, by using this search feature, someone looking for a Batman shirt can filter out books and videos about the superhero and focus the search on the desired product.&lt;/p&gt;

&lt;p&gt;Filters can be as basic as showing different available categories, and can extend to adding more advanced choices such as material, brand, colors, sizes, and more.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;A/B Testing&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;A/B testing is the process of comparing the outcome or analytics two different versions of a feature or interfaces. In the context of internal search engines, A/B testing can be used to figure out which configurations drive more sales.&lt;/p&gt;

&lt;p&gt;For example, if you have two ranking rules, and you’re unsure which one would be better for your customers, you can use A/B testing to figure that out.&lt;/p&gt;

&lt;p&gt;There are many ways to run A/B testing. Algolia provides &lt;a href="https://resources.algolia.com/a-b-testing/5-ways-to-a-b-test-your-search-for-relevance" rel="noopener noreferrer"&gt;steps to run an A/B test for search relevance&lt;/a&gt;. Search services may provide this functionality differently.&lt;/p&gt;

&lt;h3&gt;
  
  
  Optimized No-Results Page
&lt;/h3&gt;

&lt;p&gt;Despite all the configurations and optimizations you may put in place to improve your internal search engine, it’s still inevitable that the customer might land on a no-results page. Instead of trying to avoid the reality of a no-results page, you can embrace and utilize it for other opportunities.&lt;/p&gt;

&lt;p&gt;A no-results page can be optimized to include the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Recommended results: You can show results for a collection of products that drive sales. For example, discounted products or best-selling products.&lt;/li&gt;
&lt;li&gt;Suggested queries: You can show queries that are similar to the customer’s query, or queries you’re sure will provide many results.&lt;/li&gt;
&lt;li&gt;Personalized suggestions: You can use analytics related to the customer to provide suggestions. For example, you can show results based on the customer’s previous search results. You can also show results that are popular in the customer’s region or location.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Search Metrics to Measure
&lt;/h2&gt;

&lt;p&gt;Search metrics and analytics can be key in optimizing your internal search engine. They can help you better understand your customer and decide on new configurations if necessary.&lt;/p&gt;

&lt;p&gt;Some search metrics that you should measure are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Most Searched Products:&lt;/strong&gt; this metric can help you understand what your customers look for the most in your website. It can be an opportunity to find new products for your store.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Searched Products Leading to Highest Conversions:&lt;/strong&gt; just because a product is searched for the most does not mean it’s the product that leads to conversion and revenue. Combining the Most Searched Products metric with the Conversion metric can be useful to decide on ranking rules.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;New Searches:&lt;/strong&gt; new searches wouldn’t rank high in the Most Searched Products metric, but they can provide useful insights to the new and upcoming trends in your industry.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No Result Searches:&lt;/strong&gt; this metric shows which search queries lead to no-result pages. This can be used to define synonyms or search suggestions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Optimizing your internal search engine can be a powerful tool to improve your ecommerce sales. By carefully configuring your ranking rules, typo tolerance, synonyms, faceted search, and parsing search queries, you can provide a better search experience for your customers.&lt;/p&gt;

&lt;p&gt;Additionally, by measuring search metrics such as Most Searched Products, Searched Products Leading to Highest Conversions, New Searches, and No Result Searches, you can better understand your customer’s needs and optimize your search engine accordingly.&lt;/p&gt;

&lt;p&gt;By implementing these tips, you can be sure of providing an improved internal search engine experience for your customers.&lt;/p&gt;

&lt;p&gt;Medusa is a composable commerce engine that allows you to integrate whichever search engine you find most suitable for you. If you’re interested in Medusa, check out the &lt;a href="https://docs.medusajs.com/usage/create-medusa-app" rel="noopener noreferrer"&gt;quickstart guide&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>blockchain</category>
      <category>web3</category>
      <category>crypto</category>
    </item>
    <item>
      <title>Medusa v1.7.3: Sales Channels out of Beta, Improvements to events, and more!</title>
      <dc:creator>Shahed Nasser</dc:creator>
      <pubDate>Thu, 12 Jan 2023 12:23:09 +0000</pubDate>
      <link>https://forem.com/medusajs/medusa-v173-sales-channels-out-of-beta-improvements-to-events-and-more-29o9</link>
      <guid>https://forem.com/medusajs/medusa-v173-sales-channels-out-of-beta-improvements-to-events-and-more-29o9</guid>
      <description>&lt;p&gt;Time for another exciting release of Medusa!&lt;/p&gt;

&lt;p&gt;Version 1.7.3 moves Sales Channels and Publishable API Keys out of beta, introduces improvements for events system and payment sessions, and more.&lt;/p&gt;

&lt;p&gt;Keep reading below to learn more about all the new changes in this release of Medusa!&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Update Medusa?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Medusa Server
&lt;/h3&gt;

&lt;p&gt;You can update your Medusa server using the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @medusajs/medusa@latest @medusajs/medusa-cli@latest medusa-interfaces@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, please follow &lt;a href="https://docs.medusajs.com/advanced/backend/upgrade-guides/1-7-3"&gt;the upgrade guide&lt;/a&gt; for additional required steps.&lt;/p&gt;

&lt;h3&gt;
  
  
  Medusa Admin
&lt;/h3&gt;

&lt;p&gt;You can also update the Medusa Admin by pulling changes from the &lt;a href="https://github.com/medusajs/admin"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sales Channels
&lt;/h2&gt;

&lt;p&gt;We first introduced Sales Channels in v1.3.5 of Medusa. However, as this feature was in beta mode, it was guarded by feature flags. As of v1.7.3, it is enabled by default.&lt;/p&gt;

&lt;p&gt;If you’re not familiar with what sales channels are, they allow merchants to create separate channels they sell their products in. Then, they can specify which sales channels are available in that sales channel.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--d0P0ccuN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/on1ew2f7m8to9axwp4kc.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--d0P0ccuN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/on1ew2f7m8to9axwp4kc.jpg" alt="Image description" width="880" height="666"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Subsequently, this would lead to showing customers only products available in their sales channels.&lt;/p&gt;

&lt;p&gt;For example, in a B2B use case, a merchant can create a B2B sales channel with products only available for B2B customers. Other types of customers wouldn’t be able to purchase these products.&lt;/p&gt;

&lt;p&gt;Learn more about Sales Channels in &lt;a href="https://docs.medusajs.com/advanced/backend/sales-channels/"&gt;this documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Publishable API Keys
&lt;/h2&gt;

&lt;p&gt;Publishable API Keys are used to associate resources with an API key. Then, when that publishable API key is used on the client-side, retrieving or processing data is only performed on the defined scope of resources for this API key.&lt;/p&gt;

&lt;p&gt;This can be useful when you have resources that you have to pass as parameters for every or most requests.&lt;/p&gt;

&lt;p&gt;An example of this is using sales channels on the storefront. You would need to pass the ID of the sales channel to the list products and the create cart requests, among others. This approach is error-prone and can be a hassle during development.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--E6G_lAuN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/38d1t93k41d1ukb9bkpa.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--E6G_lAuN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/38d1t93k41d1ukb9bkpa.jpg" alt="Image description" width="880" height="664"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With Publishable API keys, you can just pass the API key in the header of the request. When using tools like Medusa JS Client or Medusa React, you only need to pass the API key once, and it’ll automatically be added to all requests.&lt;/p&gt;

&lt;p&gt;At the moment, publishable API keys can only be associated with sales channels.&lt;/p&gt;

&lt;p&gt;Learn more about Publishable API Keys in &lt;a href="https://docs.medusajs.com/advanced/backend/publishable-api-keys/"&gt;this documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Improvements in the Events System
&lt;/h2&gt;

&lt;p&gt;Medusa’s events system allows you to subscribe a handler method to certain events. The handler method would perform asynchronous action when the event is triggered.&lt;/p&gt;

&lt;p&gt;For example, you can subscribe to the &lt;code&gt;order.placed&lt;/code&gt; event so that when it’s triggered, you send the customer an email.&lt;/p&gt;

&lt;p&gt;In v1.7.3, we introduce improvements into the events system, the main being the ability to retry failed handler methods. Errors can commonly occur in handler methods, either due to errors in the logic of the method or due to errors with third-party services.&lt;/p&gt;

&lt;p&gt;You can now specify a unique ID of a subscribed event handler and specify how many times it should be retried in case it fails. Improvements also include enhanced readability and allowing for horizontal scaling.&lt;/p&gt;

&lt;p&gt;Learn more about the events architecture in &lt;a href="https://docs.medusajs.com/advanced/backend/events/architecture"&gt;this documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  New Payment Session Flow
&lt;/h2&gt;

&lt;p&gt;In v1.7.3, we refactored our payment session flow to improve its performance. The new change reduces the amount of times Medusa communicated with the third-party payment providers.&lt;/p&gt;

&lt;p&gt;This improvement does not require any changes in how you use and implement the checkout flow or any payment-related flow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Redesigned Admin Sidebar
&lt;/h2&gt;

&lt;p&gt;The admin sidebar is now redesigned for a clearer design. The team and users section is now removed, and the sidebar is only focused on important links such as Orders and Products.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--B8zg5MtV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5xngrh94faro5spgiczt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--B8zg5MtV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5xngrh94faro5spgiczt.png" alt="Image description" width="230" height="476"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In upcoming versions, we’ll introduce additional redesign changes such as collapsible sidebar, history, improved help center, and more.&lt;/p&gt;

&lt;h2&gt;
  
  
  Documentation Changes
&lt;/h2&gt;

&lt;p&gt;In our documentation we’ve implemented general fixes and added new documentation pages such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Storefront guide on &lt;a href="https://docs.medusajs.com/advanced/storefront/customer-profiles"&gt;how to implement customer profiles&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;  Gift cards &lt;a href="https://docs.medusajs.com/advanced/backend/gift-cards/"&gt;conceptual guide&lt;/a&gt; along with how-to guides for &lt;a href="https://docs.medusajs.com/advanced/storefront/use-gift-cards"&gt;storefronts&lt;/a&gt; and &lt;a href="https://docs.medusajs.com/advanced/admin/manage-gift-cards"&gt;admins&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://docs.medusajs.com/advanced/storefront/implement-claim-order"&gt;Claim-order flow&lt;/a&gt; documentation.&lt;/li&gt;
&lt;li&gt;  Development guide on &lt;a href="https://docs.medusajs.com/admin/development"&gt;how to customize Medusa Admin&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Did you miss out on?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://medusajs.com/blog/implementing-5-features-in-nike-ecommerce-store-with-medusa/"&gt;Implementing 5 features in Nike’s Ecommerce Store with Medusa&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://medusajs.com/blog/how-we-improved-our-documentation/"&gt;How we improved our documentation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://medusajs.com/blog/top-10-medusa-blog-posts-2022/"&gt;Top 10 Medusa Blog Posts of 2022&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://medusajs.com/blog/next-js-storefront-food-ecommerce-restaurant-medusa-paystack/"&gt;Next.js Storefront: Building a Food Ecommerce Restaurant with Medusa and Paystack&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://medusajs.com/blog/13-live-chat-widgets-for-ecommerce-stores-in-2023/"&gt;10+ Live Chat Widgets for Ecommerce Stores in 2023&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Should you have any issues or questions related to Medusa, then feel free to reach out to the Medusa team via &lt;a href="https://discord.gg/F87eGuwkTp"&gt;Discord&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>opensource</category>
      <category>programming</category>
    </item>
    <item>
      <title>How I built Nike’s Ecommerce Features with Medusa</title>
      <dc:creator>Shahed Nasser</dc:creator>
      <pubDate>Tue, 10 Jan 2023 13:06:52 +0000</pubDate>
      <link>https://forem.com/medusajs/how-i-built-nikes-ecommerce-features-with-medusa-131</link>
      <guid>https://forem.com/medusajs/how-i-built-nikes-ecommerce-features-with-medusa-131</guid>
      <description>&lt;p&gt;Nike is the world’s &lt;a href="https://www.statista.com/topics/1243/nike/#editorsPicks" rel="noopener noreferrer"&gt;largest supplier and manufacturer of athletic shoes&lt;/a&gt;. Part of what makes Nike successful is its ecommerce website. Its net sales has &lt;a href="https://www.statista.com/forecasts/1218320/nike-revenue-development-ecommercedb" rel="noopener noreferrer"&gt;crossed 12 billion USD in 2022&lt;/a&gt;. This makes up approximately 65% of Nike’s &lt;a href="https://www.statista.com/statistics/294512/nike-s-dtc-revenue-worldwide/" rel="noopener noreferrer"&gt;global direct-to-customer revenue in 2022 (18.73 billion USD)&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Nike realized early-on that they needed to implement an &lt;a href="https://medium.com/nikeengineering/accelerate-transforming-nike-digital-apis-with-graphql-a541aebe4d5e" rel="noopener noreferrer"&gt;omnichannel experience&lt;/a&gt; to provide their customers with the best user experience. This led them to build their own headless commerce platforms utilizing &lt;a href="https://medium.com/nikeengineering/nikes-cloud-journey-at-aws-re-invent-aa2e6eaefa55" rel="noopener noreferrer"&gt;microservices and cloud technologies&lt;/a&gt;.&lt;/p&gt;

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

&lt;ol&gt;
&lt;li&gt;Digital Gift Cards&lt;/li&gt;
&lt;li&gt;Free shipping for members&lt;/li&gt;
&lt;li&gt;Search suggestions and autocomplete&lt;/li&gt;
&lt;li&gt;Member-only products&lt;/li&gt;
&lt;li&gt;Birthday deals&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What is Medusa?
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/medusajs" rel="noopener noreferrer"&gt;
        medusajs
      &lt;/a&gt; / &lt;a href="https://github.com/medusajs/medusa" rel="noopener noreferrer"&gt;
        medusa
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Building blocks for digital commerce
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;p&gt;
  &lt;a href="https://www.medusajs.com" rel="nofollow noopener noreferrer"&gt;
  
    
    
    &lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F59018053%2F229103726-e5b529a3-9b3f-4970-8a1f-c6af37f087bf.svg" class="article-body-image-wrapper"&gt;&lt;img alt="Medusa logo" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F59018053%2F229103726-e5b529a3-9b3f-4970-8a1f-c6af37f087bf.svg"&gt;&lt;/a&gt;
    
  &lt;/a&gt;
&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;
  Medusa
&lt;/h1&gt;
&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h4 class="heading-element"&gt;
  &lt;a href="https://docs.medusajs.com" rel="nofollow noopener noreferrer"&gt;Documentation&lt;/a&gt; |
  &lt;a href="https://www.medusajs.com" rel="nofollow noopener noreferrer"&gt;Website&lt;/a&gt;
&lt;/h4&gt;
&lt;/div&gt;

&lt;p&gt;
  Building blocks for digital commerce
&lt;/p&gt;

&lt;p&gt;
  &lt;a href="https://github.com/medusajs/medusa/blob/develop/LICENSE" rel="noopener noreferrer"&gt;
    &lt;img src="https://camo.githubusercontent.com/6581c31c16c1b13ddc2efb92e2ad69a93ddc4a92fd871ff15d401c4c6c9155a4/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d626c75652e737667" alt="Medusa is released under the MIT license."&gt;
  &lt;/a&gt;
  &lt;a href="https://github.com/medusajs/medusa/blob/develop/CONTRIBUTING.md" rel="noopener noreferrer"&gt;
    &lt;img src="https://camo.githubusercontent.com/11c502cb0edd6eac274e462c7a70981ee26fde99043dba967b732d371efa2b87/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5052732d77656c636f6d652d627269676874677265656e2e7376673f7374796c653d666c6174" alt="PRs welcome!"&gt;
  &lt;/a&gt;
 &lt;/p&gt;
&lt;p&gt;
  &lt;a href="https://twitter.com/intent/follow?screen_name=medusajs" rel="nofollow noopener noreferrer"&gt;
    &lt;img src="https://camo.githubusercontent.com/6c164f14e4a24c801d3396eff84dbc5a7d8a3a6458b1c09ebbaaa0006d244a8c/68747470733a2f2f696d672e736869656c64732e696f2f747769747465722f666f6c6c6f772f6d65647573616a732e7376673f6c6162656c3d466f6c6c6f77253230406d65647573616a73" alt="Follow @medusajs"&gt;
  &lt;/a&gt;&lt;a href="https://discord.gg/medusajs" rel="nofollow noopener noreferrer"&gt;
    &lt;img src="https://camo.githubusercontent.com/4921ed6603bc6780b3892f60abb1cd1143568cf1595701546c32f7a2619d9daf/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f636861742d6f6e253230646973636f72642d3732383944412e737667" alt="Discord Chat"&gt;
  &lt;/a&gt;
&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Getting Started&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;Visit the &lt;a href="https://docs.medusajs.com/create-medusa-app" rel="nofollow noopener noreferrer"&gt;Quickstart Guide&lt;/a&gt; to set up a server.&lt;/p&gt;

&lt;p&gt;Visit the &lt;a href="https://docs.medusajs.com/development/backend/prepare-environment" rel="nofollow noopener noreferrer"&gt;Docs&lt;/a&gt; to learn more about our system requirements.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What is Medusa&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Learn more about &lt;a href="https://docs.medusajs.com/development/fundamentals/architecture-overview" rel="nofollow noopener noreferrer"&gt;Medusa’s architecture&lt;/a&gt; and &lt;a href="https://docs.medusajs.com/modules/overview" rel="nofollow noopener noreferrer"&gt;commerce modules&lt;/a&gt; in the Docs.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Roadmap, Upgrades &amp;amp; Plugins&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;You can view the planned, started and completed features in the &lt;a href="https://github.com/medusajs/medusa/discussions/categories/roadmap" rel="noopener noreferrer"&gt;Roadmap discussion&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Follow the &lt;a href="https://docs.medusajs.com/upgrade-guides/" rel="nofollow noopener noreferrer"&gt;Upgrade Guides&lt;/a&gt; to keep your Medusa project up-to-date.&lt;/p&gt;

&lt;p&gt;Check out all &lt;a href="https://medusajs.com/plugins/" rel="nofollow noopener noreferrer"&gt;available Medusa plugins&lt;/a&gt;.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Community &amp;amp; Contributions&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;The community and core team are available in &lt;a href="https://github.com/medusajs/medusa/discussions" rel="noopener noreferrer"&gt;GitHub Discussions&lt;/a&gt;, where you…&lt;/p&gt;
&lt;/div&gt;


&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/medusajs/medusa" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;p&gt;Using an open source ecommerce platform gives you many of the benefits of customization that a custom platform like Nike’s has. &lt;a href="https://medusajs.com" rel="noopener noreferrer"&gt;Medusa&lt;/a&gt; is built for developers that wants to build bespoke commerce experiences with a composable commerce platforms in Node.js.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Medusa also provides advanced ecommerce features related to automated RMA flows, sales channels, product and order management, and much more.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;&lt;a href="https://github.com/medusajs/medusa" rel="noopener noreferrer"&gt;If you like this article, please give Medusa a 🌟 on GitHub&lt;/a&gt;&lt;/strong&gt;
&lt;/h3&gt;

&lt;h2&gt;
  
  
  Feature 1: Digital Gift Cards
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F29kul70bofde4d90t6d0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F29kul70bofde4d90t6d0.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Medusa allows creating gift cards with custom images, unlimited amounts, description, tags, and more.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftk0kova4v1lenbts5l15.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftk0kova4v1lenbts5l15.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Merchants can also specify for a specific denomination or amount the price for different currencies.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fip1jzwrv0mtcgfq60ujw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fip1jzwrv0mtcgfq60ujw.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Customers that purchase this gift card will have it delivered to their email and can use it during checkout.&lt;/p&gt;

&lt;p&gt;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 &lt;code&gt;metadata&lt;/code&gt; attribute. This attribute can be used to store custom data.&lt;/p&gt;

&lt;p&gt;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.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;recipient_email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;example@gmail.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Feature 2: Free Shipping for Members
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2x5e6kmeyqd49whdpu0i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2x5e6kmeyqd49whdpu0i.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;In Medusa, this feature can be implemented with a combination of available features: &lt;a href="https://docs.medusajs.com/advanced/backend/customer-groups/" rel="noopener noreferrer"&gt;Customer Groups&lt;/a&gt;, &lt;a href="https://docs.medusajs.com/advanced/backend/discounts/" rel="noopener noreferrer"&gt;Discounts&lt;/a&gt;, and &lt;a href="https://docs.medusajs.com/advanced/backend/subscribers/overview" rel="noopener noreferrer"&gt;Subscribers&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Customer Groups
&lt;/h3&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffihth2mqw08qvfcunu9e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffihth2mqw08qvfcunu9e.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Discounts
&lt;/h3&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Discounts can also have conditions. For example, you can specify which products this discount can be used on or which customer groups.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5kjb6ww3os1ob4q3skue.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5kjb6ww3os1ob4q3skue.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Subscribers
&lt;/h3&gt;

&lt;p&gt;The last step required is to automatically:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add customers to the Members customer group on registration.&lt;/li&gt;
&lt;li&gt;Add the free shipping discount to customers of the Members customer group.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;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.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CustomerGroupService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CustomerService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;EventBusService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@medusajs/medusa&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;InjectedProperties&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;eventBusService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;EventBusService&lt;/span&gt;
  &lt;span class="na"&gt;customerService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CustomerService&lt;/span&gt;
  &lt;span class="na"&gt;customerGroupService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CustomerGroupService&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MembersSubscriber&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;customerService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CustomerService&lt;/span&gt;
  &lt;span class="nx"&gt;customerGroupService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CustomerGroupService&lt;/span&gt;

  &lt;span class="nf"&gt;constructor &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;InjectedProperties&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customerService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customerService&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customerGroupService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customerGroupService&lt;/span&gt;

    &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eventBusService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;customer.created&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handleRegistration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;handleRegistration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//get Members customer group&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;membersGroup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customerGroupService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Members&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;take&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;membersGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;//Members group doesn't exist&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;//add customer to customer group&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customerGroupService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addCustomers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;membersGroup&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;MembersSubscriber&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, you need to create a subscriber that automatically adds the free shipping discount to members. The subscriber should subscribe to the events &lt;code&gt;cart.created&lt;/code&gt; and &lt;code&gt;cart.customer_updated&lt;/code&gt;. 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.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CartService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;EventBusService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@medusajs/medusa&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;InjectedDependencies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;eventBusService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;EventBusService&lt;/span&gt;
  &lt;span class="na"&gt;cartService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CartService&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MemberDiscountSubscriber&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;cartService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CartService&lt;/span&gt;
  &lt;span class="nx"&gt;discountCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;

  &lt;span class="nf"&gt;constructor &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;InjectedDependencies&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cartService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cartService&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;discountCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MEMBERSFREESHIP&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

    &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eventBusService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cart.created&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handleDiscount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eventBusService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cart.customer_updated&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handleDiscount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;handleDiscount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cartId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;

    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cartService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cartId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;discounts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;discountCode&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;MemberDiscountSubscriber&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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 &lt;code&gt;DiscountService&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature 3: Search Suggestions and Auto Complete
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3pj20n0ivv7dc3uat2d2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3pj20n0ivv7dc3uat2d2.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;In Medusa, advanced search functionalities can be implemented by integrating the server to third-party services like &lt;a href="https://docs.medusajs.com/add-plugins/algolia" rel="noopener noreferrer"&gt;Algolia&lt;/a&gt; or &lt;a href="https://docs.medusajs.com/add-plugins/meilisearch" rel="noopener noreferrer"&gt;MeiliSearch&lt;/a&gt;. This removes the need to implement these features from scratch, and allows utilizing best-in-brand services in your store.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxokffvrp2h3iugmbxfov.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxokffvrp2h3iugmbxfov.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature 4: Member-only Products
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiu9nhtvmjtpmcqknay3w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiu9nhtvmjtpmcqknay3w.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;In Medusa, this can be implemented with a combination of features: Customer Groups, Sales Channels, and Subscribers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Customer Groups
&lt;/h3&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sales Channels
&lt;/h3&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz6weglc24zp1y58ofv53.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz6weglc24zp1y58ofv53.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Subscribers
&lt;/h3&gt;

&lt;p&gt;The last step is to create two subscribers that handle the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add customers to the Members customer group on registration.&lt;/li&gt;
&lt;li&gt;Assign the Members sales channel to members’ carts.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The first one’s implementation was explained earlier, and you can find the code block in a previous section.&lt;/p&gt;

&lt;p&gt;As for the second, you need to create a subscriber that subscribes to the &lt;code&gt;cart.created&lt;/code&gt; and the &lt;code&gt;cart.customer_updated&lt;/code&gt; 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.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CartService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;EventBusService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;SalesChannel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;SalesChannelService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@medusajs/medusa&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;InjectedDependencies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;eventBusService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;EventBusService&lt;/span&gt;
  &lt;span class="na"&gt;cartService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CartService&lt;/span&gt;
  &lt;span class="na"&gt;salesChannelService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SalesChannelService&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MemberDiscountSubscriber&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;cartService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CartService&lt;/span&gt;
  &lt;span class="nx"&gt;salesChannelService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SalesChannelService&lt;/span&gt;

  &lt;span class="nf"&gt;constructor &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;InjectedDependencies&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cartService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cartService&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;salesChannelService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;salesChannelService&lt;/span&gt;

    &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eventBusService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cart.created&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handleCreate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eventBusService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cart.customer_updated&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handleCreate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;handleCreate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cartId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;

    &lt;span class="c1"&gt;//retrieve default sales channel&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;defaultChannel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;salesChannelService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;retrieveByName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Default Sales Channel&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;SalesChannel&lt;/span&gt;

    &lt;span class="c1"&gt;//retrieve sales channel&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;membersChannel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;salesChannelService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;retrieveByName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Members&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;SalesChannel&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;membersChannel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;//sales channel does not exist&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;//retrieve cart&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cartService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;retrieve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cartId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;relations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;customer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;customer.groups&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isMember&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;groups&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;some&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;group&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Members&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cartService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cartId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;sales_channel_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;isMember&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;membersChannel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;defaultChannel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;MemberDiscountSubscriber&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Feature 5: Birthday Deals
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzmv3235dyaiaopqrf4in.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzmv3235dyaiaopqrf4in.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scheduled Jobs
&lt;/h3&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;You can make use of the &lt;code&gt;metadata&lt;/code&gt; field to store custom fields such as &lt;code&gt;birthday&lt;/code&gt;. You’ll need to save it when the customer registers or allow the customer to set their birthday in their profile.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;checkBirthdaysJob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;jobSchedulerService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;jobSchedulerService&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;jobSchedulerService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;check-birthdays&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0 0 * * *&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//job to execute&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;customerService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;customerService&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;customers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customerService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;birthday&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

        &lt;span class="nx"&gt;customers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;//create discount/gift card&lt;/span&gt;
            &lt;span class="c1"&gt;//and send email&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;checkBirthdaysJob&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;To send the email to the customer, you’ll need to integrate a third-party plugin such as &lt;a href="https://docs.medusajs.com/add-plugins/sendgrid" rel="noopener noreferrer"&gt;SendGrid&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Do More with Medusa
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Check out the &lt;a href="https://docs.medusajs.com/quickstart/quick-start" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; for more details on what Medusa is and how to get started.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Should you have any issues or questions related to Medusa, then feel free to reach out to the Medusa team via &lt;a href="https://discord.gg/F87eGuwkTp" rel="noopener noreferrer"&gt;Discord&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>showdev</category>
      <category>opensource</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>The 10 Medusa Posts of 2022 that you need to read</title>
      <dc:creator>Shahed Nasser</dc:creator>
      <pubDate>Mon, 09 Jan 2023 13:54:40 +0000</pubDate>
      <link>https://forem.com/medusajs/the-10-medusa-posts-of-2022-that-you-need-to-read-5ak0</link>
      <guid>https://forem.com/medusajs/the-10-medusa-posts-of-2022-that-you-need-to-read-5ak0</guid>
      <description>&lt;p&gt;At &lt;a href="https://github.com/medusajs/medusa" rel="noopener noreferrer"&gt;Medusa&lt;/a&gt;, we invested more resources and focus into our content in 2022 and achieved some amazing results, including:&lt;/p&gt;

&lt;h2&gt;
  
  
  +172,000 article reads
&lt;/h2&gt;

&lt;h2&gt;
  
  
  +100 articles
&lt;/h2&gt;

&lt;h2&gt;
  
  
  +150 community writers
&lt;/h2&gt;

&lt;p&gt;In this article, we want to highlight 10 of the best and most-read articles we put out in 2022. If you want to stay up to date on our newest content and product releases, then &lt;a href="https://ky5eo2x1u81.typeform.com/newsletter" rel="noopener noreferrer"&gt;sign up to our newsletter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you want to become part of our community writer program, then you can read more &lt;a href="https://www.notion.so/Write-for-us-74a2bf43b4ce43eeba200382f599321a" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why content is important at Medusa
&lt;/h2&gt;

&lt;p&gt;As one of the most used platforms in the OS ecom space, we want to take an active part in educating developers not only about Medusa, but the space in general and the tools available. &lt;/p&gt;

&lt;p&gt;We tailor our content towards solving community problems, researching new trends and ideas in the open source and ecommerce realm, and share company-related insights. This allows us to stay connected with the developer community, especially with the help of our community writers.&lt;/p&gt;

&lt;p&gt;If you want to know more about Medusa itself, then you can check out our &lt;a href="https://docs.medusajs.com/" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; for details on its features and how to get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  10 Best Medusa Articles in 2022
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Announcement: Medusa's $8M USD Seed Round
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnqrpzm1r9x36q7ljazbs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnqrpzm1r9x36q7ljazbs.png" alt="https://medusajs.com/images/seedround.png" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medusajs.com/blog/announcement-8m-usd-seed-round-to-build-the-leaading-ecom-platform-for-devs" rel="noopener noreferrer"&gt;Read here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In July of 2022, we announced our $8M USD Seed round led by LocalGlobe and Dawn Capital. The story was also covered by &lt;a href="https://techcrunch.com/2022/07/15/medusa-shopify-open-source-e-commerce-javascript-developers/" rel="noopener noreferrer"&gt;TechCrunch&lt;/a&gt; and &lt;a href="https://www.businesswire.com/news/home/20220715005035/en/Medusa-Raises-8m-to-Build-the-Leading-E-Commerce-Platform-for-Developers" rel="noopener noreferrer"&gt;BusinessWire&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With the seed round, we will continue building amazing features in Medusa, building integrations and plugins for third-party tools, and providing the best developer experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  9 Best Ecommerce UX Practices From the World's Best Ecommerce Site
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fixdykemlksc6xizhkoyu.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fixdykemlksc6xizhkoyu.jpg" alt="https://medusajs.com/images/ux-practices.jpg" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medusajs.com/blog/9-best-ecommerce-ux-practices-with-examples" rel="noopener noreferrer"&gt;Read here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;McMaster-Carr has been crowned the title “best e-commerce site” by HackerNews users. As we are building Medusa with the aim to be the best ecommerce platform, this grew our curiosity which lead us to do a research around this website with the help of a community writer.&lt;/p&gt;

&lt;p&gt;We then came up with 9 best user experience (UX) practices we picked up from McMaster-Carr in the hopes of it being helpful for businesses and ecommerce developers everywhere.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating a React Native Ecommerce app with Medusa
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frftx9ccyp582n3p4yjg8.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frftx9ccyp582n3p4yjg8.jpg" alt="https://medusajs.com/images/react-native-ps.jpg" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medusajs.com/blog/creating-react-native-ecommerce-app-with-medusa" rel="noopener noreferrer"&gt;Read here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Mobile apps have become an essential requirements for all types of businesses, specifically ecommerce businesses. Medusa’s composable architecture makes it possible to integrate the headless server to any frontend framework or platform.&lt;/p&gt;

&lt;p&gt;This tutorial written by one of our community writers help readers interested in developing an ecommerce app for their Medusa server get started using React Native. It has become one of our top viewed articles of all time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Headless Architecture Explained to a 5-Year-Old
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2ex68j5dhrzacxkj7utq.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2ex68j5dhrzacxkj7utq.webp" alt="https://medusajs.com/images/Myjb_gcYY.webp" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medusajs.com/blog/headless-architecture" rel="noopener noreferrer"&gt;Read here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the past couple of years, headless has become a buzz-word used by different types of companies and solutions, including Medusa. You’ve probably heard of headless ecommerce or headless CMS, but what does it really mean?&lt;/p&gt;

&lt;p&gt;This article takes the challenge of simplifying the meaning of headless that a 5 year old can understand it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Medusa vs. Magento: Comparing two open source ecommerce frameworks
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbo54h0tri8q08tdm485j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbo54h0tri8q08tdm485j.png" alt="https://medusajs.com/images/article-cover1.png" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medusajs.com/blog/medusa-vs-magento-comparing-two-open-source-ecommerce-frameworks" rel="noopener noreferrer"&gt;Read here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Both Medusa and Magento are open source platforms with a long list of impressive ecommerce and development features. This article compares both platforms, listing what each has to offer in different domains including multi-currency and region support, payment and shipping providers, installation and deployment, and more.&lt;/p&gt;

&lt;h3&gt;
  
  
  Top 5 open source ecommerce platforms for B2B: A feature-by-feature review
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedusajs.com%2Fimages%2Ffeature-by-feature%2520%282%29.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedusajs.com%2Fimages%2Ffeature-by-feature%2520%282%29.jpg" alt="https://medusajs.com/images/feature-by-feature%20(2).jpg" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medusajs.com/blog/top-5-open-source-ecommerce-platforms" rel="noopener noreferrer"&gt;Read here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This article was also part of our December B2B launch. We conducted a research along with a community writer to put together a list of features that a B2B platform must have based on general B2B use cases.&lt;/p&gt;

&lt;p&gt;We then chose 5 top ecommerce platforms (including Medusa) and checked whether these features exist in those platforms. This will help any business or developer unsure about which platform to choose for their B2B business make a choice.&lt;/p&gt;

&lt;h3&gt;
  
  
  Case study: How Tekla improved conversion by 70% at record speed using Medusa
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fftvj0s4rrbphkskbnb0j.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fftvj0s4rrbphkskbnb0j.jpg" alt="https://medusajs.com/images/tekla-conversion-rate-2.jpg" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medusajs.com/blog/case-study-tekla-medusa" rel="noopener noreferrer"&gt;Read here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Tekla is textile brand operating in more than 50 countries around the world. After migrating from WooCommerce to Medusa, their conversion rate increased by 70% and they significantly reduced their customer service response hours, among other achievements.&lt;/p&gt;

&lt;p&gt;This article explores Tekla’s challenge with their previous solution, and how they utilized Medusa to bring their vision to life.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is Composable Commerce?
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Famap18bc4xhgtuxceslp.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Famap18bc4xhgtuxceslp.jpg" alt="https://medusajs.com/images/What_is_Composable_Commerce_.jpg" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medusajs.com/blog/composable-commerce" rel="noopener noreferrer"&gt;Read here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The comparison between Monolithic and Composable architectures have been the talk of the developer community in recent years. This is especially for ecommerce platforms, as we see more adoption of the composable architecture by new and existing tools.&lt;/p&gt;

&lt;p&gt;This article dives into the differences between the two architectures, and why we at Medusa believe composable commerce is the future.&lt;/p&gt;

&lt;h3&gt;
  
  
  Medusa Hackathon Winner Announcement
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedusajs.com%2Fimages%2Fhackathon-banner-large-purple-live%2520%287%29.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedusajs.com%2Fimages%2Fhackathon-banner-large-purple-live%2520%287%29.jpg" alt="https://medusajs.com/images/hackathon-banner-large-purple-live%20(7).jpg" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medusajs.com/blog/hackathon-winners/" rel="noopener noreferrer"&gt;Read here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In October of 2022, we hosted a Medusa Hackathon with cool prizes. We got +200 project submissions which exceeded our expectations.&lt;/p&gt;

&lt;p&gt;This article highlights the winners and runner up list. Check it out to see what cool projects were built with Medusa!&lt;/p&gt;

&lt;h3&gt;
  
  
  Case Study: How Palmes built a Global on-brand Store in &amp;lt;6 Weeks with Medusa
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fezuigdku28hv61gqkyps.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fezuigdku28hv61gqkyps.jpg" alt="https://medusajs.com/images/palmes.jpg" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medusajs.com/blog/palmes-use-case/" rel="noopener noreferrer"&gt;Read here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Palmes is a meanswear brand with roots in tennis culture for wearing on and off court that sells tennis wear and accessories. By creating their ecommerce store with Medusa, they utilized advanced ecommerce features such as selling in over 40 countries and building a fully-tailored user experience in their storefront in less than 6 weeks.&lt;/p&gt;

&lt;p&gt;This article explores Palmes story of how they used Medusa to create their ecommerce platform with a unique storefront.&lt;/p&gt;

&lt;h2&gt;
  
  
  Close contestants
&lt;/h2&gt;

&lt;p&gt;Other popular articles that we believe are worth highlighting:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://medusajs.com/blog/how-to-create-comic-book-online-store-with-medusa-gatsby-paypal-meilisearch" rel="noopener noreferrer"&gt;How to Create a Comic Book Online Store with Medusa, Gatsby, PayPal, and MeiliSearch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medusajs.com/blog/ecommerce-storefront-medusa-strapi-remix" rel="noopener noreferrer"&gt;Create an Ecommerce Storefront with Medusa, Strapi, and Remix&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medusajs.com/blog/build-a-video-game-store-with-medusa-next-js-stripe-algolia" rel="noopener noreferrer"&gt;Build A Video Game Store with Medusa, Next.js, Stripe, and Algolia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medusajs.com/blog/explained-5-levels-of-difficulty-ecommerce-by-medusa" rel="noopener noreferrer"&gt;Explained in 5 Levels of Difficulty: Ecommerce by Medusa&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medusajs.com/blog/how-to-create-a-wishlist-with-medusa" rel="noopener noreferrer"&gt;How to Create a Wishlist in Medusa&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medusajs.com/blog/why-headless-commerce-is-the-perfect-solution-for-omnichannel-support" rel="noopener noreferrer"&gt;Why Headless Commerce is the Perfect Solution for Omnichannel Support&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medusajs.com/blog/how-to-add-product-reviews-to-your-medusa-server-and-next-js-storefront" rel="noopener noreferrer"&gt;How to Add Product Reviews to Your Medusa Server and Next.js Storefront&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medusajs.com/blog/medusa-b2b-part-1" rel="noopener noreferrer"&gt;Medusa B2B Part 1: Set Up B2B Store&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medusajs.com/blog/magento-to-medusa" rel="noopener noreferrer"&gt;Magento Source Plugin: Import your Magento Data into Medusa&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medusajs.com/blog/how-to-create-an-ecommerce-app-with-medusa-and-ionic" rel="noopener noreferrer"&gt;How to Create An Ecommerce App with Medusa and Ionic&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medusajs.com/blog/how-to-create-a-one-page-swag-store-in-15-minutes-using-medusa-express" rel="noopener noreferrer"&gt;How to Create a One-Page Swag Store In 15 Minutes Using Medusa.Express&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  More Top Content in 2023
&lt;/h2&gt;

&lt;p&gt;Our focus on providing top content does not end here. We will put more focus on putting out more events, campaigns, launches, and other general articles.&lt;/p&gt;

&lt;p&gt;For a full view of our articles, you can visit our blog at &lt;a href="http://medusajs.com/blog" rel="noopener noreferrer"&gt;medusajs.com/blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;What was your favorite Medusa article of 2022? And what topics would you love to see us cover in 2023?&lt;/p&gt;

</description>
      <category>discuss</category>
    </item>
    <item>
      <title>What language or framework are you planning to learn in 2023?</title>
      <dc:creator>Shahed Nasser</dc:creator>
      <pubDate>Fri, 06 Jan 2023 13:15:31 +0000</pubDate>
      <link>https://forem.com/medusajs/what-language-or-framework-are-you-planning-to-learn-in-2023-2lc8</link>
      <guid>https://forem.com/medusajs/what-language-or-framework-are-you-planning-to-learn-in-2023-2lc8</guid>
      <description>&lt;p&gt;With a new year come new resolutions! What programming languages or framework are you planning to learn this year?&lt;/p&gt;

&lt;p&gt;P.S: If you haven't learned about Medusa yet, maybe you have another new year resolution! Check out our &lt;a href="https://github.com/medusajs/medusa"&gt;GitHub&lt;/a&gt; repository and &lt;a href="https://docs.medusajs.com/"&gt;documentation&lt;/a&gt; for more details.&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>programming</category>
    </item>
    <item>
      <title>6 ways we improved our documentation in 2022</title>
      <dc:creator>Shahed Nasser</dc:creator>
      <pubDate>Tue, 03 Jan 2023 12:00:06 +0000</pubDate>
      <link>https://forem.com/medusajs/6-ways-we-improved-our-documentation-in-2022-1hnp</link>
      <guid>https://forem.com/medusajs/6-ways-we-improved-our-documentation-in-2022-1hnp</guid>
      <description>&lt;p&gt;At &lt;a href="https://github.com/medusajs/medusa" rel="noopener noreferrer"&gt;Medusa&lt;/a&gt;, we strive to provide developers with the best experience. &lt;a href="https://docs.medusajs.com/" rel="noopener noreferrer"&gt;Documentation&lt;/a&gt; plays a huge role in it, as it guides developers in their journey with the software at hand.&lt;/p&gt;

&lt;p&gt;As a technical writer at Medusa, I am heavily involved in the development of our documentation, especially in writing the content. Throughout 2022, our documentation team have put a lot of effort into not only investing more resources into documentation, but look into different ways to improve it.&lt;/p&gt;

&lt;p&gt;In this article, I’ll share with you how we went about improving our documentation at Medusa.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Medusa
&lt;/h2&gt;

&lt;p&gt;Before diving into our documentation improvement, it’s important to know what Medusa is and how developers use it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medusajs.com/" rel="noopener noreferrer"&gt;Medusa&lt;/a&gt; is an open-source composable commerce platform. Its architecture is made of three main components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The server: a headless backend that handles all the business logic and data related to the ecommerce store. It can be connected to any frontend for both the storefront and the admin.&lt;/li&gt;
&lt;li&gt;The storefront: this is the interface that customers use to browse the ecommerce store and make their purchase. We provide a Next.js and Gatsby starter storefronts, but developers have the freedom to create their own by connecting to the storefront REST APIs.&lt;/li&gt;
&lt;li&gt;The admin: this is the interface that merchants use to manage their ecommerce store’s settings and data. We provide a Medusa admin that provides all necessary and advanced functionalities, but developers are free to create their own by connecting to the admin REST APIs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can learn more about Medusa’s features and architecture in &lt;a href="https://docs.medusajs.com/introduction" rel="noopener noreferrer"&gt;the documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why we decided to improve our documentation
&lt;/h2&gt;

&lt;p&gt;At the beginning of 2022, our documentation provided developers with the basic information they needed to run Medusa. Although this was good enough to get them started, it wasn’t enough to guide them through the entire process of developing an ecommerce store.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftir3x6ficdl6lzmn4yo7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftir3x6ficdl6lzmn4yo7.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There were improvements needed regarding the content, the design, the feedback collected, and more. To provide these improvements, we needed to change how we handle our documentation and add more resources into developing it.&lt;/p&gt;

&lt;h2&gt;
  
  
  How we improved the documentation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  View the documentation as a product
&lt;/h3&gt;

&lt;p&gt;The most essential change that helped us implement all improvements is how we manage the documentation. In most software companies, documentation has less of a priority compared to the software, and it is handled as an afterthought.&lt;/p&gt;

&lt;p&gt;At Medusa, we early on came to realize that documentation is what can actually drive further adoption of Medusa.&lt;/p&gt;

&lt;p&gt;Developers that are curious would be sufficient with a &lt;a href="https://docs.medusajs.com/quickstart/quick-start" rel="noopener noreferrer"&gt;quickstart&lt;/a&gt; guide. However, those who actually want to use Medusa to create an ecommerce store for themselves or for their clients require the documentation to explain different concepts and how to implement them.&lt;/p&gt;

&lt;p&gt;By recognizing that the documentation is a product, we were able to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Recognize priorities for what changes/additions should be made for the documentation. These priorities can be decided on based on company goals, our roadmap, improvements in user experience, and more.&lt;/li&gt;
&lt;li&gt;Plan our work in cycles. We now use &lt;a href="https://linear.app/" rel="noopener noreferrer"&gt;Linear&lt;/a&gt; to manage our documentation cycles, tasks, and backlog.&lt;/li&gt;
&lt;li&gt;Involve the design team and other teams to further improve our documentation and the experience we provide.&lt;/li&gt;
&lt;li&gt;Be more in sync with the core engineering team to deliver changes to the documentation along with changes in the product. The core engineering team also uses Linear, so both teams can be informed of the work they’re each doing, which allows us to stay in sync.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Refactored our API Reference
&lt;/h3&gt;

&lt;p&gt;Our previous API reference needed serious improvements related to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Maintaining it to ensure that all the details in it were updated with continuous releases.&lt;/li&gt;
&lt;li&gt;Providing a good user experience, as it was slow and provided minimal information.&lt;/li&gt;
&lt;li&gt;Offering examples on how to use and run endpoints.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F474rt6bvvwsownkbgda2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F474rt6bvvwsownkbgda2.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To improve it, we decided that it needed to be fully refactored, both in how it was being generated and in how the website itself was built.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://docs.medusajs.com/api/store/" rel="noopener noreferrer"&gt;API reference&lt;/a&gt;’s OpenAPI Spec is now generated with every new release, ensuring that it’s never outdated. We also added in the OpenAPI Spec comments in our code (which are used to generate the API reference) examples of running requests with the Medusa JS Client and cURL. In addition, we added examples of expected request or response parameters and of possible errors.&lt;/p&gt;

&lt;p&gt;The website was previously built with Gatsby using a custom theme. As part of the refactor, we changed it to run as part of our &lt;a href="https://docusaurus.io/" rel="noopener noreferrer"&gt;Docusaurus&lt;/a&gt; main documentation and on top of &lt;a href="https://redocly.com/" rel="noopener noreferrer"&gt;Redocly&lt;/a&gt; using the &lt;a href="https://github.com/rohit-gohri/redocusaurus" rel="noopener noreferrer"&gt;Redocusaurus&lt;/a&gt; plugin.&lt;/p&gt;

&lt;p&gt;This allowed us to utilize Redocly’s functionalities and features that are tailored towards building API references. This further improves the developer experience.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8qqacwn21dceqpa8o03m.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8qqacwn21dceqpa8o03m.gif" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Completely redesigned the documentation
&lt;/h3&gt;

&lt;p&gt;In the last quarter of 2022, we focused on redesigning the documentation to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Improve the user and developer experience of our documentation.&lt;/li&gt;
&lt;li&gt;Restructure the homepage to guide developers to the essential concepts and guides in Medusa.&lt;/li&gt;
&lt;li&gt;Provide a design that is fitting with our brand identity.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks to our design team, we were able to introduce a clean design, a summarized homepage with the essential documentation on how to use Medusa, and improve on existing components used throughout the documentation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F97z6thqumty7zueuj1x7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F97z6thqumty7zueuj1x7.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Collect user feedback
&lt;/h3&gt;

&lt;p&gt;An important part of developing any product, including the documentation, is to ensure that your users are actually benefiting from the additions or changes you’re making. We implemented that in different ways.&lt;/p&gt;

&lt;p&gt;One way was to include a feedback component at the end of all documentation pages or after certain steps that allows developers to let us know whether they found the content helpful or not, and what could be improved.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx6cadrbhd30yldqyfm8k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx6cadrbhd30yldqyfm8k.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This helped us find errors in the documentation that we missed or opportunities to make the documentation clearer.&lt;/p&gt;

&lt;p&gt;Furthermore, for each code block, we added a report button. This allows developers to quickly create an issue on GitHub with information on the problem they faced while using that code block.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3r2u57ys4bksa2zcbf0c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3r2u57ys4bksa2zcbf0c.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There’s also a link to report issues in the navigation bar that developers can use at any point while browsing the documentation. This further helps us eliminate issues and errors quickly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Integrate Vale and ESLint into the Documentation
&lt;/h3&gt;

&lt;p&gt;Style guides are important as it helps maintain a single tone throughout the documentation. It is key to be consistent in how you write the documentation, as developers get used to it while browsing it.&lt;/p&gt;

&lt;p&gt;However, it’s one thing to write down a style guide, and another thing to actually apply and enforce it. As humans, we can easily miss certain things, especially for written content. This is especially the case with open source projects, as you have many contributors that look forward to help, but may not know how to follow the style guide.&lt;/p&gt;

&lt;p&gt;Similar to how a software has integration or unit tests that are built to make sure changes in the software don’t break the entire system, we implemented tests for our documentation’s style guide using &lt;a href="https://vale.sh/" rel="noopener noreferrer"&gt;Vale&lt;/a&gt; and &lt;a href="https://eslint.org/" rel="noopener noreferrer"&gt;ESLint&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Vale allows you to define a set of rules for your documentation’s textual content. Then, when ran against your documentation files (such as Markdown files), it shows you any errors in the content based on your rules.&lt;/p&gt;

&lt;p&gt;ESLint allows you to check for errors and enforce a programming style for JavaScript. Using plugins, it can be integrated with other languages and formats. Using the &lt;a href="https://github.com/eslint/eslint-plugin-markdown" rel="noopener noreferrer"&gt;eslint-plugin-markdown plugin&lt;/a&gt;, we were able to check for errors and inconsistencies in code blocks within the documentation.&lt;/p&gt;

&lt;p&gt;We also integrated both tools into our documentation pipeline by ensuring that their tests run on every documentation pull request (PR) in our GitHub repository. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4synrs24t6htmwhjc5hc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4synrs24t6htmwhjc5hc.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Run User Trials
&lt;/h3&gt;

&lt;p&gt;Collecting user feedback is helpful. However, in order to provide a good developer experience, you need to learn how your developers are actually using your documentation.&lt;/p&gt;

&lt;p&gt;The best way to do that is to monitor them using it while performing a task. To do this, we started user trials for our documentation. Every quarter, we’ll run a user trial revolving around a specific task and monitor how participants perform that task.&lt;/p&gt;

&lt;p&gt;This is essential when you introduce a new feature into your documentation and want to see how developers use it. Is it actually a good feature? What improvements are needed to make it better?&lt;/p&gt;

&lt;p&gt;This is also a good exercise to see what problems developers might run into, how the content or the information architecture can be misleading, and whether we really understand our documentation users or not.&lt;/p&gt;

&lt;p&gt;If you’re interested in helping us improve our documentation, &lt;a href="https://ky5eo2x1u81.typeform.com/to/bFCQpY59" rel="noopener noreferrer"&gt;learn more about our user trials and how you can become a participant&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s next?
&lt;/h2&gt;

&lt;p&gt;Writing this article does not mean we have the best documentation out there, and the journey to improve our documentation doesn’t end here. It’s an ongoing process and collaboration between our different teams and the community behind Medusa. The goal is to ensure that we understand our developers and make the information they need accessible.&lt;/p&gt;

&lt;p&gt;What do you think of our improvements on the documentation? And do you have suggestions for further improvements? Let us know below!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Should you have any issues or questions related to Medusa, then feel free to reach out to the Medusa team via &lt;a href="https://discord.gg/F87eGuwkTp" rel="noopener noreferrer"&gt;Discord&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>showdev</category>
      <category>opensource</category>
      <category>productivity</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
