<?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: Chris Bongers</title>
    <description>The latest articles on Forem by Chris Bongers (@dailydevtips1).</description>
    <link>https://forem.com/dailydevtips1</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%2F370165%2F083a25f4-e79b-42c4-9e5f-0332b834ca5e.png</url>
      <title>Forem: Chris Bongers</title>
      <link>https://forem.com/dailydevtips1</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/dailydevtips1"/>
    <language>en</language>
    <item>
      <title>I wrote 1000 articles, what’s next?</title>
      <dc:creator>Chris Bongers</dc:creator>
      <pubDate>Thu, 22 Dec 2022 05:46:40 +0000</pubDate>
      <link>https://forem.com/dailydevtips1/i-wrote-1000-articles-whats-next-1187</link>
      <guid>https://forem.com/dailydevtips1/i-wrote-1000-articles-whats-next-1187</guid>
      <description>&lt;p&gt;Most of you know me for my consistency, a golden arrow in my blog series.&lt;/p&gt;

&lt;p&gt;I've written 1000 articles in 1008 days!&lt;br&gt;
Almost an article a day, and my honeymoon was the only holiday I ever took.&lt;/p&gt;

&lt;p&gt;I'm super proud of this achievement; it has been a fantastic journey.&lt;/p&gt;

&lt;p&gt;But what's next?&lt;/p&gt;

&lt;p&gt;And it took me a while to think about that.&lt;br&gt;
Do I want to continue writing daily? Do I want to try out other things? What is the next big thing?&lt;/p&gt;

&lt;p&gt;And before answering that, I need to open up about the fact that writing daily is super unique, and I loved it, but it was also quite draining towards the 1000th article.&lt;/p&gt;

&lt;p&gt;And then something magical and exciting changed my whole thinking about everything.&lt;/p&gt;

&lt;p&gt;We got pregnant!&lt;br&gt;
So amazing, and I'm ready to become a dad.&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1671605496310%2FoFFC1-_o9.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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1671605496310%2FoFFC1-_o9.jpg" alt="Baby Bongers on the way"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What to expect
&lt;/h2&gt;

&lt;p&gt;So alright, what to expect from me in the future?&lt;br&gt;
I will never stop creating content as I enjoy it so much, but I decided to move away from daily content.&lt;/p&gt;

&lt;p&gt;There are a couple of aspects I want to try to find the next best thing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Youtube videos&lt;/li&gt;
&lt;li&gt;Long-form articles&lt;/li&gt;
&lt;li&gt;Ebooks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Between those, I'll dedicate more time per content piece and might even go deeper into some of the ones I already created.&lt;/p&gt;

&lt;p&gt;Besides that, I have a few hobby projects and ideas I want to execute, so keep an eye out for those.&lt;/p&gt;

&lt;h2&gt;
  
  
  What about the site?
&lt;/h2&gt;

&lt;p&gt;Well, I might drop an article irregularly as I see fit, but the truth is, I'm planning on selling the website.&lt;/p&gt;

&lt;p&gt;It's been my baby for almost three years, and it has matured a lot to a point where I'd love to see someone take it over and keep providing unique content for new developers.&lt;/p&gt;

&lt;p&gt;If that person is you, please do get in touch 🙏.&lt;/p&gt;

&lt;h2&gt;
  
  
  Some numbers
&lt;/h2&gt;

&lt;p&gt;Many of you probably came here to see some numbers, right?&lt;br&gt;
What do 1000 articles represent?&lt;/p&gt;

&lt;p&gt;And here are some of them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;528704 words&lt;/li&gt;
&lt;li&gt;1920 newsletter subscribers&lt;/li&gt;
&lt;li&gt;160K+ page views every month&lt;/li&gt;
&lt;li&gt;110+ #1 ranked google queries&lt;/li&gt;
&lt;li&gt;an average of 100K impressions daily&lt;/li&gt;
&lt;li&gt;5K clicks on search terms alone&lt;/li&gt;
&lt;li&gt;And so much more&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I couldn't be happier.&lt;br&gt;
In the beginning, I'd never thought that would even be possible, yet I achieved it.&lt;/p&gt;

&lt;p&gt;And that's not because of my writing, but because of all you amazing people motivating me and reading the articles.&lt;/p&gt;

&lt;p&gt;So from the bottom of my heart:&lt;/p&gt;

&lt;p&gt;Thank you for joining me on this journey.&lt;/p&gt;

&lt;h3&gt;
  
  
  Thank you for reading, and let's connect!
&lt;/h3&gt;

&lt;p&gt;Thank you for reading my blog. Feel free to subscribe to my email newsletter and connect on &lt;a href="https://www.facebook.com/DailyDevTipsBlog" rel="noopener noreferrer"&gt;Facebook&lt;/a&gt; or &lt;a href="https://twitter.com/DailyDevTips1" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>beginners</category>
      <category>watercooler</category>
    </item>
    <item>
      <title>Vendure community and more</title>
      <dc:creator>Chris Bongers</dc:creator>
      <pubDate>Wed, 21 Dec 2022 07:02:20 +0000</pubDate>
      <link>https://forem.com/dailydevtips1/vendure-community-and-more-49ei</link>
      <guid>https://forem.com/dailydevtips1/vendure-community-and-more-49ei</guid>
      <description>&lt;p&gt;It's not the first time I'll be talking about community. I think it's an essential aspect of any successful tool.&lt;/p&gt;

&lt;p&gt;This shows in my previous explorations of &lt;a href="https://daily-dev-tips.com/tags/astro/"&gt;Astro&lt;/a&gt;, &lt;a href="https://daily-dev-tips.com/tags/webshop/"&gt;Medusa&lt;/a&gt;, and now Vendure as well.&lt;/p&gt;

&lt;p&gt;All these products thrive in a super open, welcoming, and helpful community.&lt;br&gt;
To me, this is super amazing as it helps you use the product to the fullest.&lt;/p&gt;

&lt;p&gt;Let me sketch some examples of the Vendure community.&lt;/p&gt;

&lt;h2&gt;
  
  
  Examples of the community
&lt;/h2&gt;

&lt;p&gt;First of all, Vendure has an excellent slack channel where you can pop in and ask any questions.&lt;br&gt;
Anyone is super helpful to help you get to solve your issues.&lt;/p&gt;

&lt;p&gt;I even got a DM from a core member stating they saw my articles on Vendure and asking why I was looking into their product.&lt;br&gt;
I love it when creators like to get feedback on those things.&lt;/p&gt;

&lt;p&gt;Secondly, there is a healthy amount of help available online, as in people writing about Vendure, creating examples, and even products.&lt;/p&gt;

