In the first part of this tutorial series, I compared Medusa and Shopify to showcase how Medusa is the open-source alternative to Shopify. Where Shopify lacks when it comes to its pricing plans, minimal customization abilities, and inability to fit for every business use case, Medusa can compensate for it.
Medusa is an open-source headless commerce solution that allows you to own your stack and make it fit into whatever use case your business needs. It is fast and very flexible.
In the previous tutorial, you learned about Medusa’s 3 components and how you can install and run each of them. It is a very easy process that can get your store up and running in seconds.
In this tutorial, you will start making changes to the server to make it your own. You will learn how to create new API endpoints, services, and subscribers. The API you will create will retrieve the products with the most sales, and you will create a service and subscriber to help us do that.
The code for this tutorial is on this GitHub repository.
Prerequisites
This tutorial assumes you have already read and followed along with part 1. In the first part, you learn how to setup the Medusa store, which you will make changes to in this tutorial, as well as the Medusa storefront and the admin. If you have not went through it yet, please do before continuing with this tutorial.
In addition, you need to have Redis installed and running on your machine to be able to use subscribers. So, if you do not have it installed and you want to follow along with the tutorial you should go ahead and install it.
Add a Service
As mentioned earlier, you will be creating an API endpoint that allows you to get the top products, i.e. the products with the most sales.
In Medusa, services generally handle the logic of models or entities in one place. They hold helper functions that allow you to retrieve or perform action on these models. Once you put them in a service, you can access the service from anywhere in your Medusa project.
So, in this tutorial, you will create a service
that will hold all the logic needed to update products with their number of sales and to retrieve the products sorted by their number of sales.Copy to clipboardTopProductsService
To create a service, start by creating the file
with the following content:Copy to clipboardsrc/services/top-products.js
Here are a few things to note about this service:
- When this service is retrieved in other places in your code, the service should be referred to as the camel-case version of the file name followed by “Service”. In this case, the file name is
, so to access it in other places we useCopy to clipboardtop-product
.Copy to clipboardtopProductsService
- Similarly to how you will use this service, we inject as dependencies the
andCopy to clipboardproductService
in the constructor. When you create classes in Medusa, you can use dependency injection to get access to services.Copy to clipboardorderService
Implement getTopProducts
The next step is to add the method
to theCopy to clipboardgetTopProducts
class. This method will retrieve the products from the database, sort them by their number of sales, then return the top 5 products.Copy to clipboardTopProductsService
Inside
class add the new method:Copy to clipboardTopProductsService
You first use
to retrieve the list of products. Notice that theCopy to clipboardthis.productService_
method can take 2 optional parameters. The first one specifies where conditions, and the second parameter specifies the relations on this products to retrieve.Copy to clipboardlist
Then, you sort the array with the sort Array method giving it a compare function. In the compare function, you compare the number of sales stored inside the
field. In Medusa, most entities have theCopy to clipboardmetadata
field which allows you to easily add custom attributes in the default entities for your purposes. Here, you use theCopy to clipboardmetadata
field to store the number of sales. You are also sorting the products descending.Copy to clipboardmetadata
Finally, you use the splice Array method to retrieve only the first 5 items.
Implement updateSales
Next, you will implement the
method in theCopy to clipboardupdateSales
. This method receives an order ID as a parameter, then retrieves this order and loops over the items ordered. Then, theCopy to clipboardTopProductsService
property insideCopy to clipboardsales
is incremented and the product is updated.Copy to clipboardmetadata
Add the new method in
:Copy to clipboardTopProductsService
You first use
to retrieve the order by its ID. TheCopy to clipboardthis.orderService_
method takes the order ID as the first parameter and a config object as the second parameter which is similar to the ones you used in the previous method. You pass it the relations array to retrieve the ordered items and their products.Copy to clipboardretrieve
Then, you loop over the items and use the product id inside each item to retrieve the product. Afterward, you increment the number of sales and update the product using the
method onCopy to clipboardupdate
.Copy to clipboardthis.productService_
This service is now ready to update product sales numbers and retrieve products ordered based on their sales number.
Add an API Endpoint
Now, you will add an API endpoint to retrieve the top products. To add an API endpoint, you can do that by creating the file
with the following content:Copy to clipboardsrc/api/index.js
Creating an endpoint is easy. You just need to export an Express Router. This router can hold as many routes as you want.
In this code, you add a new GET route at the endpoint
. The reason you are usingCopy to clipboard/store/top-products
here as a prefix toCopy to clipboardstore
is that Medusa prefixes all storefront endpoints withCopy to clipboardtop-products
, and all admin endpoints withCopy to clipboard/store
. You do not need to add this prefix, but it is good to follow the conventions of the Medusa APIs.Copy to clipboard/admin
In this route, you retrieve the service you created in the previous section with this line:
You can retrieve any service inside routes using
. As explained in the services section, you need to use the camel-case version of the file name followed byCopy to clipboardreq.scope.resolve
when referencing a service in your code.Copy to clipboardService
After retrieving the service, you can then use the methods you created on it. So, you return a JSON response that has the key
and the value will be the array of top products returned byCopy to clipboardproducts
.Copy to clipboardgetTopProducts
Let us test it out. You can access this endpoint at
. As this is a GET request, you can do it from your browser or using a client like Postman or Thunder Client.Copy to clipboardlocalhost:9000/store/top-products
You should see an array of products in the response. At the moment, nothing is sorted as you have not implemented the subscriber which will update the sales number.
Add a Subscriber
Finally, you will add a subscriber which will update the sales number of products when an order is placed.
Before creating the subscriber, you need to make sure that Redis is installed and running on your machine. You can test that by running the following command in your terminal:
If the command returns “PONG” then the Redis service is running.
Then, go to
in the root of your project. You will see that at the end of the file inside the exported config there is this line commented out:Copy to clipboardMedusa``-config.js
Remove the comments. This uses the variable
declared in the beginning of the file. Its value is either the Redis URL set inCopy to clipboardREDIS_URL
or the default Redis URLCopy to clipboard.env
. If you have a different Redis URL, add the new variableCopy to clipboardredis://localhost:6379
inCopy to clipboardREDIS_URL
with the URL.Copy to clipboard.env
Then, restart the server. This will take the updated configuration and connect to your Redis server.
Now, you will implement the subscriber. Create the file
with the following content:Copy to clipboardsrc/subscribers/top-products.js
Similar to how you implemented
, you pass theCopy to clipboardTopProductsService
in the constructor using dependency injection. You also passCopy to clipboardtopProductsService
. This is used to subscribe a handler to an event in the constructor.Copy to clipboardeventBusService
You subscribe to the order placed event with this line:
The
method onCopy to clipboardsubscribe
takes the name of the event as the first parameter and the handler as the second parameter.Copy to clipboardeventBusService
You then define in the class the
method which will handle theCopy to clipboardhandleTopProducts
event. Event handlers in Medusa generally receive aCopy to clipboardorder.placed
object that holds anCopy to clipboarddata
property with the ID of the entity this event is related to. So, you pass this ID into theCopy to clipboardid
method onCopy to clipboardupdateSales
to update the number of sales for each of the products in the order.Copy to clipboardthis.topProductsService_
Test It Out
You will now test everything out. Make sure the server is running. If not, run it with the following command:
Then, go to the Medusa storefront installation and run:
Go to the storefront and place an order. This will trigger the
which will update the sales of the products in that order.Copy to clipboardTopProductsSubscriber
Now, send a request to
like you did before. You should see thatCopy to clipboard/store/top-products
inside theCopy to clipboardsales
property of the products in that order has increased.Copy to clipboardmetadata
Try to add a new product from the admin panel or use the database in the GitHub repository of this tutorial, which has an additional product. Then, try to make more orders with that product. You will see that the sorting in the endpoint has changed based on the number of sales.
Conclusion
In this tutorial, you learned how to add custom API endpoint, service, and subscriber. You can use these 3 to implement any custom feature or integration into your store.
In the next tutorial, you will use the API endpoint you created in this part to customize the frontend and add a product slider that showcases the top selling products on your store.
In the meantime, should you have any issues or questions related to Medusa, then feel free to reach out to the Medusa team via Discord.
Share this post
Try Medusa
Spin up your environment in a few minutes.
