<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[A Dev In Wild]]></title><description><![CDATA[Sharing with the world!]]></description><link>https://blog.imadil.dev</link><generator>RSS for Node</generator><lastBuildDate>Sun, 10 May 2026 13:41:06 GMT</lastBuildDate><atom:link href="https://blog.imadil.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Customize the Admin UI Plugin with Medusa.js 1.8.X and Up: A Step-by-Step Guide]]></title><description><![CDATA[❗
This guide is deprecated for newer versions, Medusa.js team has now implemented a better way to customize the Admin UI, check the docs


The Challenge: Customizing the Admin UI Plugin
With Medusa >=1.8.x, the admin repository has been packaged as a...]]></description><link>https://blog.imadil.dev/customize-the-admin-ui-plugin-with-medusajs-18x-and-up-a-step-by-step-guide</link><guid isPermaLink="true">https://blog.imadil.dev/customize-the-admin-ui-plugin-with-medusajs-18x-and-up-a-step-by-step-guide</guid><category><![CDATA[medusa]]></category><category><![CDATA[medusa.js]]></category><category><![CDATA[customization]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Adil]]></dc:creator><pubDate>Wed, 17 May 2023 10:46:27 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/9JU2CKqtw0M/upload/dd585cd28feb00ddcbfe75cee1da9352.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><img src="https://media0.giphy.com/media/3orif0rjs49gsPWg1y/giphy.gif?cid=ecf05e47gmjf59rx5ytitieaqfy85yqa0ofvkmm47wt2vxo6&amp;ep=v1_gifs_search&amp;rid=giphy.gif&amp;ct=g" alt="season 7 dad jokes GIF" class="image--center mx-auto" /></p>
<div data-node-type="callout">
<div data-node-type="callout-emoji">❗</div>
<div data-node-type="callout-text">This guide is <strong>deprecated for newer versions</strong>, Medusa.js team has now implemented a better way to customize the <code>Admin UI</code>, <a target="_blank" class="az afn" href="https://docs.medusajs.com/admin/quickstart">check the docs</a></div>
</div>