&lt;p&gt;An excellent example of the latter is &lt;a href="https://pinelab.studio/"&gt;Pinelab&lt;/a&gt;, where the owner Martijn creates open-source Vendure plugins. Vendure saw this effort and made Pinelab a silver Vendure partner.&lt;br&gt;
Small acknowledgments like that show how much they respect their community and how much they want it to be healthy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Expectations
&lt;/h2&gt;

&lt;p&gt;When it comes to communities, what would you be looking for?&lt;/p&gt;

&lt;p&gt;As for me, it's mainly a matter of responsiveness, friendliness, and activeness.&lt;/p&gt;

&lt;p&gt;To elaborate a bit on that, if there is no responsiveness, you will get frustrated and eventually give up on trying to solve the issue or question you might have.&lt;/p&gt;

&lt;p&gt;The same with snarky comments or people being rude. It will deter you from being active in the community and again give up.&lt;/p&gt;

&lt;p&gt;Or, in the last case, if there is nobody active actually to answer questions, it will naturally just die out.&lt;/p&gt;

&lt;p&gt;These are my main three elements of a healthy community. My question to you is:&lt;br&gt;
What's the most important thing for a community for you?&lt;/p&gt;

&lt;h3&gt;
  
  
  Thank you for reading, and let's connect!
&lt;/h3&gt;

&lt;p&gt;Thank you for reading my blog. Feel free to subscribe to my email newsletter and connect on &lt;a href="https://www.facebook.com/DailyDevTipsBlog"&gt;Facebook&lt;/a&gt; or &lt;a href="https://twitter.com/DailyDevTips1"&gt;Twitter&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>community</category>
    </item>
    <item>
      <title>Deploying Vendure</title>
      <dc:creator>Chris Bongers</dc:creator>
      <pubDate>Tue, 20 Dec 2022 07:11:02 +0000</pubDate>
      <link>https://forem.com/dailydevtips1/deploying-vendure-3fa3</link>
      <guid>https://forem.com/dailydevtips1/deploying-vendure-3fa3</guid>
      <description>&lt;p&gt;The cool part about Vendure is how easy it is to set up and how abstract each layer is.&lt;br&gt;
Basically, we get the following elements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;External database&lt;/li&gt;
&lt;li&gt;Server&lt;/li&gt;
&lt;li&gt;Worker&lt;/li&gt;
&lt;li&gt;Admin UI&lt;/li&gt;
&lt;li&gt;Frontend&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While this is amazing, it also brings a bit of complexity when it comes to hosting your Vendure shop.&lt;/p&gt;

&lt;p&gt;At the time of writing, I'm still doing some research, and it seems Michael from Vendure is also working on an excellent guide for hosting. (Which I'll add here once it's done).&lt;/p&gt;

&lt;p&gt;For my testing purpose, I decided to try out hosting to see what's possible and go with the following setup.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://daily-dev-tips.com/posts/hosting-a-discord-bot-on-railway/"&gt;RailwayApp&lt;/a&gt; for the database, server, worker, and admin UI&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://daily-dev-tips.com/posts/hosting-a-static-blog-on-netlify/"&gt;Netlify&lt;/a&gt; for the storefront&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I wouldn't change the front end, as Netlify works fine. However, Vercel or Cloudflare would work equally well.&lt;/p&gt;

&lt;p&gt;As for the backend side, Railway works, but it's a bit slow on their free tier. (Still experimenting with a nice setup there).&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up the backend
&lt;/h2&gt;

&lt;p&gt;I'll still show you how to host Vendure for free but with limited resources for this guide.&lt;br&gt;
You should be able to apply this process to another provider or potentially upgrade the railway subscription.&lt;/p&gt;

&lt;p&gt;Create a new &lt;a href="https://railway.app/"&gt;Railway account&lt;/a&gt; or login into your existing one, creating a new project.&lt;/p&gt;

&lt;p&gt;The first thing I added was a PostgreSQL database. I then manually connected to it with &lt;a href="https://daily-dev-tips.com/posts/top-5-mysql-clients-for-mac/#1-tableplus"&gt;TablePlus&lt;/a&gt; and imported the database I had locally.&lt;/p&gt;

&lt;p&gt;You'll see the database connection string we'll need in a bit on the connection screen, so copy that to a safe spot.&lt;/p&gt;

&lt;p&gt;The next thing we need to do is add a new service, a project, from GitHub. (Assuming you pushed your project to GitHub).&lt;br&gt;
In Railway, you'll be able to right-click on the canvas and select the new service option.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sjPYcZXG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1671428796417/khHH3oM34.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sjPYcZXG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1671428796417/khHH3oM34.png" alt="New service" width="347" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you add the service, it should automatically add the Postgres variables.&lt;br&gt;
Add optional variables you have set in your &lt;code&gt;.env&lt;/code&gt; file.&lt;br&gt;
You'll need to add the database options here, as Vendure uses an indirect connection.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ASlwC1No--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1671428982652/TDOl7J3X6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ASlwC1No--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1671428982652/TDOl7J3X6.png" alt="Variables" width="800" height="356"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then we'll also need to define what command it should run in the settings.&lt;br&gt;
And in the case of Vendure, we can set it to &lt;code&gt;yarn run build&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nQVdAL6v--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1671429062584/mrCUu_G5A.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nQVdAL6v--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1671429062584/mrCUu_G5A.png" alt="Build command" width="800" height="422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It should then auto-build your application every single time you push new changes.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: I sometimes found you need to trigger re-deploy as it would timeout the first deployment manually.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once it's running, you should already have access to your admin UI!&lt;/p&gt;

&lt;p&gt;The URL should be something like this: &lt;code&gt;https://your-name.up.railway.app/admin&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hosting the storefront
&lt;/h2&gt;

&lt;p&gt;Depending on which storefront option you choose, we have multiple options.&lt;br&gt;
I chose Remix, so I found Netlify super easy.&lt;/p&gt;

&lt;p&gt;Log in to Netlify and choose the project from your GitHub repos.&lt;br&gt;
It will automatically recognize it's a Remix project and set all the configurations for us.&lt;/p&gt;

&lt;p&gt;All we need to do here is set an environment variable for the &lt;code&gt;VENDURE_API_URL&lt;/code&gt;. This should point to your Railway environment.&lt;/p&gt;

&lt;p&gt;Then go ahead and deploy it, and it should be up and running.&lt;/p&gt;

&lt;h3&gt;
  
  
  Thank you for reading, and let's connect!
&lt;/h3&gt;

&lt;p&gt;Thank you for reading my blog. Feel free to subscribe to my email newsletter and connect on &lt;a href="https://www.facebook.com/DailyDevTipsBlog"&gt;Facebook&lt;/a&gt; or &lt;a href="https://twitter.com/DailyDevTips1"&gt;Twitter&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Vendure - Overwriting email templates</title>
      <dc:creator>Chris Bongers</dc:creator>
      <pubDate>Mon, 19 Dec 2022 06:08:05 +0000</pubDate>
      <link>https://forem.com/dailydevtips1/vendure-overwriting-email-templates-47nh</link>
      <guid>https://forem.com/dailydevtips1/vendure-overwriting-email-templates-47nh</guid>
      <description>&lt;p&gt;The previous article looked at &lt;a href="https://daily-dev-tips.com/posts/customizing-vendure/" rel="noopener noreferrer"&gt;customizing Vendure&lt;/a&gt; on a data and process level.&lt;br&gt;
In this article, we'll look at customizing emails, as they are often a big part of a webshop system.&lt;/p&gt;

&lt;p&gt;We'll be looking at two different layers of customization for customizing these emails.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Globals&lt;/li&gt;
&lt;li&gt;New email flows&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Changing email globals
&lt;/h2&gt;

&lt;p&gt;We'll first have to add the email plugin, which we'll use to modify things.&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; @vendure/email-plugin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can open our &lt;code&gt;vendure-config.ts&lt;/code&gt; file and load the plugin.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&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;VendureConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;EmailPlugin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;handlers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;defaultEmailHandlers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;templatePath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../static/email/templates&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;globalTemplateVars&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;fromAddress&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" &amp;lt;noreply@example.com`&amp;gt;&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;transport&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;smtp&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;smtp.example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;587&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;username&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;pass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;password&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="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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From this config, you can change quite a few variables. For a complete list, check out the &lt;a href="https://www.vendure.io/docs/typescript-api/email-plugin/" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This config can change almost everything that has to do with the existing templates.&lt;br&gt;
You can change the copy and format of each email by modifying them in &lt;code&gt;static/email/templates&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  New email flows
&lt;/h2&gt;

&lt;p&gt;At one stage, I needed to support a brand-new email flow.&lt;br&gt;
By default, this was the email being sent on payment to give you an order confirmation, but I needed it to be sent pre-payment as payment instructions were manual for this shop.&lt;/p&gt;

&lt;p&gt;So to add a new flow, we can create custom email event handlers on specific actions in our system.&lt;/p&gt;

&lt;p&gt;This is the example of the order placed handler.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;orderPlacedHandler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;EmailEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;order-placed&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="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;OrderPlacedEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loadData&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="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;injector&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="nf"&gt;transformOrderLineAssetUrls&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;injector&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;shippingLines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;hydrateShippingLines&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;injector&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;shippingLines&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;setRecipient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;emailAddress&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setSubject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Order confirmation for #{{ order.code }}`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setFrom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`{{ fromAddress }}`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setTemplateVars&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;order&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;shippingLines&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&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;shippingLines&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And to use it, we return this handler in our config.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;EmailPlugin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="na"&gt;handlers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;orderPlacedHandler&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;When creating this new template, ensure that you add a new physical email in your template folder.&lt;br&gt;
In this case, &lt;code&gt;static/email/templates/order-placed/body.hbs&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: You can also subscribe to your own &lt;a href="https://daily-dev-tips.com/posts/customizing-vendure/#customizing-the-order-process" rel="noopener noreferrer"&gt;custom events&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Pretty cool, as it's straightforward to create and customize email flows within Vendure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Thank you for reading, and let's connect!
&lt;/h3&gt;

&lt;p&gt;Thank you for reading my blog. Feel free to subscribe to my email newsletter and connect on &lt;a href="https://www.facebook.com/DailyDevTipsBlog" rel="noopener noreferrer"&gt;Facebook&lt;/a&gt; or &lt;a href="https://twitter.com/DailyDevTips1" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/p&gt;

</description>
      <category>tooling</category>
    </item>
    <item>
      <title>Customizing vendure</title>
      <dc:creator>Chris Bongers</dc:creator>
      <pubDate>Sun, 18 Dec 2022 05:50:51 +0000</pubDate>
      <link>https://forem.com/dailydevtips1/customizing-vendure-4a8k</link>
      <guid>https://forem.com/dailydevtips1/customizing-vendure-4a8k</guid>
      <description>&lt;p&gt;Even though Vendure is a pretty significant project out of the box, in some cases, we might want to go in and modify some elements to work to our specific use case.&lt;/p&gt;

&lt;p&gt;In this article, I'll take a high-level look at some elements we can customize within Vendure.&lt;/p&gt;

&lt;p&gt;The cool part is that it mainly works via changing the one &lt;code&gt;vendure-config.ts&lt;/code&gt; file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Customizing models
&lt;/h2&gt;

&lt;p&gt;The first thing one might want to do is add custom fields to any of the existing Entities.&lt;br&gt;
I'm surprised by how easy this process is and how slick everything works.&lt;/p&gt;

&lt;p&gt;Open your &lt;code&gt;vendure-config.ts&lt;/code&gt; file and add or modify the &lt;code&gt;customFields&lt;/code&gt; section.&lt;br&gt;
In my case, I'll add a custom field to the product entity. You can give it any name you'd like.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&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;VendureConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;customFields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;:&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;myCustomField&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&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="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;Since this will introduce a database change, we'll need to generate a new migration.&lt;br&gt;
Don't worry. Vendure will take care of all the heavy lifting.&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 migration:generate customField
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now, we can run our server, and everything will work.&lt;br&gt;
If we visit a product, we should see the custom field.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xpHfvx7P--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1671257802939/mmxKD_mmd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xpHfvx7P--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1671257802939/mmxKD_mmd.png" alt="Custom field in Vendure" width="752" height="524"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Customizing the order process
&lt;/h2&gt;

&lt;p&gt;Besides customizing the fields, you might want to add additional steps in the order process.&lt;/p&gt;

&lt;p&gt;For instance, you might want to validate some aspects of the order or the customer.&lt;br&gt;
This could be customer should be a valid tax-related customer, or an order above a certain weight can't be shipped.&lt;/p&gt;

&lt;p&gt;So let's take the first example as the &lt;a href="https://www.vendure.io/docs/developer-guide/customizing-the-order-process/"&gt;Vendure docs&lt;/a&gt; describe this best.&lt;/p&gt;

&lt;p&gt;Currently, we would have the following state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AddingItems &amp;gt; ArrangingPayment
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We want it to be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AddingItems &amp;gt; ValidateCustomer &amp;gt; ArrangingPayment
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first thing we need to do is define a new state. This is a physical text and doesn't do anything yet.&lt;/p&gt;