<h2 id="heading-the-challenge-customizing-the-admin-ui-plugin"><strong>The Challenge: Customizing the Admin UI Plugin</strong></h2>
<p>With Medusa <code>&gt;=1.8.x</code>, the admin repository has been packaged as a plugin that can be directly installed/used in your backend. However, customizing the admin UI can be a challenge since it remains a Medusa.js plugin.<br />But don't worry! I have a solution that allows you to customize the admin UI to meet your needs.</p>
<h2 id="heading-solution-customizing-the-admin-ui-plugin"><strong>Solution: Customizing the Admin UI Plugin</strong></h2>
<p>Make sure you have installed <code>@medusajs/medusa</code> in your back-end first</p>
<ol>
<li><p><strong>Eject the admin UI</strong>: By ejecting the admin ui, we will get all the sources. To do this, use the <code>medusa-admin eject -o &lt;outputDirectory&gt;</code> command in your terminal. Replace <code>&lt;outputDirectory&gt;</code> with the desired directory where you want the ejected admin UI to be placed. This command will create a standalone React application using Vite.js, which you can now customize to your heart's content.</p>
</li>
<li><p><strong>Install dependencies and run the project</strong>: With the ejected admin UI ready for customization, navigate to the directory where it was ejected (specified by <code>&lt;outputDirectory&gt;</code>) and install the required dependencies.</p>
</li>
<li><p><strong>Once the dependencies are installed:</strong> You can start the development server by running the following command: <code>yarn dev</code></p>
<p> This command will launch the admin UI on <strong>port 7001 by default</strong>.</p>
</li>
<li><p><strong>Access the app:</strong> You can access the admin UI by opening your web browser and navigating to <a target="_blank" href="http://localhost:7001"><code>http://localhost:7001</code></a>.</p>
</li>
<li><p><strong>It's done:</strong> Now, you have a fully customizable admin UI at your fingertips. 🎉 You now have a common React application with Vite.js that serves as the foundation for the admin UI customization. You can leverage your frontend development skills to modify the UI, add new features, and create a tailored admin interface for your commerce store!</p>
</li>
</ol>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>Customizing the admin UI plugin with Medusa.js <code>&gt;=1.8.x</code> and newer versions is now within your reach.</p>
<p><img src="https://media4.giphy.com/media/7Xov9qZ44Mq0qkCN9Q/giphy.gif?cid=ecf05e47bp0bi8sakgy5vi0etym7z5gxxqa7ec597w4s7dgy&amp;ep=v1_gifs_search&amp;rid=giphy.gif&amp;ct=g" alt="Season 9 Nbc GIF by The Office" class="image--center mx-auto" /></p>
<p>By following this step-by-step instructions provided in this guide, you can take full control over the appearance and functionality of the admin interface.</p>
<p>However, there is a problem with this solution: the update of your UI. The Medusa.js team has considered this issue and a solution with a better way to customize your admin interface without compromising updates is in the works.</p>
<p>You can follow up the discussion about "<em>Admin Extensibility</em>" here 👇<a target="_blank" href="https://github.com/medusajs/medusa/discussions/4116">https://github.com/medusajs/medusa/discussions/4116</a></p>
<p>Remember to explore the Medusa.js documentation and community resources for further guidance and inspiration as you embark on your customization journey!</p>
]]></content:encoded></item><item><title><![CDATA[Extend validators with Medusa.js 1.8.x and up]]></title><description><![CDATA[With Medusa.js version >=1.8.x, it's now possible to extend and add new properties to validators that are triggered on some endpoints.
In this guide, I'll walk you through the steps required to extend core validators with Medusa.js. Let's get started...]]></description><link>https://blog.imadil.dev/extend-validators-with-medusajs-18x-and-up</link><guid isPermaLink="true">https://blog.imadil.dev/extend-validators-with-medusajs-18x-and-up</guid><category><![CDATA[medusa]]></category><category><![CDATA[medusa.js]]></category><category><![CDATA[customization]]></category><dc:creator><![CDATA[Adil]]></dc:creator><pubDate>Fri, 05 May 2023 15:00:39 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/ehRsHPvOFlU/upload/2cba1caa2f231a33b3bd6a5a85786aad.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>With <strong>Medusa.js</strong> version <code>&gt;=1.8.x</code>, it's now possible to extend and add new properties to validators that are triggered on some endpoints.</p>
<p>In this guide, I'll walk you through the steps required to extend core validators with Medusa.js. Let's get started!</p>
<p><img src="https://media2.giphy.com/media/RrVzUOXldFe8M/giphy.gif?cid=ecf05e47s1vudds5ensk8dev1b2visiqd0lwhxgqtgju7dir&amp;ep=v1_gifs_search&amp;rid=giphy.gif&amp;ct=g" alt="Movie gif. Nicholas Cage as Peter in Vampire's Kiss leaning forward from an office chair with a cigarette hanging out of his mouth, pointing out excitedly." class="image--center mx-auto" /></p>
<h2 id="heading-extending-core-validators"><strong>Extending Core Validators</strong></h2>
<p>In that new version of Medusa.js, you can add file loaders that will be executed when the server is launched. These loaders allow you to fully customize Medusa.js to your needs.</p>
<p><strong>Create an utility function</strong></p>
<p>The first step to extending core validators is to create a function that allows you to override the validator in question. Here's an example of what this function might look like:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// src/utils/register-extended-validator.ts</span>
<span class="hljs-keyword">import</span> { ValidatorOptions } <span class="hljs-keyword">from</span> <span class="hljs-string">'class-validator'</span>
<span class="hljs-keyword">import</span> { ClassConstructor } <span class="hljs-keyword">from</span> <span class="hljs-string">'@medusajs/medusa'</span>

<span class="hljs-keyword">const</span> extendedValidators: <span class="hljs-built_in">any</span> = []
<span class="hljs-keyword">let</span> isInitialized = <span class="hljs-literal">false</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">registerExtendedValidator</span>&lt;
    <span class="hljs-title">T</span> <span class="hljs-title">extends</span> <span class="hljs-title">ClassConstructor</span>&lt;<span class="hljs-title">any</span>&gt;,