&lt;p&gt;Let's create a new file to separate things: &lt;code&gt;validate-customer.ts&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&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;CustomOrderProcess&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="s1"&gt;@vendure/core&lt;/span&gt;&lt;span class="dl"&gt;'&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;const&lt;/span&gt; &lt;span class="nx"&gt;validateCustomer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CustomOrderProcess&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ValidateCustomer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;transitions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;AddingItems&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;to&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;ValidateCustomer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="nx"&gt;merge&lt;/span&gt; &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;replace&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;ValidateCustomer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;to&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;ArrangingPayment&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;AddingItems&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="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;As you can see, this tells what transitions are valid to make.&lt;br&gt;
We can then open our &lt;code&gt;vendure-config.ts&lt;/code&gt; and load our new process.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&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;VendureConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;orderOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;process&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;validateCustomer&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;Now let's make sure this intercepts something. Go back to the &lt;code&gt;validate-customer.ts&lt;/code&gt; file and add the following function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;validateCustomer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CustomOrderProcess&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ValidateCustomer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Transitions go here&lt;/span&gt;

  &lt;span class="c1"&gt;// The logic for enforcing our validation goes here&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;onTransitionStart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fromState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;toState&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="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;fromState&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ValidateCustomer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;toState&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ArrangingPayment&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;isValid&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;validateTheActualCustomer&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;order&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="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;isValid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Returning a string is interpreted as an error message.&lt;/span&gt;
        &lt;span class="c1"&gt;// The state transition will fail.&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`The customer is not valid`&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is simply a mockup, and you should create the actual validation function to evaluate the customer on specific fields.&lt;br&gt;
If we return a string, it's handled as an error state, and the transition won't happen.&lt;/p&gt;
&lt;h2&gt;
  
  
  Generic modifiers
&lt;/h2&gt;

&lt;p&gt;The above two are very common modifiers, but Vendure comes packed with more.&lt;/p&gt;

&lt;p&gt;For instance, I needed to modify the stock level output in one of my shops.&lt;br&gt;
By default, it only shows things as "low stock" and never the actual number.&lt;br&gt;
On my webshop, it was needed to show the exact stock at hand.&lt;/p&gt;

&lt;p&gt;To achieve that, we can open the &lt;code&gt;vendure-config.ts&lt;/code&gt; and create a new function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;MyStockDisplayStrategy&lt;/span&gt; &lt;span class="kr"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;StockDisplayStrategy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;getStockLevel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RequestContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;productVariant&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ProductVariant&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;saleableStockLevel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;
  &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;string&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;saleableStockLevel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a function to extend the default stock display strategy. In my case, I return the physical stock level as a string.&lt;br&gt;
To ensure Vendure uses this value, we can modify the config to load that.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&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;VendureConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;catalogOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;stockDisplayStrategy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;MyStockDisplayStrategy&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;And now, the stock levels on the site will show the exact amount you have left in stock.&lt;/p&gt;

&lt;p&gt;This is just one example, Vendure comes packed with small little hooks that we can use to customize our webshop and system.&lt;br&gt;
I love that they made it easy and hassle-free to adjust the system to your needs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Thank you for reading, and let's connect!
&lt;/h3&gt;

&lt;p&gt;Thank you for reading my blog. Feel free to subscribe to my email newsletter and connect on &lt;a href="https://www.facebook.com/DailyDevTipsBlog"&gt;Facebook&lt;/a&gt; or &lt;a href="https://twitter.com/DailyDevTips1"&gt;Twitter&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Vendure - Setting up facets</title>
      <dc:creator>Chris Bongers</dc:creator>
      <pubDate>Sat, 17 Dec 2022 05:20:15 +0000</pubDate>
      <link>https://forem.com/dailydevtips1/vendure-setting-up-facets-39f7</link>
      <guid>https://forem.com/dailydevtips1/vendure-setting-up-facets-39f7</guid>
      <description>&lt;p&gt;As mentioned in my very &lt;a href="https://daily-dev-tips.com/posts/vendure-headless-commerce-part1/" rel="noopener noreferrer"&gt;first article about Vendure&lt;/a&gt;, one of the most important things for me was the concept of facets.&lt;/p&gt;

&lt;p&gt;In my use case, I needed custom elements for each product that the end users could filter.&lt;/p&gt;

&lt;p&gt;For example, the beer shop I'm working on needs to have quite a lot of global fields:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Brewery&lt;/li&gt;
&lt;li&gt;Type of brew&lt;/li&gt;
&lt;li&gt;Alcohol percentage&lt;/li&gt;
&lt;li&gt;And so on&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Luckily for us, Vendure supported this out of the box, and in this article, I'll show you how to set it up.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: This involves more graphical interaction over development.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Manually creating facets
&lt;/h2&gt;

&lt;p&gt;You can spool up your Vendure server and visit the admin UI at &lt;a href="http://localhost:3000/admin/" rel="noopener noreferrer"&gt;http://localhost:3000/admin/&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Move to Facets in the left sidebar menu; if you opt for the demo products, you'll see some already created facets.&lt;/p&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%2F6k4n8sqi8q4l4vyaqwuq.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%2F6k4n8sqi8q4l4vyaqwuq.png" alt="Facets in Vendure" width="800" height="558"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the top right, you'll have the option to create new facets containing a name and optional code.&lt;br&gt;
Once you've chosen a name, you'll be able to add facet values. These are all the unique values for that facet.&lt;/p&gt;

&lt;p&gt;I've created a Test facet with &lt;code&gt;foo&lt;/code&gt; and &lt;code&gt;bar&lt;/code&gt; values in the example below.&lt;/p&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%2Fjbbd8qkwuyu1cg96i05h.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%2Fjbbd8qkwuyu1cg96i05h.png" alt="New facet" width="800" height="558"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is all great and well, but it doesn't do much. So we must move to one of our products to leverage these facets.&lt;/p&gt;

&lt;p&gt;When you open a product, you should see on the right side there is a button to add facets.&lt;br&gt;
The important part to note here is that it searches based on the values, not the name.&lt;/p&gt;

&lt;p&gt;I'm trying to add the &lt;code&gt;Foo&lt;/code&gt; value in this case.&lt;/p&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%2Fb2o0qa6c6echr0l8yvfw.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%2Fb2o0qa6c6echr0l8yvfw.png" alt="New facet to product" width="800" height="558"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Importing facets
&lt;/h2&gt;

&lt;p&gt;As mentioned in the &lt;a href="https://daily-dev-tips.com/posts/vendure-importing-data/" rel="noopener noreferrer"&gt;Vendure import article&lt;/a&gt;, we can use an import to manage many products.&lt;/p&gt;

&lt;p&gt;It's good to note that this also supports the import of facets!&lt;/p&gt;

&lt;p&gt;How it works is that you define &lt;code&gt;variantFacets&lt;/code&gt; as a column, and inside, you can column deliminate all the values like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;test:Foo|test:Bar
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This would add both the &lt;code&gt;Foo&lt;/code&gt; and the &lt;code&gt;Bar&lt;/code&gt; value.&lt;br&gt;
The &lt;code&gt;test&lt;/code&gt; part is the code of the facet that we created.&lt;/p&gt;

&lt;p&gt;This is super helpful in creating an excellent structured database with all my products organized with custom facets.&lt;/p&gt;

&lt;h3&gt;
  
  
  Thank you for reading, and let's connect!
&lt;/h3&gt;

&lt;p&gt;Thank you for reading my blog. Feel free to subscribe to my email newsletter and connect on &lt;a href="https://www.facebook.com/DailyDevTipsBlog" rel="noopener noreferrer"&gt;Facebook&lt;/a&gt; or &lt;a href="https://twitter.com/DailyDevTips1" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/p&gt;

</description>
      <category>watercooler</category>
    </item>
    <item>
      <title>Vendure - Assets to an S3 bucket</title>
      <dc:creator>Chris Bongers</dc:creator>
      <pubDate>Fri, 16 Dec 2022 05:45:29 +0000</pubDate>
      <link>https://forem.com/dailydevtips1/vendure-assets-to-an-s3-bucket-3fag</link>
      <guid>https://forem.com/dailydevtips1/vendure-assets-to-an-s3-bucket-3fag</guid>
      <description>&lt;p&gt;As my webshop serves a lot of images, I wanted to leverage a good CDN and quickly found a neat integration with S3 available for Vendure.&lt;/p&gt;

&lt;p&gt;In this article, I'll show you how you can connect and leverage an S3 bucket as your asset storage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up Amazon
&lt;/h2&gt;

&lt;p&gt;The first thing you'll need is an AWS account, which can be tedious. (Mainly quite long)&lt;/p&gt;

&lt;p&gt;Head over to AWS and create your account.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://aws.amazon.com/console/" rel="noopener noreferrer"&gt;Create AWS account&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you are set up and ready to go, it's time to create a new bucket.&lt;br&gt;
The steps for creating a bucket are extensive and might change over time, so it's best to follow &lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/creating-bucket.html" rel="noopener noreferrer"&gt;Amazon's docs on this&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Eventually, your bucket should be ready like this:&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1671082563917%2Fi2CZgABnC.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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1671082563917%2Fi2CZgABnC.png" alt="S3 Bucket"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You'll also need to add new access credentials.&lt;br&gt;
Head to AWS's &lt;code&gt;IAM&lt;/code&gt; section and create a new user.&lt;/p&gt;

&lt;p&gt;You can give it a name that represents the tool you will use and also choose to make it an Access key.&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1671083005468%2Fh27c5_cqp.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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1671083005468%2Fh27c5_cqp.png" alt="IAM Role"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the next screen, you'll need to attach the permissions. In our case, we can choose to connect them manually.&lt;br&gt;
Search for &lt;code&gt;AmazonS3FullAccess&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1671083099475%2FjRU16zQvi.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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1671083099475%2FjRU16zQvi.png" alt="Attach policies"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can press next until you get to the actual keys in the next couple of steps.&lt;br&gt;
Copy the key and secret to a secure place.&lt;/p&gt;
&lt;h2&gt;
  
  
  Setting the bucket as storage
&lt;/h2&gt;