&gt;(<span class="hljs-params">classValidator: T</span>): <span class="hljs-title">Promise</span>&lt;<span class="hljs-title">void</span>&gt; </span>{
    extendedValidators.push(classValidator)

    <span class="hljs-keyword">if</span> (isInitialized) {
        <span class="hljs-keyword">return</span>
    }

    isInitialized = <span class="hljs-literal">true</span>

    <span class="hljs-keyword">const</span> <span class="hljs-keyword">module</span> = await import('@medusajs/medusa/dist/utils/validator')
    const originalValidator = <span class="hljs-keyword">module</span>.validator
    <span class="hljs-keyword">module</span>.validator = &lt;T extends ClassConstructor&lt;any&gt; = any, V = any&gt;(
        typedClass: T,
        plain: V,
        config?: ValidatorOptions,
    ): Promise&lt;any&gt; =&gt; {
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> extendedValidator <span class="hljs-keyword">of</span> extendedValidators) {
            <span class="hljs-keyword">if</span> (extendedValidator.name === typedClass.name) {
                typedClass = extendedValidator
                <span class="hljs-keyword">break</span>
            }
        }
        <span class="hljs-keyword">return</span> originalValidator(typedClass, plain, config)
    }
}
</code></pre>
<blockquote>
<p>This code accepts a validator as a parameter.<br />This validator will be used to find a match in existing internal validators. Once the validator has been found it will be overridden ✅</p>
</blockquote>
<h4 id="heading-create-an-extended-validator">Create an extended validator</h4>
<p>In my case I decided to extend the product creation validator to add a new property <code>store_id</code>.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// We alias the core validator so we can have the EXACT same name as the core validator.</span>
<span class="hljs-keyword">import</span> { AdminPostProductsReq <span class="hljs-keyword">as</span> MedusaAdminPostProductsReq } <span class="hljs-keyword">from</span> <span class="hljs-string">'@medusajs/medusa'</span>
<span class="hljs-keyword">import</span> { IsEnum, IsString } <span class="hljs-keyword">from</span> <span class="hljs-string">'class-validator'</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> AdminPostProductsReq <span class="hljs-keyword">extends</span> MedusaAdminPostProductsReq {
    <span class="hljs-meta">@IsString</span>()
    store_id: <span class="hljs-built_in">string</span>
}
</code></pre>
<h3 id="heading-call-the-function-in-a-loader"><strong>Call the Function in a Loader</strong></h3>
<p>Now you need to call this function inside a loader with a custom validator.</p>
<p>A loader is a file that exports a function that Medusa.js will execute when the server is launched.</p>
<p>Here's an example of what the loader might look like:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// src/loaders/index.ts</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    registerExtendedValidator(AdminPostProductsReq)
}
</code></pre>
<p>This loader calls the <code>registerExtendedValidator</code> function and passes in the new <code>AdminPostProductsReq</code> class to register an extended validator for the create product endpoint.</p>
<p>This means that my endpoint will now wait for <code>store_id</code> to be part of the new DTO.</p>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p><img src="https://media3.giphy.com/media/eGmHwDLBAFjRovEGig/giphy.gif?cid=ecf05e47fmw4hnn2na5j2jc3komyjnb6aq0npcub9kyrwm5o&amp;ep=v1_gifs_search&amp;rid=giphy.gif&amp;ct=g" alt="Zak Wow GIF by Bank Cler" class="image--center mx-auto" /></p>
<p><strong>That's it!</strong> You've now learned how to extend core validators with Medusa.js 1.8.x.<br />By using this function inside a loader and registering an extended version of a specific validator, you can add custom validation logic to your Medusa.js commerce store.</p>
<p>I hope you found this guide helpful.<br />If you have any questions or comments, feel free to leave them below.</p>
<p>A special thanks to <a target="_blank" href="https://github.com/adrien2p"><strong>Adrien De Peretti</strong></a> for his help with this.</p>
]]></content:encoded></item></channel></rss>