&lt;p&gt;Now it's time to add some code.&lt;br&gt;
First of all, we need to install the &lt;code&gt;aws-sdk&lt;/code&gt; package.&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;aws-sdk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I save my secrets in a &lt;code&gt;.ENV&lt;/code&gt; file for maximum protection, so go ahead and add the following two keys.&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="nv"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="o"&gt;={&lt;/span&gt;YOUR_KEY&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="nv"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class="o"&gt;={&lt;/span&gt;YOUR_SECRET&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we need to modify the &lt;code&gt;vendure-config.ts&lt;/code&gt; file to use a new asset plugin.&lt;/p&gt;

&lt;p&gt;Start by importing the S3 one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&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;AssetServerPlugin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;configureS3AssetStorage&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="s1"&gt;@vendure/asset-server-plugin&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;p&gt;And inside the config, add a new plugin.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&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;VendureConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;Your&lt;/span&gt; &lt;span class="nx"&gt;other&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="nx"&gt;here&lt;/span&gt;
    &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nx"&gt;AssetServerPlugin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;route&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;assets&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;storageStrategyFactory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;configureS3AssetStorage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="na"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vendure&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="p"&gt;{&lt;/span&gt;
                    &lt;span class="na"&gt;accessKeyId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;secretAccessKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AWS_SECRET_ACCESS_KEY&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="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's essential to change the bucket name to your bucket.&lt;/p&gt;

&lt;p&gt;And now, any assets you upload via the admin UI or the import will be stored in the S3 bucket!&lt;/p&gt;

&lt;h3&gt;
  
  
  Thank you for reading, and let's connect!
&lt;/h3&gt;

&lt;p&gt;Thank you for reading my blog. Feel free to subscribe to my email newsletter and connect on &lt;a href="https://www.facebook.com/DailyDevTipsBlog" rel="noopener noreferrer"&gt;Facebook&lt;/a&gt; or &lt;a href="https://twitter.com/DailyDevTips1" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>aws</category>
    </item>
    <item>
      <title>Vendure - Importing data</title>
      <dc:creator>Chris Bongers</dc:creator>
      <pubDate>Thu, 15 Dec 2022 06:33:33 +0000</pubDate>
      <link>https://forem.com/dailydevtips1/vendure-importing-data-1g15</link>
      <guid>https://forem.com/dailydevtips1/vendure-importing-data-1g15</guid>
      <description>&lt;p&gt;Product imports are an essential element that I found great when working with Vendure.&lt;br&gt;
It was super important to me to be able to import products and their data.&lt;/p&gt;

&lt;p&gt;Many people might have the exact needs as they often already have a webshop or system to keep track of these things.&lt;/p&gt;

&lt;p&gt;In this article, we'll look at how we can use the data import feature of Vendure.&lt;/p&gt;
&lt;h2&gt;
  
  
  Importing products
&lt;/h2&gt;

&lt;p&gt;When importing products, Vendure accepts a flat &lt;code&gt;.csv&lt;/code&gt; format, and it can hold products, assets, variants, and even custom fields.&lt;br&gt;
These elements will be created during the import, which is excellent.&lt;/p&gt;

&lt;p&gt;You can find an example of the CSV format on the &lt;a href="https://www.vendure.io/docs/developer-guide/importing-product-data/#product-import-format"&gt;Vendure docs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The are multiple ways of doing the import, and I decided I wanted to be able to run it manually, so I created my script for it.&lt;/p&gt;

&lt;p&gt;I called the script &lt;code&gt;populate-server.ts&lt;/code&gt;.&lt;br&gt;
It's important to note that this uses the Vendure core &lt;code&gt;populate&lt;/code&gt; function, which takes two parameters, the first being the initial data and the second your import.&lt;/p&gt;

&lt;p&gt;My complete file looks like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// populate-server.ts&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;bootstrap&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="s1"&gt;@vendure/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&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;populate&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="s1"&gt;@vendure/core/cli&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&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;config&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="s1"&gt;./src/vendure-config&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&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;initialData&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="s1"&gt;./my-initial-data&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;productsCsvFile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;import.csv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;populate&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;bootstrap&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;initialData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;productsCsvFile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;app&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;return&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;close&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;then&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;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exit&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="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exit&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="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;Let's go ahead and also create this &lt;code&gt;my-initial-data.ts&lt;/code&gt; file.&lt;br&gt;
This file can be used to set up the admin section of your Vendure server.&lt;/p&gt;

&lt;p&gt;In my case, I limited quite a bit of the setup, and my file looks like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&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;InitialData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;LanguageCode&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="s1"&gt;@vendure/core&lt;/span&gt;&lt;span class="dl"&gt;'&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;const&lt;/span&gt; &lt;span class="nx"&gt;initialData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;InitialData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;paymentMethods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="na"&gt;defaultLanguage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;LanguageCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;en&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;countries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="na"&gt;defaultZone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SA&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;taxRates&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="na"&gt;shippingMethods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="na"&gt;collections&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;You can find the complete object you can construct on the &lt;a href="https://www.vendure.io/docs/developer-guide/importing-product-data/#initial-data"&gt;Vendure docs&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: Every time you run your import, these will be executed, so running it more than once will result in duplicates.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To run the actual import, we can use 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;yarn ts-node populate-server.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Depending on the number of products, it can take some time to finish.&lt;/p&gt;

&lt;p&gt;I often ran the script for testing purposes to get the proper import set.&lt;br&gt;
You might have to play around with your CSV format if importing many custom fields this way.&lt;/p&gt;

&lt;h3&gt;
  
  
  Thank you for reading, and let's connect!
&lt;/h3&gt;

&lt;p&gt;Thank you for reading my blog. Feel free to subscribe to my email newsletter and connect on &lt;a href="https://www.facebook.com/DailyDevTipsBlog"&gt;Facebook&lt;/a&gt; or &lt;a href="https://twitter.com/DailyDevTips1"&gt;Twitter&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Vendure - Storefronts</title>
      <dc:creator>Chris Bongers</dc:creator>
      <pubDate>Wed, 14 Dec 2022 06:09:47 +0000</pubDate>
      <link>https://forem.com/dailydevtips1/vendure-storefronts-1o80</link>
      <guid>https://forem.com/dailydevtips1/vendure-storefronts-1o80</guid>
      <description>&lt;p&gt;When setting up a commerce website, you'll likely need a storefront. This will be what the end users will use to order your products.&lt;/p&gt;

&lt;p&gt;In the case of Vendure, we are open to creating our own, but luckily for us, they have some fantastic starters already set up.&lt;/p&gt;

&lt;p&gt;At the time of writing, they have official starters for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Remix&lt;/li&gt;
&lt;li&gt;Vue Storefront&lt;/li&gt;
&lt;li&gt;Next.js&lt;/li&gt;
&lt;li&gt;Angular&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since I started a &lt;a href="https://daily-dev-tips.com/tags/remix/" rel="noopener noreferrer"&gt;series on Remix&lt;/a&gt;, I have been eager to try that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing the Remix Storefront
&lt;/h2&gt;

&lt;p&gt;To install the Storefront, we can clone the &lt;a href="https://github.com/vendure-ecommerce/storefront-remix-starter" rel="noopener noreferrer"&gt;Git repo&lt;/a&gt; and install it.&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 git@github.com:vendure-ecommerce/storefront-remix-starter.git

&lt;span class="nb"&gt;cd &lt;/span&gt;storefront-remix-starter

npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, create a &lt;code&gt;.env&lt;/code&gt; file in the root and point it to our local Vendure server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;VENDURE_API_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//localhost:3001/shop-api&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now go ahead and run the Storefront with: &lt;code&gt;npm run dev&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: Make sure your Vendure server is running.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And visit the Storefront on &lt;a href="http://localhost:3000/" rel="noopener noreferrer"&gt;http://localhost:3000/&lt;/a&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1670909460542%2FtTALBNZ5F.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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1670909460542%2FtTALBNZ5F.png" alt="Remix Storefront example"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Play around with it and try to order some products. You'll see it's blazing fast!&lt;/p&gt;

&lt;h2&gt;
  
  
  Other options
&lt;/h2&gt;

&lt;p&gt;As mentioned, you can use some other supported Storefronts, which you can find on the &lt;a href="https://www.vendure.io/integration/" rel="noopener noreferrer"&gt;Vendure website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And if you want to use something together, you can take inspiration from any of the existing integrations and create your own new integration.&lt;/p&gt;

&lt;p&gt;Since, in the end, they query the API and have no concrete direct implementations in place.&lt;/p&gt;

&lt;p&gt;I'm super stoked to see this freedom of front-end in a (headless) commerce system being so well demoed out.&lt;/p&gt;

&lt;h3&gt;
  
  
  Thank you for reading, and let's connect!
&lt;/h3&gt;

&lt;p&gt;Thank you for reading my blog. Feel free to subscribe to my email newsletter and connect on &lt;a href="https://www.facebook.com/DailyDevTipsBlog" rel="noopener noreferrer"&gt;Facebook&lt;/a&gt; or &lt;a href="https://twitter.com/DailyDevTips1" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Vendure - Quick setup</title>
      <dc:creator>Chris Bongers</dc:creator>
      <pubDate>Tue, 13 Dec 2022 06:30:28 +0000</pubDate>
      <link>https://forem.com/dailydevtips1/vendure-quick-setup-51d8</link>
      <guid>https://forem.com/dailydevtips1/vendure-quick-setup-51d8</guid>
      <description>&lt;p&gt;In the previous article, we talked about &lt;a href="https://daily-dev-tips.com/posts/vendure-headless-commerce-part1/" rel="noopener noreferrer"&gt;Vendure&lt;/a&gt;, what it is, and why I find it an exciting commerce solution.&lt;/p&gt;

&lt;p&gt;In this article, we'll get started and see how easy it is to set up.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: For these articles, I mainly use &lt;a href="https://www.vendure.io/docs" rel="noopener noreferrer"&gt;Vendure's documentation&lt;/a&gt;, which is comprehensive.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Vendure quick start
&lt;/h2&gt;

&lt;p&gt;The easiest way to start with Vendure is to run their create command. This will set up everything you need to get started.&lt;/p&gt;

&lt;p&gt;Out of the box, we get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Admin GraphQL API&lt;/li&gt;
&lt;li&gt;Shop GraphQL API&lt;/li&gt;
&lt;li&gt;Admin UI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All you need to have is node (with npx) npm 5.2 and higher installed.&lt;br&gt;
With that, you can 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;npx @vendure/create my-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where &lt;code&gt;my-app&lt;/code&gt; is the actual name of your application.&lt;/p&gt;

&lt;p&gt;In the installation process, you'll be asked which type of database you want to proceed with. You can pick whichever one works best for your system.&lt;br&gt;
It will also give you the option to populate with sample data.&lt;/p&gt;

&lt;p&gt;To get started quickly, the SQLite option is very nice to get started with.&lt;/p&gt;

&lt;p&gt;Once the script is done, you can navigate to your folder and run the application.&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="nb"&gt;cd &lt;/span&gt;my-app
yarn dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It will spool up all your available resources. Once healthy, you should see the following in your terminal.&lt;/p&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%2Fcny53rt54uiq78tzexr5.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%2Fcny53rt54uiq78tzexr5.png" alt="Vendure server running" width="800" height="476"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Exploring the APIs
&lt;/h2&gt;

&lt;p&gt;As mentioned, we get access to the Vendure APIs, split into two categories.&lt;/p&gt;

&lt;p&gt;An admin API and a Shop API.&lt;br&gt;
Administrators can use the admin API to manage products, orders, and more.&lt;br&gt;
The shop API is public-facing and can be used for our eventual storefront, mobile app, etc.&lt;/p&gt;

&lt;p&gt;Let's take a quick look at how we can reach them.&lt;br&gt;
For the following examples, I'm using &lt;a href="https://daily-dev-tips.com/posts/testing-api-calls-in-insomnia/" rel="noopener noreferrer"&gt;Insomnia&lt;/a&gt;, but any API tool will work.&lt;/p&gt;

&lt;p&gt;Let's start with the shop API. To access it, we can use the following URL &lt;code&gt;http://localhost:3000/shop-api&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The API uses GraphQL as its layer of communication.&lt;br&gt;
For instance, we can use the following graphQL query to get a list of products.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;query products {
  products {
    items {
      name
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which results in a list of all our product names:&lt;/p&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%2Ff2oyiezkfvogfnut2674.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%2Ff2oyiezkfvogfnut2674.png" alt="Insomnia shop API result" width="800" height="492"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Another fantastic element of Vendure is that it gets shipped with a GraphQL explorer built in, so we can visit the API URL and query from there.&lt;br&gt;
Visit &lt;code&gt;http://localhost:3000/shop-api&lt;/code&gt; and execute your queries.&lt;/p&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%2Fq5xkb3rp28m61r0ql0ti.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%2Fq5xkb3rp28m61r0ql0ti.png" alt="Vendure GraphQL playground" width="800" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The same goes for the admin API. However, we should use the following URL &lt;code&gt;http://localhost:3000/admin-api&lt;/code&gt;.&lt;/p&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%2F3z59hltqw4dc0j8ypjp8.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%2F3z59hltqw4dc0j8ypjp8.png" alt="Vendure Admin API" width="800" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Exploring the admin UI
&lt;/h2&gt;

&lt;p&gt;Besides these fantastic APIs, we also get access to a default admin UI.&lt;br&gt;
Visit the following URL: &lt;code&gt;http://localhost:3000/admin/&lt;/code&gt;, and you can explore the admin interface.&lt;/p&gt;

&lt;p&gt;It's an excellent place to start exploring how things are linked together.&lt;br&gt;
If you choose the population of data, you'll even be able to see some advanced linking and facets.&lt;/p&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%2Fcbhlujuas79e28j9jpag.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%2Fcbhlujuas79e28j9jpag.png" alt="Admin UI" width="800" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Thank you for reading, and let's connect!
&lt;/h3&gt;

&lt;p&gt;Thank you for reading my blog. Feel free to subscribe to my email newsletter and connect on &lt;a href="https://www.facebook.com/DailyDevTipsBlog" rel="noopener noreferrer"&gt;Facebook&lt;/a&gt; or &lt;a href="https://twitter.com/DailyDevTips1" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>css</category>
      <category>tailwindcss</category>
    </item>
    <item>
      <title>Vendure headless commerce - part 1</title>
      <dc:creator>Chris Bongers</dc:creator>
      <pubDate>Mon, 12 Dec 2022 06:16:30 +0000</pubDate>
      <link>https://forem.com/dailydevtips1/vendure-headless-commerce-part-1-3pm4</link>
      <guid>https://forem.com/dailydevtips1/vendure-headless-commerce-part-1-3pm4</guid>
      <description>&lt;p&gt;A while ago, we started looking at &lt;a href="https://daily-dev-tips.com/posts/trying-out-a-medusa-webshop/" rel="noopener noreferrer"&gt;Medusa&lt;/a&gt; as an alternative for a WooCommerce webshop, I'm still busy with this project, but in the meantime began looking at Vendure too.&lt;/p&gt;

&lt;p&gt;At first, the website was a little bit of a letdown, and Medusa seemed to do a more appealing job at marketing itself.&lt;br&gt;
However, I quickly came back to this after giving it a try.&lt;/p&gt;

&lt;p&gt;A big shoutout to the Vendure team, as they have been super helpful with all my requests and taking the time to help me set up.&lt;/p&gt;

&lt;p&gt;In a couple of articles, we'll be looking at Vendure and how we can set up a headless commerce system and link it to some storefronts.&lt;/p&gt;

&lt;h2&gt;
  
  
  What exactly is headless commerce?
&lt;/h2&gt;

&lt;p&gt;You might be wondering, what is this headless commerce system?&lt;/p&gt;

&lt;p&gt;It's an e-commerce system, but it's not tied to any specific frontend solution.&lt;br&gt;
In the case of Vendure, it's focused on developers who love the modern web, and we see familiar names such as TypeScript, GraphQL, and NodeJS.&lt;/p&gt;

&lt;p&gt;We get an out-of-the-box solid system that we can extend and modify to work with our other systems.&lt;/p&gt;

&lt;p&gt;This benefit is that a developer can choose whatever frontend stack they want to work in, giving a lot of freedom.&lt;br&gt;
We can quickly hook up multiple solutions and languages to work with, even on the admin side.&lt;/p&gt;

&lt;h2&gt;
  
  
  What makes Vendure so great?
&lt;/h2&gt;

&lt;p&gt;I started looking at Vendure because of how flexible their setup was. We get a lot of hooks we can use and a pretty large open-source community that is super helpful.&lt;/p&gt;

&lt;p&gt;One of my main selling points was that they provide product variants with facets.&lt;br&gt;
I was particularly struggling with this on Medusa, and it worked great out of the box from Vendure.&lt;/p&gt;

&lt;p&gt;The other real benefit was the number of storefronts we could hook into. I liked the Remix storefront a lot as I was exploring Remix in general (so this is very biased).&lt;/p&gt;

&lt;p&gt;It also comes with many great options that need little to no modifications to make things change, and more in future articles.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to expect
&lt;/h2&gt;

&lt;p&gt;You might be interested in this process, so let's discuss what to expect from the following articles.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We'll set up the system with the admin interface&lt;/li&gt;
&lt;li&gt;We'll create a storefront&lt;/li&gt;
&lt;li&gt;Importing data&lt;/li&gt;
&lt;li&gt;Setting up products and facets&lt;/li&gt;
&lt;li&gt;Attaching a CDN&lt;/li&gt;
&lt;li&gt;Customizing Vendure&lt;/li&gt;
&lt;li&gt;Deploying&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are keen on any of the above topics, subscribe to my newsletter and get the latest articles as they are launched.&lt;/p&gt;

&lt;h3&gt;
  
  
  Thank you for reading, and let's connect!
&lt;/h3&gt;

&lt;p&gt;Thank you for reading my blog. Feel free to subscribe to my email newsletter and connect on &lt;a href="https://www.facebook.com/DailyDevTipsBlog" rel="noopener noreferrer"&gt;Facebook&lt;/a&gt; or &lt;a href="https://twitter.com/DailyDevTips1" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/p&gt;

</description>
      <category>welcome</category>
    </item>
    <item>
      <title>Conditional wrapping in React</title>
      <dc:creator>Chris Bongers</dc:creator>
      <pubDate>Sun, 11 Dec 2022 05:54:45 +0000</pubDate>
      <link>https://forem.com/dailydevtips1/conditional-wrapping-in-react-46o5</link>
      <guid>https://forem.com/dailydevtips1/conditional-wrapping-in-react-46o5</guid>
      <description>&lt;p&gt;This is something you do not always need, but I wrote this article for those looking for it.&lt;/p&gt;

&lt;p&gt;Sometimes we might have a generic element, a specific component that renders inside a modal.&lt;br&gt;
When a specific flag is set, the component should get a parent wrapper to display it in a different variant.&lt;/p&gt;

&lt;p&gt;We could use an if...else statement, but it looks messy.&lt;/p&gt;
&lt;h2&gt;
  
  
  Conditional wrapping in React
&lt;/h2&gt;

&lt;p&gt;Let's say we got specific service cards to make it a bit easier to follow. In some cases, they explain a service, but in others, they need to link to a detail page.&lt;/p&gt;

&lt;p&gt;The component might look like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ServiceCard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;url&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;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;section&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h2&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;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h2&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&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;description&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;alt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/section&lt;/span&gt;&lt;span class="err"&gt;&amp;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;As mentioned, what happens if we need to wrap this whole thing in a link element when the URL exists?&lt;br&gt;
We could use the if...else loop.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ServiceCard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;url&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;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;section&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;url&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;lt;&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h2&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;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h2&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&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;description&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;alt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/a&lt;/span&gt;&lt;span class="err"&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="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h2&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;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h2&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&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;description&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;alt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="p"&gt;)}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/section&lt;/span&gt;&lt;span class="err"&gt;&amp;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;But this shows duplicate code, so it's a bit silly. If we need to style each element, we must modify it in two places.&lt;/p&gt;

&lt;p&gt;So how can we better wrap this conditionally?&lt;/p&gt;

&lt;p&gt;We can create a generic component that handles this for us, the component will be named &lt;code&gt;ConditionalWrapper&lt;/code&gt;, and it will take a condition, the wrapper, and the children it should wrap.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ConditionalWrapper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;children&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;condition&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that in place, we can use it on our existing component to clean it up.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ServiceCard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;url&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;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;section&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ConditionalWrapper&lt;/span&gt;
                &lt;span class="nx"&gt;condition&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;url&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="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/a&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;            &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
                    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h2&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;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h2&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&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;description&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;alt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ConditionalWrapper&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/section&lt;/span&gt;&lt;span class="err"&gt;&amp;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;And now, if we use our component, depending on whether we pass the URL. It will render with or without the href. And the best part is that we have no duplication in our elements.&lt;/p&gt;

&lt;p&gt;For example, the following use case:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ServiceCard&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foo bar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;img1.jpg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It would return the following HTML output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;section&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;test&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;foo bar&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"img1.jpg"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"test"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will get the following output if we put the URL in the element.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;section&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"url"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;test&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;foo bar&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"img1.jpg"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"test"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pretty cool, right?&lt;/p&gt;

&lt;p&gt;The main magic, of course, happens in the ConditionalWrapper component and, to be precise, the wrapper argument.&lt;/p&gt;

&lt;p&gt;Since we pass the children (which is a React default prop), we can see that the use case of our function as in &lt;code&gt;wrapper={children =&amp;gt; &amp;lt;a href={url}&amp;gt;{children}&amp;lt;/a&amp;gt;}&lt;/code&gt; states.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the condition is met&lt;/li&gt;
&lt;li&gt;Wrap the children in this specific element&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There will only be a handful of times when you might need this function, but it can be a huge lifesaver.&lt;/p&gt;

&lt;p&gt;Note: big thanks to &lt;a href="https://blog.hackages.io/conditionally-wrap-an-element-in-react-a8b9a47fab2" rel="noopener noreferrer"&gt;Olivier&lt;/a&gt; for the original idea!&lt;/p&gt;

&lt;h3&gt;
  
  
  Thank you for reading, and let's connect!
&lt;/h3&gt;

&lt;p&gt;Thank you for reading my blog. Feel free to subscribe to my email newsletter and connect on &lt;a href="https://www.facebook.com/DailyDevTipsBlog" rel="noopener noreferrer"&gt;Facebook&lt;/a&gt; or &lt;a href="https://twitter.com/DailyDevTips1" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
