<?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: Jonathan Schneider </title>
    <description>The latest articles on Forem by Jonathan Schneider  (@shnydercom).</description>
    <link>https://forem.com/shnydercom</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%2F234692%2F6377928a-7cd3-4907-a0ec-6aa09d4fe302.jpg</url>
      <title>Forem: Jonathan Schneider </title>
      <link>https://forem.com/shnydercom</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/shnydercom"/>
    <language>en</language>
    <item>
      <title>Boilerplate for complex hardware prototypes (RPi 5, 2 cameras, sensors, webapp)</title>
      <dc:creator>Jonathan Schneider </dc:creator>
      <pubDate>Tue, 20 Aug 2024 16:57:41 +0000</pubDate>
      <link>https://forem.com/shnydercom/steal-my-boilerplate-rpi-5-2-cameras-sensors-webapp-3p55</link>
      <guid>https://forem.com/shnydercom/steal-my-boilerplate-rpi-5-2-cameras-sensors-webapp-3p55</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;You can't touch your raspberry pi 5, but you can touch your phone.&lt;br&gt;
You can't hold 2 cameras, but you need control.&lt;br&gt;
You know some part will break, but not sure which part.&lt;br&gt;
Oh and maybe it'll float away and you'll need to catch it.&lt;/p&gt;

&lt;p&gt;-- &lt;cite&gt;The ocean&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If Use Cases similar to these are appearing in your project, I might have some code to get you started. Feel free to modify to your needs: &lt;a href="https://github.com/shnydercom/sbc-closed-system-controller" rel="noopener noreferrer"&gt;Code on github (sbc-closed-system-controller)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I wrote this code because I had to throw a Raspberry Pi 5 in the waves while it was monitoring a prototype with 2 cameras and some sensors. Here's a video of the result:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/gJKwXXPtf4A"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h1&gt;
  
  
  Tech stack choice
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;em&gt;Python&lt;/em&gt; web backend (FastAPI) was chosen because the sensors I've used have good python support. The camera libraries for the raspberry pi 5 are also still in development and not yet as mature as the ones prior to the Pi5. However, only the Pi5 has two connectors for cameras over CSI/DSI, and synchronized recording from two cameras was important. Installing the libraries from the latest git branch can be done with this command: &lt;code&gt;pip install git+https://github.com/raspberrypi/picamera2.git@next&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;A &lt;em&gt;React WebApp&lt;/em&gt; that would poll individual sensors over REST was chosen to avoid having to refresh for updates. That is a drawback you see with some html templating engines. The Raspberry Pi would go underwater, and risked loosing the WiFi connection provided by the phone's hotspot. Having the frontend downloaded and only periodically updating its data allowed for each called endpoint to fail without the UI breaking down. It wasn't sure if the sensor and camera connection would survive when the prototype went underwater. This turned out to be easier for testing as well. Lastly, I'm used to React from my regular work as a frontend developer. The built frontend code can be served from FastAPI by using a symbolic link: &lt;code&gt;ln -s ../../client-web-app/dist .&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;A service description to run the server at start of the Raspberry Pi. This actually took some searching before I found the right section, so &lt;a href="https://forums.raspberrypi.com/viewtopic.php?t=314455" rel="noopener noreferrer"&gt;here's the link&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  How the system behaves
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;The first behaviour when using 2 cameras on a Pi5 with a powerbank is... shutdowns. Most of them happen because of low voltage. The reason can be found in the system log: &lt;code&gt;journalctl -S -10m --system&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;failures are expected, so recordings are chopped into 5 second bits. The sensor readings go into a .csv-file, the raw .h264-files and their timestamps are saved separately. If everything had failed and got soaked in salt water, then the last barrier of water-proofing would have been the SD-card, which in theory should be waterproof. Luckily it didn't come to that. The combination of the files to bigger files is done with the python script &lt;code&gt;convert_recordings.py&lt;/code&gt;, in the root of the repository.&lt;/li&gt;
&lt;li&gt;The recording can be started and stopped from the web interface. The cameras stream a lowres-image to the web interface, both while recording and when idle. The latest sensor readings are summarized on the web interface as well.&lt;/li&gt;
&lt;li&gt;There is a servo/PWM-controller board as well. This is used to control a pan-/tilt-head for one of the cameras, which uses 2 servos. The remaining channels are used for controlling the strength of LEDs to light the inner part.&lt;/li&gt;
&lt;li&gt;It's possible to use a smart watch to display webapps on your local network. So far I only know how to do that with Samsung devices: Use Samsung internet to add the web address of the Pi to your favorites, while it's inside your hotspot's network. Samsung internet can be opened on the smartwatch as well, with limited functionality. For example the video display will have significant delay and the corners of the App are very hard to reach, so important buttons should be centered.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;and that's about it&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;I hope this code helps someone get started with their own project. Hopefully you don't have to build something under water :) &lt;/p&gt;

&lt;p&gt;Putting electronics in the water though is something I do not recommend, because waterproofing the components ended up taking the majority of the time I needed for this project. Extra care needs to be taken that the glues aren't water-solulable and other parts aren't harmful to the environment either. &lt;/p&gt;

&lt;p&gt;In the end, the prototype showed that electricity can be generated in the surf on the shoreline. Recording the inside and outside of a wavebreaker produced some footage that makes this so much easier to explain.&lt;/p&gt;

&lt;p&gt;So I hope you'll enjoy watching the above video. And let me know if this code has been helpful, I'd like to hear about similar use cases! :)&lt;/p&gt;

</description>
      <category>raspberrypi</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Underwater Voltmeter - high performance diagrams on Arduino</title>
      <dc:creator>Jonathan Schneider </dc:creator>
      <pubDate>Mon, 03 Jul 2023 17:39:52 +0000</pubDate>
      <link>https://forem.com/shnydercom/underwater-voltmeter-high-performance-diagrams-on-arduino-dg8</link>
      <guid>https://forem.com/shnydercom/underwater-voltmeter-high-performance-diagrams-on-arduino-dg8</guid>
      <description>&lt;p&gt;When you're developing for the web and mobile, you don't need to worry much about the performance of your display. This article covers the case when your device is offline, has limited resources and on top of it all... gets thrown into the water.&lt;/p&gt;

&lt;h1&gt;
  
  
  Video intro
&lt;/h1&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/HD4bUMoowLA"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;It's a bit like coding in the early days of User Interfaces. This interface works like an oscilloscope that can measure the voltage of an analog input pin and display it as a waveform on the screen.  But we need to treat data, time, and updates in other UIs as well. So I hope this article helps you with whatever you want to optimize.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/shnydercom/tutorials/blob/main/underwater-voltmeter/VoltMeterGraph.ino#L65"&gt;Here's the link to the Arduino code&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Challenges
&lt;/h1&gt;

&lt;p&gt;One of the challenges of this project is to make the display as smooth and responsive as possible, without flickering or lagging. To achieve this, &lt;strong&gt;only updated, individual pixels are drawn&lt;/strong&gt; in each loop, instead of clearing and redrawing the whole screen. This way, I can save a lot of time and resources, and make the display more efficient.&lt;/p&gt;

&lt;p&gt;Calculating diagonal lines between data points would take time and resources. Especially because we would need to re-calculate them when we overdraw the previous pixels, so we're constrained to using pixels or horizontal bars.&lt;/p&gt;

&lt;p&gt;It can cause some artifacts or glitches if the pixels are not updated correctly or in sync. So we need to keep track of time to make the display independent of the processor speed.&lt;/p&gt;

&lt;h1&gt;
  
  
  How the algorithm is faster
&lt;/h1&gt;

&lt;p&gt;The algorithm does &lt;strong&gt;&lt;em&gt;not&lt;/em&gt;&lt;/strong&gt; redraw all of the pixels on each frame. Actually, it is pretty picky and benefits from the fact that we only show one value on the Y-Axis, and not multiple lines.&lt;/p&gt;

&lt;p&gt;Here's how it works:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It reads the voltage from an analog pin and adds it to a buffer array.&lt;/li&gt;
&lt;li&gt;It calculates and displays the max, average, and current voltage on the screen as text.&lt;/li&gt;
&lt;li&gt;It draws a waveform on the screen that shows the voltage over time using iterative pixel drawing.&lt;/li&gt;
&lt;li&gt;Iterative pixel drawing means that it only updates the pixels that change in each loop, instead of clearing and redrawing the whole screen.&lt;/li&gt;
&lt;li&gt;To do this, it uses another array called yBuf that stores the height of each pixel on the waveform.&lt;/li&gt;
&lt;li&gt;It shifts the values in yBuf to the left by a number of pixels that depends on the time difference between loops.&lt;/li&gt;
&lt;li&gt;It loops through each pixel on the waveform and updates its color and height according to the values in yBuf and the buffer array.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  The narrower the use case, the easier it is to improve performance
&lt;/h1&gt;

&lt;p&gt;"Drawing the text actually competes for processor time with drawing the diagram, so this could be optimized"&lt;/p&gt;

&lt;p&gt;But.... When we talk about "performance", these kinds of arguments are often brought up. Even if the diagram was quicker, would I actually use the 1 second diagram that often, or are the 5 seconds better? Is the accurate measurement or the moving average more important? Do I need a second value on the y-Axis, or is a single one just fine? All of these questions are more reasons to open-source the code, so your individual code can solve your individual problem. Are your problem and mine then similar enough to be solved with the same code?&lt;/p&gt;

&lt;p&gt;These are the types of questions that developers of libraries, APIs and standards often face, and why custom solutions might have higher, "local" performance.&lt;/p&gt;

&lt;p&gt;Why "local"? It's because it depends on the context, the resource limits at the time of writing the code, the device(s) available, what environment they'll be used in and the person who is implementing it. So it's not just about knowing the Tech, it's about knowing the User(s), the facilitators and people's Needs and Skills as well.&lt;/p&gt;

&lt;p&gt;And someone who is doing this for many people... wow! My deep appreciation for all the API/library/standards-creators out there!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>String-literal Union Types to Array - or - How to kick Pluto out of the list of planets</title>
      <dc:creator>Jonathan Schneider </dc:creator>
      <pubDate>Fri, 07 Oct 2022 00:52:57 +0000</pubDate>
      <link>https://forem.com/shnydercom/string-literal-union-types-to-array-or-how-to-kick-pluto-out-of-the-list-of-planets-f21</link>
      <guid>https://forem.com/shnydercom/string-literal-union-types-to-array-or-how-to-kick-pluto-out-of-the-list-of-planets-f21</guid>
      <description>&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Pluto" rel="noopener noreferrer"&gt;Pluto&lt;/a&gt;! Once a member of our own solar system's list of 9 planets, is now considered a dwarf planet. Who thought that list would change? And if Pluto is ever re-considered to be more than a dwarf, what happens to &lt;a href="https://en.wikipedia.org/wiki/Planet_Nine" rel="noopener noreferrer"&gt;Planet Nine&lt;/a&gt;'s name?&lt;/p&gt;

&lt;p&gt;Similar to our solar system, we sometimes expect that some code or master data doesn't change. But when it does,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;["Pluto"]&lt;/code&gt; is often hardcoded in arrays for dropdown-lists etc.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;{"Pluto": {"discovered":1930, "namedAfter": "Pluto"}}&lt;/code&gt; can be found in keys or values of complex json objects&lt;/li&gt;
&lt;li&gt;or it is a parameter of a &lt;code&gt;function getLegendFromMythologyByDeity(f: "Pluto" | "Flora" | "Juno" | "Apollo" | "Minerva" | "Diana" | "Vulcan" | "Sol")&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;So to improve the situation, we can fill these types by hand. They're called string literal types, more on that topic &lt;a href="https://mariusschulz.com/blog/string-literal-types-in-typescript" rel="noopener noreferrer"&gt;here&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;PlanetsType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Mercury&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Venus&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Earth&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Mars&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Jupiter&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Saturn&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Uranus&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; 
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Neptune&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
  &lt;span class="c1"&gt;//| "Pluto";&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;MythologicalDeityType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Pluto&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Flora&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Juno&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Apollo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Minerva&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Diana&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Vulcan&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sol&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;type&lt;/span&gt; &lt;span class="nx"&gt;MickeysFriendsType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Pluto&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Minnie&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Donald&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;If you additionally want an array that contains all possible permutations of a string literal union type, you could fill that by hand:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PlanetsTypeKeys&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PlanetsType&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Mercury&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="s2"&gt;Venus&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="s2"&gt;Earth&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="s2"&gt;Mars&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="s2"&gt;Jupiter&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="s2"&gt;Saturn&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="s2"&gt;Uranus&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="s2"&gt;Neptune&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;...but you would have to remember to update that each time a new string literal type enters that union.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;PlanetsType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Mercury&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Venus&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Earth&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Mars&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Jupiter&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Saturn&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Uranus&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; 
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Neptune&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; 
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Planet9TheMisterious&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;-- the new one, now the array has to be updated&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  A self-sustaining solution
&lt;/h1&gt;

&lt;p&gt;Remembering to update the "right part" of the code is usually hard, even if you leave a comment. Throwing type errors is thus much better - and because we can't create an Array (runtime - javascript) from a union type (compile time - typescript), we'll use a helper, that'll finally bring us back to the master data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * PlanetsType is a union type of string literals. This is useful for making switch-case statements exhaustive:
 * https://www.typescriptlang.org/docs/handbook/2/narrowing.html#exhaustiveness-checking
 * 
 * below however, we want to get an exhaustive list of all string literals in the union type. By building a helper
 * object with the key set to "s in PlanetsType", we effectively create an enum-like structure with all the
 * string literals of PlanetsType. When you create or change PlanetsType, this helps to keep the 
 * array in PlanetsTypeKeys up-to-date by throwing a typing error. The order of the entries is also preserved
 */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PlanetsTypeHelperObj&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;s&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;PlanetsType&lt;/span&gt; &lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;PlanetsType&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Mercury&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="s2"&gt;Mercury&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="s2"&gt;Venus&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="s2"&gt;Venus&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="s2"&gt;Earth&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="s2"&gt;Earth&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="s2"&gt;Mars&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="s2"&gt;Mars&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="s2"&gt;Jupiter&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="s2"&gt;Jupiter&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="s2"&gt;Saturn&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="s2"&gt;Saturn&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="s2"&gt;Uranus&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="s2"&gt;Uranus&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="s2"&gt;Neptune&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="s2"&gt;Neptune&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;//"Pluto":"Pluto",&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Planet9TheMisterious&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="s2"&gt;Planet9TheMisterious&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PlanetsTypeKeys&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PlanetsType&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PlanetsTypeHelperObj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;PlanetsType&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This turns &lt;code&gt;PlanetsTypeKeys&lt;/code&gt; into an &lt;strong&gt;exhaustive Array of string literals&lt;/strong&gt; in typescript. Because it's based on a json object (also called "dictionary" in this usage), there are no duplicates. Finally, the value-side of the json object (&lt;code&gt;{"VenusKey":"VenusValue"}&lt;/code&gt;) can be anything you like. If you'd like to make the value-part your master data, the &lt;code&gt;PlanetsTypeKeys&lt;/code&gt; will stay up to date, ready to be used ... with a dropdown for example.&lt;/p&gt;

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

</description>
      <category>typescript</category>
    </item>
    <item>
      <title>The Hue-Light Mosquito Killer</title>
      <dc:creator>Jonathan Schneider </dc:creator>
      <pubDate>Fri, 13 Aug 2021 14:29:36 +0000</pubDate>
      <link>https://forem.com/shnydercom/the-hue-light-mosquito-killer-47co</link>
      <guid>https://forem.com/shnydercom/the-hue-light-mosquito-killer-47co</guid>
      <description>&lt;p&gt;Real-life problem: Mosquitos enter your bedroom at night, but electrical Mosquito killers consume electricity (and cartridges) all day. So let's put a smart plug in the middle and schedule the Moskito killers to run in the evening only.&lt;/p&gt;

&lt;p&gt;If that's all you want, you can do it from an App. In the Hue App, you can create a zone for your mosquito killer and create an automation like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5vvedwYK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mojqh5m1hea3wi1d6j9p.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5vvedwYK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mojqh5m1hea3wi1d6j9p.jpg" alt="Click-Through Guide for the Hue App: Create a new zone, switch to the Automations-Tab, create an Automation, choose Custom, set a start and end date to the evening, choose your newly created zone, name the Automation and you're done."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But we're programmers! So if you want finer control or create automations with devices from different IoT manufacturers, you can control individual plugs with &lt;a href="https://nodered.org/"&gt;node-red&lt;/a&gt;. It comes &lt;a href="https://www.raspberrypi.org/software/operating-systems/"&gt;pre-installed on raspbian&lt;/a&gt;, or you can install it &lt;a href="https://nodered.org/#get-started"&gt;locally or in the cloud&lt;/a&gt;. I've used a Philips Hue smart plug because &lt;a href="https://youtu.be/LM7bk_jDHWw"&gt;I've played around with Hue lights and node-red before&lt;/a&gt;. If you're using a different manufacturer, you can &lt;a href="https://flows.nodered.org/"&gt;search the extensive library for a node&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing packages in node-red
&lt;/h3&gt;

&lt;p&gt;Navigate to the URL of your node-red installation, which is &lt;code&gt;http://localhost:1880&lt;/code&gt; if you've installed it locally. Note that the port &lt;code&gt;1880&lt;/code&gt; is most often also the port when you run it on a Raspberry Pi or NAS on your network. Head to "Manage palette" in the side-menu on the right.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9Y1hEY1b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jjusq9dj3byqanudc2nj.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9Y1hEY1b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jjusq9dj3byqanudc2nj.jpg" alt="node-red-manage-palette"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here you can see which nodes are available in your installation, in which package they come, or you can install new packages. The &lt;a href="https://flows.nodered.org/search?term=node-red-contrib-huemagic"&gt;nodes for the Hue lights&lt;/a&gt; can be found in the &lt;code&gt;node-red-contrib-huemagic&lt;/code&gt; package. When you click "install" the package will be downloaded and the nodes in it will be available shortly afterwards in the palette on the left of the main screen.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fsowJ-SD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r0d51nzxod7czeu2w16w.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fsowJ-SD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r0d51nzxod7czeu2w16w.jpg" alt="node-red-install-package"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Connecting node-red with the Hue Bridge
&lt;/h3&gt;

&lt;p&gt;Before we can use them, we need to configure the connection to the hue bridge, which is accessible from the "config" tab in the menu. A click on the "search"-icon will look on the local network for an available hue bridge, so you don't have to manually enter the IP. Once you have the IP, you can generate an API key by clicking on the "user" icon, and pressing the bridge's button within 20 seconds.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1lz6gtiK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pdxkwmt36dnsjw8ng62u.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1lz6gtiK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pdxkwmt36dnsjw8ng62u.jpg" alt="node-red-config-bridge"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding and connecting nodes to send messages
&lt;/h3&gt;

&lt;p&gt;From the panel on the left we drag and drop two inject nodes for testing the smart plug. Clicking the little rectangle to the left of the node will send a message into the flow, but for now the nodes are unconnected. Double-clicking the flow opens a property panel. The inject node which has &lt;code&gt;msg.payload&lt;/code&gt; set to &lt;code&gt;false&lt;/code&gt; switches the plug off, the node which has &lt;code&gt;msg.payload&lt;/code&gt; set to &lt;code&gt;true&lt;/code&gt; switches the plug on. The &lt;code&gt;Name&lt;/code&gt; is just for yourself to tell the nodes apart.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Vfcd67UM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o50y3d9sxvtm3ek4vnht.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Vfcd67UM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o50y3d9sxvtm3ek4vnht.jpg" alt="node-red-switch-off-light"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Accessing a node's documentation
&lt;/h3&gt;

&lt;p&gt;How do you know what &lt;code&gt;msg.payload&lt;/code&gt; should be? That depends on the node that receives the messages. The smart plug can be controlled with the "Hue light" node (because switching on/off is the same behaviour for lights and for switches). To receive messages from the inject nodes, it needs to be connected. All messages flow left to right, so with the "Hue light" having one on the left and one on the right, it means that this node can also send messages. The current state "turned off" is displayed on the bottom. To find out what goes in and out of the "Hue light" node, we can look at the documentation, which you can find by clicking on the book icon.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dr0r9MnL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wy6ir8oetln5zbca9in1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dr0r9MnL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wy6ir8oetln5zbca9in1.jpg" alt="node-red-documentation"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Debugging a flow
&lt;/h3&gt;

&lt;p&gt;From the documentation we can learn that the "Hue light" node has a "simple" and "extended" way of interpreting &lt;code&gt;msg.payload&lt;/code&gt;. In the simple mode it takes a boolean (true/false, 1 or 0), in the "extended" mode a JSON object. The output always has the form of the "extended" JSON object, which you can try out by using a debug node, and checking the output in the "debug" tab. As a plus, if you have several debug nodes, hovering over a debug message will highlight the corresponding debug node in the flow.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--oF5RLL2X--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/122317ou37b7u6dtw2i7.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oF5RLL2X--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/122317ou37b7u6dtw2i7.jpg" alt="node-red-debug"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Mosquito Schedule mixed with our schedule
&lt;/h3&gt;

&lt;p&gt;Now I don't know when exactly Mosquitos are active, but I roughly know mine (Sometimes I have the feeling they're active all night). It's not always regular and sometimes I'm not at home, so it would be great if the plug switched off then as well.&lt;/p&gt;

&lt;p&gt;So as a basis a time-based schedule is necessary, and there are some options: You can install a &lt;a href="https://flows.nodered.org/node/node-red-contrib-light-scheduler"&gt;scheduler-package that's configured inside the flows&lt;/a&gt;, or you could &lt;a href="https://flows.nodered.org/node/node-red-contrib-ui-time-scheduler#frontend--demo"&gt;try out a dashboard scheduler&lt;/a&gt;. You can then still combine that with other nodes. With one of the schedulers and the HueMagic nodes alone we could set up a combined logic including a motion-, temperature- or brightness sensor. But with this article you should have the basics of setting up your very own &lt;code&gt;Mosquito Killer Smart Home Defender™&lt;/code&gt;🤺🛡️ ... Let me know what you've built! 😊&lt;/p&gt;

&lt;h3&gt;
  
  
  While researching for this post I found...
&lt;/h3&gt;

&lt;p&gt;This is also not the only way to protect yourself from mosquito bites. There are repellants and there's even &lt;br&gt;
&lt;a href="https://www.researchgate.net/publication/38031708_Response_Of_Adult_Mosquitoes_To_Light-Emitting_Diodes_Placed_In_Resting_Boxes_And_In_The_Field"&gt;research about which type of bug is attracted by which type of light&lt;/a&gt;. So dimming your LEDs red apparently makes your home less attractive to blood-sucking mosquitos, but may attract other creatures of the night 😉.&lt;/p&gt;

&lt;p&gt;Photo credits for part of the header go to: &lt;br&gt;
&lt;a href="https://unsplash.com/photos/nZgpg4xYhjM"&gt;Wolfgang Hasselmann&lt;/a&gt;&lt;/p&gt;

</description>
      <category>lowcode</category>
      <category>nodered</category>
      <category>iot</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Asking for feedback: Visual editor + template for QR-Reader and -Generator Apps</title>
      <dc:creator>Jonathan Schneider </dc:creator>
      <pubDate>Fri, 16 Jul 2021 00:02:59 +0000</pubDate>
      <link>https://forem.com/shnydercom/asking-for-feedback-visual-editor-template-for-qr-reader-and-generator-apps-1j7f</link>
      <guid>https://forem.com/shnydercom/asking-for-feedback-visual-editor-template-for-qr-reader-and-generator-apps-1j7f</guid>
      <description>&lt;h3&gt;
  
  
  Quick access or setup
&lt;/h3&gt;

&lt;p&gt;Here's &lt;a href="https://metaexplorer.io/try-editor"&gt;a live demo&lt;/a&gt; of the editor on the project homepage,&lt;br&gt;
and of course &lt;a href="https://github.com/shnydercom/metaExplorer-starter"&gt;the repository on github&lt;/a&gt;.&lt;br&gt;
The repo also works as a template&lt;br&gt;
&lt;a href="https://github.com/shnydercom/metaExplorer-starter/generate"&gt;&lt;img alt="image of a template button" src="https://res.cloudinary.com/practicaldev/image/fetch/s--bu7xHTzr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/lzitp0wshy80lwgn7c8v.PNG"&gt;&lt;/a&gt; for your own QR-enabled web apps. Everything open source.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing: The MetaExplorer-starter template
&lt;/h2&gt;

&lt;p&gt;I'm quite a fan of storybook, because you can communicate with your team mates about the features of frontend components. So everyone gets a clear idea of the UI component parts: But to talk about how two UI components look side by side, UX or measuring funnels I always wanted to have an editor for communicating more complex topics. Topics like how the application state changes, how good UX can be reused or if more copy moves the rest of the content "below the fold".&lt;/p&gt;

&lt;p&gt;And so here's a simple App with a QR-reader and a QR-writer and some navigation. You can edit this App without code, and add your own UI components after reviewing them in storybook. &lt;br&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bKjRd7A7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yeez9k630no2377r6vur.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bKjRd7A7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yeez9k630no2377r6vur.png" alt="sectionimage-mxp"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Feedback to make it more usable
&lt;/h2&gt;

&lt;p&gt;Now it's mostly an editor for Application state, inspired by node-based editors such as in blender3D, node-red or camunda (BPMN).&lt;/p&gt;

&lt;p&gt;Let me know your thoughts! Is it hard/easy to use, intuitive or not, are you against visual editors in general, what do you need in an editor? - and how long did it take you to crash it? 😉&lt;/p&gt;

&lt;p&gt;Thanks! Wish everyone a productive time :) 📝&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>typescript</category>
      <category>webdev</category>
      <category>react</category>
    </item>
    <item>
      <title>Devs go to stackoverflow, where do non-devs go?</title>
      <dc:creator>Jonathan Schneider </dc:creator>
      <pubDate>Tue, 11 May 2021 10:06:48 +0000</pubDate>
      <link>https://forem.com/shnydercom/devs-go-to-stackoverflow-where-do-non-devs-go-47l8</link>
      <guid>https://forem.com/shnydercom/devs-go-to-stackoverflow-where-do-non-devs-go-47l8</guid>
      <description>&lt;p&gt;We all know the process: Google, stackoverflow, copy&amp;amp;paste --&amp;gt; Time saved!&lt;br&gt;
Recently I had to do a non-dev task that involved googling a lot, so I made a &lt;a href="https://bit.ly/googleFindInSites"&gt;script to create the search-links in the browser console&lt;/a&gt;. That got me thinking that it might be useful for more people: So it's under permissive open source. Opening the console requires a bit of technical knowledge, so I also put together &lt;a href="https://youtu.be/nW6GGYhr0fA"&gt;a little video of copying and running it&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So, &lt;strong&gt;how&lt;/strong&gt; to use it is clear. &lt;strong&gt;Who&lt;/strong&gt; would use it though?&lt;/p&gt;

&lt;p&gt;Just listing it on github and youtube does &lt;em&gt;NOT&lt;/em&gt; put it in front of people. &lt;a href="https://samuelmullen.com/articles/startup-fallacies-if-you-build-it-they-will-come/"&gt;"Built it and they will come"&lt;/a&gt; doesn't work for startups and it seems - neither for little stuff. &lt;/p&gt;

&lt;p&gt;But &lt;strong&gt;where&lt;/strong&gt; do you put your little scripts, so that they help someone?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclaimer&lt;/em&gt;: The links above will get me click-analytics, which I intend to share once I've learned something from the numbers.&lt;br&gt;
Here are the full, non-tracked links: &lt;a href="https://github.com/shnydercom/codesnippets/blob/master/googleFindInSites.js"&gt;Script&lt;/a&gt;, &lt;a href="https://www.youtube.com/watch?v=nW6GGYhr0fA"&gt;Youtube-video&lt;/a&gt;&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>javascript</category>
    </item>
    <item>
      <title>No code for nocode (1)</title>
      <dc:creator>Jonathan Schneider </dc:creator>
      <pubDate>Fri, 05 Mar 2021 14:10:33 +0000</pubDate>
      <link>https://forem.com/shnydercom/no-code-for-nocode-1-1dl9</link>
      <guid>https://forem.com/shnydercom/no-code-for-nocode-1-1dl9</guid>
      <description>&lt;p&gt;This post is part of a series:&lt;/p&gt;

&lt;h1&gt;
  
  
  Summary of the series
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Nocode solves a business problem rather than a technical problem: It can be understood as a form of declarative programming &lt;/li&gt;
&lt;li&gt;Instead of saving the declarative programs in a database, we could use git repos and our existing dev-toolset&lt;/li&gt;
&lt;li&gt;We can build on top of existing data formats and create mappings between declarative and imperative programming&lt;/li&gt;
&lt;li&gt;Different non-developers can get different shards of declarative data. That means: Custom nocode-tools for our team mates!&lt;/li&gt;
&lt;li&gt;We'll look at a proof of concept with a popular tech stack. It shows that the concept works, what its possibilities and its boundaries are&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  The promise
&lt;/h1&gt;

&lt;p&gt;"No coding needed" - does that mean thousands of devs will loose their jobs and dev.to can close their virtual doors?&lt;/p&gt;

&lt;p&gt;Yes ;) All your fears will come true, the same way your past selves lost their jobs when you created that automation-script you're now a little proud of. But why was your automation-script so useful in the first place?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Did nobody like to do that task?&lt;/li&gt;
&lt;li&gt;Your team mate often asked for A, B and C, and your script takes &lt;code&gt;paramA&lt;/code&gt;, &lt;code&gt;paramB&lt;/code&gt;, &lt;code&gt;paramC&lt;/code&gt; as input?&lt;/li&gt;
&lt;li&gt;Three different colleagues tried the task before, with three different results - and you had to combine their work?&lt;/li&gt;
&lt;li&gt;It has run so often that you're thinking about version 2&lt;/li&gt;
&lt;/ul&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%2F6u71fcdlkya18v1wl9j8.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6u71fcdlkya18v1wl9j8.jpg" alt="depiction of a car startup, early 20th century" width="800" height="160"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Productivity
&lt;/h1&gt;

&lt;p&gt;All of the above points don't have anything to do with code in the script. Code might have been important to build it, but the value your script creates for your team and organisation comes from something else. Your script touches the work of others, and makes their day a bit easier. This lets all of you work better together, but only if you're not on vacation. When your team mate sends you an email with A, B and C - &lt;em&gt;your email-inbox is the current nocode-interface to that script&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The emails keep coming
&lt;/h2&gt;

&lt;p&gt;Let's say word-of-mouth makes your script really popular, but now you get emails with B, C, D and E - and you've never heard of D and E, but the colleagues from the other department talk about it all the time. Your team mate has the &lt;a href="https://en.wikipedia.org/wiki/Domain_knowledge" rel="noopener noreferrer"&gt;Domain knowledge&lt;/a&gt; how D and E can be combined to form A. Wouldn't it be nice if your team mate could help improve the problems with your inbox filling up, and combine D and E?&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance issues, outside computers
&lt;/h2&gt;

&lt;p&gt;From a productivity perspective, once one bottleneck is fixed, another one needs fixing. What happened with your script, your colleagues and your team mate is the normal way an organization progresses. From a business perspective though, the power to progress is important. This power in the hands of more employees, that's what "Nocode" then sounds like.&lt;/p&gt;

&lt;h1&gt;
  
  
  Data about common problems
&lt;/h1&gt;

&lt;p&gt;The problems that nocode-tools are currently trying to fix are mostly the ones that have existed across different industries. For example building web-interfaces. Centering elements in CSS? Just one click inside a nocode-tool.&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%2Fpgzktleygtd0pe438jjh.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpgzktleygtd0pe438jjh.jpg" alt="3 buttons, for left- center- and right-alignment" width="800" height="160"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Where does your data go in which format?
&lt;/h2&gt;

&lt;p&gt;Now the nocode-tools may save &lt;code&gt;element.centered = true&lt;/code&gt; somewhere in a database, somewhere in the cloud. Another one will save &lt;code&gt;UIComponent.alignment = 'center'&lt;/code&gt; somewhere else, and both tools probably use a similar center-button. So there exists an unwritten industry-standard saying how a center-button should look like. However, there isn't an industry standard saying &lt;em&gt;how&lt;/em&gt; or &lt;em&gt;where&lt;/em&gt; to save that information.&lt;/p&gt;

&lt;h2&gt;
  
  
  Declaring &lt;em&gt;what&lt;/em&gt; the program should do
&lt;/h2&gt;

&lt;p&gt;"Alexa, center that element" is something we can't universally do because every tool implements the center-button-click in its own way. This is different when there is a commonly understood format for "centered-ness". &lt;em&gt;How&lt;/em&gt; the button-click is turned into CSS or mobile elements is less important than &lt;em&gt;what&lt;/em&gt; that button does.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closed source and interfaces
&lt;/h2&gt;

&lt;p&gt;With nocode-platforms, the declarative part of our program is hidden away in a database in a proprietary format. This is actually good in the Genesis-phase of tool-development, because changes happen often and code and interfaces stabilize slowly. The more mature a tool becomes, the more it has to stabilize though. For example a company working on a voice-plugin needs interfaces to "just work".&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%2Fabdmg96cpwxzcjapp8lb.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fabdmg96cpwxzcjapp8lb.jpg" alt="electricity lines going to different factories" width="800" height="160"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;h1&gt;
  
  
  De-facto standards
&lt;/h1&gt;

&lt;p&gt;At some point a tool reaches a point where it's a de-facto standard, where people say "Excel" instead of "spreadsheet". Excel's interfaces can't change that much any more, spreadsheet-tools are rather similar. This process is called &lt;a href="https://www.investopedia.com/terms/c/commoditize.asp" rel="noopener noreferrer"&gt;commoditization&lt;/a&gt;. New tools based on spreadsheets can change a lot though, and a lot of nocode-tools actually make use of spreadsheets. How reliable can a proprietary de-facto standard be?&lt;/p&gt;

&lt;h2&gt;
  
  
  Remember Flash and Actionscript?
&lt;/h2&gt;

&lt;p&gt;Flash offered something that was not possible on the web at the time. Nowadays, animations, video, audio and webcam-access seem normal, but then it was a substantial improvement. More importantly, in the visual editor it was quicker to design and animate than in code, and in the code-editor it was easier to create the behaviour. &lt;/p&gt;

&lt;p&gt;What made it popular was the interface between designers and coders. What made it unpopular were security-issues, performance-issues and web-standards catching up. Although many were affected, there was only one company that could really fix Flash. When major parts were open sourced it was already too late, Flash was on the decline. Code-bases had to be rewritten rather than migrated because the custom tooling worked well with Flash, but not "the outside world". However, the source code for Flash-programs was saved in the file system. If a nocode-platform goes down it's up to the platform what happens with the nocode-programs it hosts.&lt;br&gt;
Worst-case scenario: No more code. &lt;/p&gt;

&lt;p&gt;We gladly have better open standards now, and the nocode-movement lets people solve more problems. If the nocode approach leads to many proprietary standards, that means integrators and customizers can get more business. Skillsets aren't as easily transferable between tools, which leads to rather closed communities around those standards.&lt;/p&gt;

&lt;h1&gt;
  
  
  Self-hosted vs cloud
&lt;/h1&gt;

&lt;p&gt;The risk of a platform failing is lower with tools that can run both in the cloud, on-premise and locally. WordPress would be an example where anything "nocode" can be easily migrated. This is part of the design, thanks to established data formats. To migrate a mature data format would take quite an investment: The efforts for the &lt;a href="https://wordpress.org/gutenberg/" rel="noopener noreferrer"&gt;Gutenberg editor&lt;/a&gt; from WordPress can give an indicator for that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Open data formats
&lt;/h2&gt;

&lt;p&gt;Open, mature data formats have a steeper learning curve, but it pays off to develop against established interfaces. Our IDEs are already one step closer to nocode-environments, especially if we count in extensions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Frontend and backend devs interface with OpenAPI/graphql schemas&lt;/li&gt;
&lt;li&gt;devs and devops interface with docker-compose files/kubernetes&lt;/li&gt;
&lt;li&gt;devs and data/analytics interface with SQL queries&lt;/li&gt;
&lt;li&gt;Pull requests get synced into JIRA for Project managers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So maybe the &lt;a href="https://code.visualstudio.com/api/language-extensions/language-server-extension-guide" rel="noopener noreferrer"&gt;VS Code language server&lt;/a&gt; is a good starting point for the next low/nocode-tool. Asking &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What part of my IDE might be interesting for my colleagues?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Also, when data formats are open and limited in their scope, it seems that dev-communities form around them. When the code fits into a stackoverflow-like answer, I guess it's easier to help each other.&lt;/p&gt;

&lt;h1&gt;
  
  
  To code or not to code, is this really a question?
&lt;/h1&gt;

&lt;p&gt;All in all with nocode, we developers won't lose our jobs and Dev.to won't have to close their virtual doors either. There will be more problems to solve. Let's welcome new problem solvers, whether they're coders, hackers, software developers or the new &lt;em&gt;citizen developers&lt;/em&gt;, and improve interfaces together.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>webdev</category>
      <category>discuss</category>
      <category>productivity</category>
    </item>
    <item>
      <title>schema.org-objects from GraphQL, typescript and a headless WordPress</title>
      <dc:creator>Jonathan Schneider </dc:creator>
      <pubDate>Tue, 23 Feb 2021 17:30:14 +0000</pubDate>
      <link>https://forem.com/shnydercom/the-headless-seo-middleman-or-wordpress-graphql-schema-org-and-typescript-combined-16gj</link>
      <guid>https://forem.com/shnydercom/the-headless-seo-middleman-or-wordpress-graphql-schema-org-and-typescript-combined-16gj</guid>
      <description>&lt;h2&gt;
  
  
  Let's be lazy:
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/shnydercom/wpgraphql-to-linkeddata" rel="noopener noreferrer"&gt;Here's a repo with a basic setup&lt;/a&gt; that maps from wpgraphql to json-linked-data in the schema.org vocabulary through a type-safe function. If that sounds complicated, here's the long explanation:&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%2F8rk6z00pae0337zph8u2.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%2F8rk6z00pae0337zph8u2.png" alt="section-jsonld" width="800" height="160"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;h1&gt;
  
  
  Intro to schema.org
&lt;/h1&gt;

&lt;p&gt;To be more easily discoverably by search engines, &lt;a href="https://schema.org/WebSite" rel="noopener noreferrer"&gt;WebSite&lt;/a&gt;s can add structured data to their content. In this way google find's a site's own &lt;a href="https://schema.org/Rating" rel="noopener noreferrer"&gt;Rating&lt;/a&gt;s:&lt;br&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%2Fi%2Fq6tpby90jjcd5hphkzaj.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fq6tpby90jjcd5hphkzaj.jpg" alt="showing google search results with some rating stars" width="340" height="340"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But it is also used to make the Web more interactive. Emails can include data about possible &lt;a href="https://schema.org/Action" rel="noopener noreferrer"&gt;Action&lt;/a&gt;s, which GMail turned into this "View Pull Request" button here, so you don't have to open, read and click a link in the email:&lt;br&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%2Fi%2Fecphffkcie69kj8r6gzt.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fecphffkcie69kj8r6gzt.jpg" alt="gViewPR_border" width="340" height="340"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The examples above are using a common, open vocabulary, schema.org, so other email-services could understand the structured data in github's email as well. More importantly, we can add data structured in json-ld and other formats to our own web pages, and for example make it easier for people to find an &lt;a href="https://schema.org/Event" rel="noopener noreferrer"&gt;Event&lt;/a&gt; we're hosting close to them:&lt;br&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%2Fi%2Fxj5w66zsxvx3y3j5fkrr.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fxj5w66zsxvx3y3j5fkrr.jpg" alt="gEvents_border" width="340" height="340"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I don't know how many readers are hosting events, but I guess it would be easier to let Event hosts fill in the data dynamically, for example with WordPress.&lt;/p&gt;
&lt;h2&gt;
  
  
  Isn't there a plugin for it?
&lt;/h2&gt;

&lt;p&gt;The short answer is &lt;a href="https://wordpress.org/plugins/search/schema.org/" rel="noopener noreferrer"&gt;Yes, many&lt;/a&gt;. This article is for typescript developers who work with GraphQL, we'll only use WordPress as a headless Content Management System (CMS). This article doesn't even include a single line of PHP, instead we'll generate typescript from a GraphQL schema. This approach actually works without WordPress, but it's easier to start with it.&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%2Flk3d4un00cfty3m3vzbz.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%2Flk3d4un00cfty3m3vzbz.png" alt="section-wpgraphql" width="800" height="160"&gt;&lt;/a&gt; &lt;/p&gt;
&lt;h1&gt;
  
  
  From WordPress to GraphQL endpoint
&lt;/h1&gt;

&lt;p&gt;The &lt;a href="https://www.wpgraphql.com/" rel="noopener noreferrer"&gt;WPGraphQL Plugin&lt;/a&gt; is quickly installed from the admin panel of your WordPress installation.&lt;br&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%2Fi%2F6exs28rowkkwzfhgsrux.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%2Fi%2F6exs28rowkkwzfhgsrux.PNG" alt="Wordpress Admin page: Adding a new plugin" width="767" height="357"&gt;&lt;/a&gt;&lt;br&gt;
You don't have to leave the Admin Panel to play around with queries either, you get a GraphQL IDE right inside WordPress:&lt;br&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%2F5a79rjg1jj4gmmshmpdf.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%2F5a79rjg1jj4gmmshmpdf.PNG" alt="GraphqlIDE" width="800" height="512"&gt;&lt;/a&gt; &lt;br&gt;
That query retrieves all the posts in a given category, a short excerpt and a featured image - ideal to create a preview for a blog post.&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%2Failzwj8u2uhz0vk0pinu.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%2Failzwj8u2uhz0vk0pinu.png" alt="section-gql-schema" width="800" height="160"&gt;&lt;/a&gt; &lt;/p&gt;
&lt;h1&gt;
  
  
  From GraphQL endpoint to GraphQL schema
&lt;/h1&gt;

&lt;p&gt;Now this is security relevant: When we have a graphql API, it can tell a client about its structure with a so-called &lt;a href="https://graphql.org/learn/introspection/" rel="noopener noreferrer"&gt;introspection query&lt;/a&gt;. You can generate a graphql schema with such a query, if you don't already have access to the graphql schema. An attacker could do the same and try to find vulnerabilities in your API, that's why it's better to have it switched off in production. WPGraphQL has this switched off by default, you need to go into the settings to switch it on:&lt;br&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%2Fw49nwrcwd6e7tp2wezc0.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%2Fw49nwrcwd6e7tp2wezc0.PNG" alt="GraphqlPublicIntrospection" width="800" height="179"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While it's switched on, we can use standard functionality from the &lt;code&gt;graphql&lt;/code&gt; package and &lt;code&gt;node.js&lt;/code&gt; to generate the schema and save it locally.&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="nx"&gt;fs&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fs&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;fetch&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node-fetch&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;getIntrospectionQuery&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;printSchema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;buildClientSchema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;graphql&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * runs an introspection query on an endpoint and retrieves its result
 * thanks to this gist:
 * https://gist.github.com/craigbeck/b90915d49fda19d5b2b17ead14dcd6da
 */&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&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;introspectionQuery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getIntrospectionQuery&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;response&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;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://blog.example.com/graphql&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;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&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="s2"&gt;application/json&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;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;introspectionQuery&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&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;schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;buildClientSchema&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;outputFile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./wpgraphql-schema.gql&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;outputFile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;printSchema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;blog.example.com&lt;/code&gt; with your domain or localhost, whereever your graphql endpoint can be found. To be able to run that script, you need to set &lt;code&gt;"type": "module"&lt;/code&gt; in your package.json (because it's in ES6 syntax).&lt;br&gt;
And once you have the &lt;code&gt;wpgraphql-schema.gql&lt;/code&gt; file, please, switch off introspection.&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%2Fctjgj6e27i90i5t53jpe.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%2Fctjgj6e27i90i5t53jpe.png" alt="section-gql2ts" width="800" height="160"&gt;&lt;/a&gt; &lt;/p&gt;
&lt;h1&gt;
  
  
  From GraphQL schema to typescript
&lt;/h1&gt;

&lt;p&gt;With the &lt;code&gt;wpgraphql-schema.gql&lt;/code&gt; file we can already create typescript types. In order for that to work we'll add graphql-generator to our project:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;yarn add @graphql-codegen/cli @graphql-codegen/typescript @graphql-codegen/typescript-operations -D&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;To run the generator, it needs a little bit of configuration, this is done in the &lt;code&gt;codegen.yml&lt;/code&gt;-file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;overwrite&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./wpgraphql-schema.gql"&lt;/span&gt;
&lt;span class="na"&gt;documents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;src/**/*.graphql"&lt;/span&gt;
&lt;span class="na"&gt;generates&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;src/generated/wp-graphql.ts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;typescript"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;typescript-operations"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first line tells it to overwrite existing .ts-files, the second one points to the graphql schema we just got from the endpoint, and the third is the &lt;a href="https://graphql-code-generator.com/docs/getting-started/documents-field" rel="noopener noreferrer"&gt;glob-pattern or file where our queries, mutations, fragments and subscriptions can be found&lt;/a&gt;. If we don't have any "documents", we can still run the generator with:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;yarn graphql-codegen --config codegen.yml&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;and get types for our graphql schema. So getting all graphql schema-types is configured with the &lt;code&gt;plugins:&lt;/code&gt; &lt;code&gt;- "typescript"&lt;/code&gt; part. Getting queries etc is configured with the &lt;code&gt;- "typescript-operations"&lt;/code&gt; part. That adds types for all "documents" it finds to our .ts-file. You could also generate separate .ts-files, this is up to you (to split server-features from client-features like queries and fragments, for example). &lt;/p&gt;

&lt;p&gt;We could already use the wpgraphql &lt;code&gt;Post&lt;/code&gt;-type to create a mapper-function to &lt;code&gt;schema.org/BlogPosting&lt;/code&gt;. However, that type is pretty large and we might not always query all fields for a given blog post. &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%2F6u400t7g24r1dlh7b9sp.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%2F6u400t7g24r1dlh7b9sp.PNG" alt="PostTypeOverview" width="800" height="157"&gt;&lt;/a&gt;&lt;br&gt;
As you can see, we don't need to write documentation on the types from wpgraphql, it gets the documentation from the graphql schema.&lt;/p&gt;

&lt;p&gt;To simplify our queries, we can create a graphql fragment like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;fragment&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PostPreview&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;excerpt&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;featuredImage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;sourceUrl&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;altText&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;RAW&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;srcSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;THUMBNAIL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and now any time we want to query these exact same fields we can just reference it in a query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;wpPostPreviewByCategory&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;categoryName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"my-side-projects"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;edges&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="n"&gt;PostPreview&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That query gets us the posts from a particular category, but only the fields from the fragment for each post. This is great for reusability in graphql-queries, and &lt;code&gt;graphql-codegen&lt;/code&gt; generates the type &lt;code&gt;PostPreviewFragment&lt;/code&gt; for us, so we can create a mapper-function that consistently works across queries.&lt;/p&gt;

&lt;p&gt;We can now begin with the mapper-function:&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;mapWpPostPreviewToSchemaBlogPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PostPreviewFragment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;wpBaseURL&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;BlogPosting&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...curious about wpBaseURL and BlogPosting? We'll use that to build our json-ld&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fxoesd0688283cif8h1ia.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%2Fxoesd0688283cif8h1ia.png" alt="section-ts2jsonld" width="800" height="160"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;h1&gt;
  
  
  From typescript to schema.org
&lt;/h1&gt;

&lt;p&gt;Here's &lt;a href="https://schema.org/BlogPosting#examplespannel" rel="noopener noreferrer"&gt;a good example of what a Blogposting can look like&lt;/a&gt; with json-ld and other formats that mix with HTML. The benefit of adding a script-tag with json-ld is that we can generate our website - the &lt;em&gt;un_structured data - in another way than our publicly available _structured&lt;/em&gt; data. This gives you more flexibility, especially since it's allowed to add several script-tags with &lt;code&gt;type="application/ld+json"&lt;/code&gt; to web pages. &lt;/p&gt;

&lt;p&gt;Of course, for the &lt;em&gt;human&lt;/em&gt; readers of our website we would want something visual. For our blog post we might display a link, an image, the post's title and a short preview. The &lt;em&gt;machines&lt;/em&gt; reading our website don't know which is what. To help them read it properly as a BlogPosting, the equivalent json-ld would look like this:&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;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"application/ld+json"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@context&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="s2"&gt;https://schema.org&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="s2"&gt;@type&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="s2"&gt;BlogPosting&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="s2"&gt;@id&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="s2"&gt;https://shnyder.com/business-model-canvas-for-metaexplorer&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="s2"&gt;name&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="s2"&gt;Business Model Canvas for MetaExplorer&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="s2"&gt;abstract&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="s2"&gt;&amp;lt;p&amp;gt;Before starting out with my plans to turn metaexplorer into a business, I wrote a business plan &amp;amp;#8211; and because 2020 showed us what it thinks of plans&amp;amp;#8230;&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;\n&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="s2"&gt;image&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@type&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="s2"&gt;ImageObject&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="s2"&gt;name&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="s2"&gt;Metaexplorer business model canvas, 2020-12-28&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="s2"&gt;contentUrl&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="s2"&gt;https://shnyder.com/wp-content/uploads/2020/12/IMG_20201228_234552-scaled.jpg&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="s2"&gt;thumbnailUrl&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="s2"&gt;https://shnyder.com/wp-content/uploads/2020/12/IMG_20201228_234552-150x150.jpg&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="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see in the example's &lt;code&gt;"@id"&lt;/code&gt;-field and image urls, it includes a domain name - my blog's domain. Relative URLs are also possible in json-ld, so on shnyder.com I could use this:&lt;br&gt;
&lt;code&gt;"@id": "/business-model-canvas-for-metaexplorer"&lt;/code&gt;&lt;br&gt;
Here on dev.to I would want to include the full domain name. To let our mapping-function include my domain, we add the optional parameter &lt;code&gt;wpBaseURL&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So how do we get the json-ld from the graphql types? The library &lt;code&gt;schema-dts&lt;/code&gt; helps us do exactly that, and in a typesafe way. That means we get intellisense on the &lt;a href="https://schema.org/image" rel="noopener noreferrer"&gt;actual schema.org vocabulary&lt;/a&gt; (&lt;a href="https://en.wikipedia.org/wiki/Ontology_(information_science)" rel="noopener noreferrer"&gt;ontology&lt;/a&gt;, even). &lt;br&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%2Fjohek7cbuz0ec5u2tw53.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%2Fjohek7cbuz0ec5u2tw53.PNG" alt="schema-dts-intellisense" width="744" height="274"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can install it with:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;yarn add schema-dts&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;A naive approach would be to map only the "happy path" from graphql to json-ld, where we always have the data that we want and need. That approach would look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PostPreviewFragment&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./../generated/wp-graphql&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;BlogPosting&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;schema-dts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * mapper-function to create schema.org/BlogPosting(s) for previews from fragments
 * @param input a fragment of a WordPress blog post
 * @param wpBaseURL the base domain of your wordpress installation. Used to add the slug
 */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;mapWpPostPreviewToSchemaBlogPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PostPreviewFragment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;wpBaseURL&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;BlogPosting&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;featuredImgNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;featuredImage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;thumbnailUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;featuredImgNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;srcSet&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;val&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;val&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BlogPosting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@type&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="s2"&gt;BlogPosting&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;wpBaseURL&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;wpBaseURL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&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="nx"&gt;input&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="na"&gt;abstract&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;excerpt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@type&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="s2"&gt;ImageObject&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;featuredImgNode&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="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;featuredImgNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;altText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;contentUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;featuredImgNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sourceUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;thumbnailUrl&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;output&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 this does compile in a standard typescript configuration. The thing is that we can't always be sure to get a preview image, and other fields might be empty as well. Handling empty fields will prevent errors in the future. So for the mapper function, we need to set &lt;code&gt;compilerOptions.strict&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt; in our tsconfig.json. If we try to compile again, we'll receive lots of errors such as:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;- error TS2533: Object is possibly 'null' or 'undefined'.&lt;/code&gt;&lt;br&gt;
and&lt;br&gt;
&lt;code&gt;Type 'null' is not assignable to type 'string | PronounceableTextLeaf | readonly PronounceableText[] | undefined'.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Since we don't want something like &lt;code&gt;"title": undefined&lt;/code&gt; in our json-ld, we have to remove the fields from the output-object altogether. The first thought to fix this might be to add lots of if-statements. But with the recent advances in ES6 and typescript, we have the option to create the output in a way that looks very much like a nested json-ld-object. Ant such a nested object is what we want to output. The main difference is that this nested object includes conditions for safe mapping as well:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * mapper-function to create schema.org/BlogPosting(s) for previews from fragments
 * @param input a fragment of a WordPress blog post
 * @param wpBaseURL the base domain of your wordpress installation. Used to add the slug
 */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;mapWpPostPreviewToSchemaBlogPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;PostPreviewFragment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;wpBaseURL&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;BlogPosting&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&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;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;featuredImgNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;featuredImage&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;featuredImage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;thumbnailUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;featuredImgNode&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;featuredImgNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;srcSet&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;featuredImgNode&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;srcSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;val&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;val&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BlogPosting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@type&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="s2"&gt;BlogPosting&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="nx"&gt;wpBaseURL&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;wpBaseURL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&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="nx"&gt;input&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="p"&gt;...(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;excerpt&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;abstract&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;excerpt&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
        &lt;span class="p"&gt;...(&lt;/span&gt;&lt;span class="nx"&gt;featuredImgNode&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@type&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="s2"&gt;ImageObject&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="nx"&gt;featuredImgNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;featuredImgNode&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="p"&gt;}),&lt;/span&gt;
                &lt;span class="p"&gt;...(&lt;/span&gt;&lt;span class="nx"&gt;featuredImgNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;altText&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&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="nx"&gt;featuredImgNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;altText&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;featuredImgNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sourceUrl&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="na"&gt;contentUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;featuredImgNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sourceUrl&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;thumbnailUrl&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;thumbnailUrl&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;output&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;23 lines without null-checks, compared to 32 lines with strict type checks. Plus, this notation scales into nested structures. Not much extra effort - but the reliability has improved greatly. Let's look at it the code detail. First, the output is an Object typed "BlogPosting", and it has to include a &lt;code&gt;"@type": "BlogPosting"&lt;/code&gt;. Typescript types are lost during compilation, whereas &lt;code&gt;"@type"&lt;/code&gt; gets baked into json:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BlogPosting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@type&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="s2"&gt;BlogPosting&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;The three dots that appear across the function are &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax" rel="noopener noreferrer"&gt;spread operators&lt;/a&gt;. Those operators are trying to spread the result of the evaluation in brackets &lt;code&gt;()&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;...(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&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="nx"&gt;input&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="s2"&gt;```


Inside the brackets there's a logical operator, which returns the second part [if the first one is truthy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_AND#description), otherwise the first one. That means if one of our input fields like `&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="s2"&gt;` is undefined, null or an empty string, this is what the spread operator gets to see:


```&lt;/span&gt;&lt;span class="nx"&gt;javascript&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;mytestvar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="kc"&gt;undefined&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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mytestvar&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;mytestvar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="kc"&gt;null&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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mytestvar&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;mytestvar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mytestvar&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;mytestvar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="kc"&gt;false&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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mytestvar&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;mytestvar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mytestvar&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;//always an object with no fields&lt;/span&gt;
&lt;span class="s2"&gt;```


This way of converting is helpful to secure our string-based values, but it's not a magic syntax for API conversion. 

However, if we do have an `&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="s2"&gt;`, the second expression `&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;input&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="s2"&gt;` is an object, and its key(s) and value(s) are added to the containing object.

## Conclusion
Now you have a pipeline to build structured data from your graphql API, independent of which frontend technology you choose. Structured data can have a good effect on SEO, some of it is displayed as special widgets in google. With `&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;dts&lt;/span&gt;&lt;span class="s2"&gt;` you don't only avoid typos while you build structured data, your IDE will help you find the right fields for your `&lt;/span&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;type&lt;/span&gt;&lt;span class="s2"&gt;`s. 

If you're thinking about where to put those conversions in your architecture, maybe ideas like the [middleman engine](https://css-tricks.com/reconciling-editor-experience-and-developer-experience-in-the-cms/#3-the-middleman-engine) can even help improve the handling of data in your organization as a whole.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>typescript</category>
      <category>graphql</category>
      <category>wordpress</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Monorepos: Lerna, TypeScript, CRA and Storybook combined</title>
      <dc:creator>Jonathan Schneider </dc:creator>
      <pubDate>Thu, 31 Oct 2019 16:16:10 +0000</pubDate>
      <link>https://forem.com/shnydercom/monorepos-lerna-typescript-cra-and-storybook-combined-4hli</link>
      <guid>https://forem.com/shnydercom/monorepos-lerna-typescript-cra-and-storybook-combined-4hli</guid>
      <description>&lt;h1&gt;
  
  
  Let’s be lazy:
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://github.com/shnydercom/lerna-typescript-cra-uilib-starter" rel="noopener noreferrer"&gt;repository on github&lt;/a&gt;&lt;br&gt;
That’s the code for the starter repository.&lt;br&gt;
Also made this repository a template repository&lt;br&gt;
&lt;a href="https://github.com/shnydercom/lerna-typescript-cra-uilib-starter/generate" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Flzitp0wshy80lwgn7c8v.PNG" alt="image of a template button"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This post details &lt;em&gt;why&lt;/em&gt;, how to prevent errors and how to do it yourself. It is useful if you want to set up a monorepo for an existing codebase, or if you run into errors when extending your monorepo.&lt;/p&gt;
&lt;h2&gt;
  
  
  Updated to use react-scripts v4.0.2!
&lt;/h2&gt;

&lt;p&gt;With this update, the template contains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the latest React@^17.0.1 and storybook&lt;/li&gt;
&lt;li&gt;some example stories and components in the UI library part&lt;/li&gt;
&lt;li&gt;those components can use css and scss, and CSS gets built into the output folder, along with type definitions&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;modifying&lt;/em&gt; the UI library triggers a storybook hot reload, &lt;em&gt;building&lt;/em&gt; the UI library triggers a CRA hot reload&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  So, for the not-so-lazy:
&lt;/h2&gt;

&lt;p&gt;If you've been using ReactJS in more than one project or are building multiple Apps, you've probably come across lerna already. Since setting up webpack can be tricky, the choice is usually to use create-React-app as long as possible. So we're going to look at how this works with a centralised TypeScript config that we'll also use for our ui component library, which we'll put in a separate repository. We’ll use yarn since we’ll make use of yarn workspaces as well.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;yarn init&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;a private package as the root of our monorepo. Why private? Because private packages don’t get published to npm, our root is only there for organizing everything, and lastly defining yarn workspaces only works in a private package.&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F94iwtvt9yqqksdv3ssyq.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F94iwtvt9yqqksdv3ssyq.png" alt="Lerna logo"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  Introducing: Lerna
&lt;/h1&gt;

&lt;p&gt;First of all, you’ll need to install lerna, and while you can do that globally, I recommend installing it in your monorepo unless you (and the contributors to your monorepo) want to author lots of monorepos with lerna and it’s part of your standard toolset.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;yarn add lerna -D&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now we have lerna, which gives us organization tools for monorepos. For example initialization:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;yarn lerna init&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This will create a &lt;code&gt;lerna.json&lt;/code&gt; file and a &lt;code&gt;packages&lt;/code&gt; folder. Since we’ll use yarn workspaces, we need to define yarn as our &lt;code&gt;npmClient&lt;/code&gt; and set &lt;code&gt;useWorkspaces&lt;/code&gt; to true. Our &lt;code&gt;lerna.json&lt;/code&gt; will end up looking like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"packages"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"packages/*"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"npmClient"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"yarn"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"useWorkspaces"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that is all the configuration we need for lerna.&lt;/p&gt;

&lt;p&gt;Since we’re using yarn workspaces, we need to modify our &lt;code&gt;package.json&lt;/code&gt;, by adding:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"workspaces"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"packages/*"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: your &lt;code&gt;packages&lt;/code&gt;-folder doesn’t need to have that name. You could also have your ui-lib, apps and server code in different subfolders. For using workspaces and lerna together, you should however define them in both &lt;code&gt;lerna.json&lt;/code&gt; and &lt;code&gt;package.json&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Farv15fm07g2q9bo7wcpn.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Farv15fm07g2q9bo7wcpn.png" alt="Storybookjs logo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Project Setup: UI component library package
&lt;/h1&gt;

&lt;p&gt;Initializing sub-packages in monorepos is pretty similar to normal repos, with one thing to note when setting the name. You just change into the directory:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cd packages &amp;amp;&amp;amp; mkdir my-monorepo-ui-lib &amp;amp;&amp;amp; cd my-monorepo-ui-lib&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;And initialize a package:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;yarn init&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;But with the name &lt;code&gt;@my-org/my-monorepo-ui-lib&lt;/code&gt;. This is using a feature called &lt;em&gt;npm organization scope&lt;/em&gt; and requires you to set up an organization with npmjs.com if you want to publish as the &lt;code&gt;@my-org&lt;/code&gt; organization.&lt;/p&gt;

&lt;p&gt;This is not mandatory, but it shows a source for bugs when we’re developing monorepos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The package name &lt;em&gt;isn’t&lt;/em&gt; always the same as the directory name&lt;/li&gt;
&lt;li&gt;Configuration files and script parameters sometimes need a &lt;em&gt;package name&lt;/em&gt;, sometimes a &lt;em&gt;directory name&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;You can use this syntax even if you never intend to publish&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Quick and dirty package installation
&lt;/h2&gt;

&lt;p&gt;We want to build reusable react components in our UI library, but later our create-react-app package will determine which version of react we will use. That’s why react and react-dom can only be a &lt;code&gt;peerDependency&lt;/code&gt; in the UI library. Storybook is our way to quickly try out our react components, so we’ll add that as a &lt;code&gt;devDependency&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;yarn add react react-dom -P&lt;/code&gt;&lt;br&gt;
&lt;code&gt;yarn add @storybook/react babel-loader -D&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This is how we’ve always been doing it, right? Turns out, now there’s a &lt;code&gt;node_modules&lt;/code&gt; folder in our &lt;em&gt;ui-lib&lt;/em&gt; package, with &lt;code&gt;react&lt;/code&gt;, &lt;code&gt;react-dom&lt;/code&gt; and &lt;code&gt;@storybook&lt;/code&gt; inside. But we wanted to have our packages at the root, so lerna will help us do that from the root package:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cd ../..&lt;/code&gt;&lt;br&gt;
&lt;code&gt;yarn lerna bootstrap&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now there’s a node_modules folder at the &lt;em&gt;root&lt;/em&gt;, containing &lt;code&gt;react&lt;/code&gt;, &lt;code&gt;react-dom&lt;/code&gt; and &lt;code&gt;@storybook&lt;/code&gt;. The &lt;code&gt;node_modules&lt;/code&gt; folder inside our ui-lib package is still there, it contains a &lt;code&gt;.bin&lt;/code&gt;-folder with storybook’s &lt;em&gt;command line (bash/cmd)&lt;/em&gt; scripts for starting and building. All tools executing &lt;em&gt;command line&lt;/em&gt; scripts such as storybook, tsc and create-react-app are not necessarily aware that they’re run in a monorepo, they execute commands on the operating system and are usually built for “normal” npm repos. &lt;/p&gt;
&lt;h2&gt;
  
  
  Troubleshooting bash and cmd scripts: storybook, tsc, react-scripts
&lt;/h2&gt;

&lt;p&gt;Inside ui-lib, if we try to run&lt;/p&gt;

&lt;p&gt;&lt;code&gt;yarn start-storybook&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;it will execute the script but tell us that we have no storybook configuration file yet:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Create a storybook config file in "./.storybook/config.{ext}&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We get the same error if we add it as a script in ui-lib’s &lt;code&gt;package.json&lt;/code&gt; (naturally):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"story"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"start-storybook"&lt;/span&gt;&lt;span class="w"&gt; 
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s fix that error by creating the file &lt;code&gt;packages/my-monorepo-ui-lib/.storybook/config.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;configure&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;@storybook/react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../src&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;story&lt;/span&gt;&lt;span class="se"&gt;\.(&lt;/span&gt;&lt;span class="sr"&gt;ts|tsx&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;$/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;configure&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;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;req&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="kr"&gt;module&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and &lt;code&gt;packages/my-monorepo-ui-lib/src&lt;/code&gt; folder, that can be empty for now. Inside our ui-lib, running&lt;/p&gt;

&lt;p&gt;&lt;code&gt;yarn start-storybook&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;and&lt;/p&gt;

&lt;p&gt;&lt;code&gt;yarn story&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;works fine now, although it’s empty.&lt;/p&gt;

&lt;p&gt;The difference becomes clear once we go to the root and try to run command line scripts from there:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cd ../..&lt;/code&gt;&lt;br&gt;
&lt;code&gt;yarn start-storybook&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;and we have the same error as before. The reason is that the &lt;code&gt;node_modules-folder&lt;/code&gt; at the root also contains the command line script, and tries to look for a storybook config relative to the &lt;em&gt;root&lt;/em&gt; package. Lerna will help us here as well, at the root we can call&lt;/p&gt;

&lt;p&gt;&lt;code&gt;yarn lerna run story --stream&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;That command will run ‘story’ relative to all &lt;em&gt;packages&lt;/em&gt; in parallel, and ‘stream’ the script output to the console. This only works for so-called ‘lifecycle scripts’, i.e. scripts defined in one of the sub-packages' &lt;code&gt;package.json&lt;/code&gt;, so the following command will not work:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;yarn lerna run start-storybook&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This is also the reason you’ll see scripts defined such as&lt;/p&gt;

&lt;p&gt;&lt;code&gt;"tsc": "tsc",&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;but it’s generally better to choose a different name to avoid confusion, especially because a lot of people install tsc and other tools globally.&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fow8q7vh3j0wqjwe2ry9w.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fow8q7vh3j0wqjwe2ry9w.png" alt="initial App preview"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  Project Setup: CRA App
&lt;/h1&gt;

&lt;p&gt;Take caution when using CRA for new packages in combination with yarn workspaces:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cd packages&lt;/code&gt;&lt;br&gt;
&lt;code&gt;create-react-app my-monorepo-cra-app&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This will throw an error, since &lt;em&gt;CRA&lt;/em&gt; copies files out of the &lt;code&gt;node_modules&lt;/code&gt; folder where it’s installed in (here: &lt;code&gt;packages/my-monorepo-cra-app/node_modules&lt;/code&gt;), while &lt;em&gt;yarn workspaces&lt;/em&gt; make sure everything gets installed in the root-&lt;code&gt;node_modules&lt;/code&gt;-folder. So in the root &lt;code&gt;package.json&lt;/code&gt; delete&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;"workspaces"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"packages/*"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and add it back in after you’ve run CRA. Then in the root folder run &lt;/p&gt;

&lt;p&gt;&lt;code&gt;yarn lerna bootstrap&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;and your dependencies will neatly be moved to the root-&lt;code&gt;node_modules&lt;/code&gt;. Running&lt;/p&gt;

&lt;p&gt;&lt;code&gt;yarn lerna run start --stream&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;will start your CRA-App, the JavasScript version of it.&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F72pugvruhcbxvlr79oj2.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F72pugvruhcbxvlr79oj2.png" alt="Typescript logo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Adding Typescript
&lt;/h1&gt;

&lt;p&gt;Monorepos can help centralize configuration, so we’ll create a general tsconfig.json at the root of our monorepo. It would be great if we could use that in every subproject, but CRA needs to make some assumptions about its TypeScript setup, so it adds/overwrites the values inside tsconfig. That’s also good news, since it doesn’t just overwrite the file - and we can extend from another tsconfig. In our library project on the other hand we are more free, we can change the webpack there if we have to.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to structure your typescript-configurations
&lt;/h2&gt;

&lt;p&gt;This decision depends on how many packages and what types of typescript-packages you want in your monorepo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One CRA App, one UI library: Go for 

&lt;ul&gt;
&lt;li&gt;one tsconfig.json at the root with cosmetic settings like &lt;code&gt;removeComments&lt;/code&gt;; settings that don’t conflict with CRA and which aren’t library-specific, like library export&lt;/li&gt;
&lt;li&gt;one extending from that, autogenerated in your CRA package&lt;/li&gt;
&lt;li&gt;Lastly one for your library that sets &lt;code&gt;“outDir”:”lib”&lt;/code&gt; and configures declaration export. This needs to correspond with the settings in the lib’s &lt;code&gt;package.json&lt;/code&gt;:
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./lib/index.js"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"types"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./lib/index.d.ts"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Many CRA Apps: Same structure as the one above. The reason is, that right now using CRA means that you’ll have to recompile your library to make changes in your CRA App. When running &lt;code&gt;react-scripts start&lt;/code&gt; though, the &lt;code&gt;node_modules&lt;/code&gt;-folder is also being watched, so you can run &lt;code&gt;tsc&lt;/code&gt; in your library in watch mode after starting CRA&lt;/li&gt;
&lt;li&gt;Many libraries: Create an additional &lt;code&gt;tsconfig.lib.json&lt;/code&gt; at the root, where you generalize your export settings. If one of your libraries depends on another one of your libraries, have a look at typescripts &lt;a href="https://www.typescriptlang.org/docs/handbook/module-resolution.html#path-mapping" rel="noopener noreferrer"&gt;path-mapping&lt;/a&gt; and &lt;a href="https://www.typescriptlang.org/docs/handbook/project-references.html" rel="noopener noreferrer"&gt;project references&lt;/a&gt; features&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Apart from typescript, create-react-app supports css, scss and json-imports out of the box with just a little bit of configuration. We’ll add a &lt;code&gt;typings.d.ts&lt;/code&gt;-file at the root for those types, so those file types are importable by default:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;declare&lt;/span&gt; &lt;span class="kr"&gt;module&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;*.json&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;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kr"&gt;declare&lt;/span&gt; &lt;span class="kr"&gt;module&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*.scss&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;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kr"&gt;declare&lt;/span&gt; &lt;span class="kr"&gt;module&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;IClassNames&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="kr"&gt;string&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;classNames&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IClassNames&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;classNames&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 the minimal tsconfig.json we could work with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"exclude"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"node_modules"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"files"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"./typings.d.ts"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"compilerOptions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"jsx"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"react"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"esModuleInterop"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"skipLibCheck"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We want to use typescript in all our packages, which is done by the &lt;code&gt;lerna add&lt;/code&gt; command:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;yarn lerna add typescript -D&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We include &lt;code&gt;skipLibCheck&lt;/code&gt; as well, because we want tsc to run fast.&lt;/p&gt;

&lt;h1&gt;
  
  
  UI-library with storybook and typescript
&lt;/h1&gt;

&lt;p&gt;When structuring our UI library, it’s good to follow a consistent pattern. The goal is to just run ‘tsc’ and have working Javascript, no webpack needed if we can avoid it by clear structure.&lt;/p&gt;

&lt;p&gt;It’s especially important to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Separate concerns by usage (utils in one folder, React components in another)&lt;/li&gt;
&lt;li&gt;Prevent cyclic imports/exports (utils exported before react components - if you use factories don’t put them in utils, export them after react components)&lt;/li&gt;
&lt;li&gt;Make it easy for the next person to extend the library (group your react component with its story and its unit test)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So your folder structure may end up looking 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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F6al70b6b28rbkh3q3ap1.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F6al70b6b28rbkh3q3ap1.png" alt="index.ts in every folder, *.spec.ts and *.story.ts in component folders"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Any file named &lt;code&gt;index.ts&lt;/code&gt; is either a leaf in the file tree and exports unit-tested code or is a branch and exports its subfolders. Unit-tests and stories are not exported and their files can be excluded from the compiled code via configuration. Here’s an example of what the files may look like:&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fbpv0d35tn6j2cqd1rgml.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fbpv0d35tn6j2cqd1rgml.png" alt="export * from utils; export * from './myuihelper'"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, we do need webpack for one thing: Storybook’s configuration for typescript. And since we’re at it, we can add support for scss and some file types as well.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cd packages/my-monorepo-ui-lib&lt;/code&gt;&lt;br&gt;
&lt;code&gt;yarn add @babel/core @types/storybook__react awesome-typescript-loader babel-loader node-sass sass-loader source-map-loader style-loader -D&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Bootstrapping is not needed because we’re using yarn workspaces, and our packages can be found at the root’s &lt;code&gt;node_modules&lt;/code&gt; folder. &lt;/p&gt;

&lt;p&gt;Directly adding it inside the package is a workaround for an error in &lt;code&gt;lerna add&lt;/code&gt; in combination with organization scopes:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;lerna WARN No packages found where &lt;a class="mentioned-user" href="https://dev.to/babel"&gt;@babel&lt;/a&gt;/core can be added&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The cleaner option would be to use &lt;code&gt;lerna add&lt;/code&gt; with the &lt;code&gt;--scope&lt;/code&gt; parameter, however this has been incompatible with how we set the organisation scope. The command would be:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;yarn lerna add @babel/core @types/storybook__react awesome-typescript-loader babel-loader node-sass sass-loader source-map-loader style-loader --scope=@my-org/my-monorepo-ui-lib -D&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Are you wondering, what the &lt;code&gt;--scope&lt;/code&gt;-parameter is all about?&lt;br&gt;
Here, &lt;code&gt;--scope&lt;/code&gt; is the installation scope parameter, &lt;code&gt;@my-org&lt;/code&gt; the npmjs-organization scope. So all those packages will be added to our UI library package.&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fmzv9m2wkt91dbh1y3mx3.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fmzv9m2wkt91dbh1y3mx3.png" alt="file webpack.config.js inside packages/my-monorepo-ui-lib/.storybook"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our UI lib’s webpack config is comparatively short:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&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="kr"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;module&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
        &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;scss$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;loaders&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;style-loader&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="s2"&gt;css-loader&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="s2"&gt;sass-loader&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="na"&gt;include&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;resolve&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;../&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="na"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;css/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;loaders&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;style-loader&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="s2"&gt;css-loader&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="na"&gt;include&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;resolve&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;../&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="na"&gt;enforce&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pre&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;js$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;source-map-loader&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;exclude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="sr"&gt;/node_modules&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;tsx&lt;/span&gt;&lt;span class="se"&gt;?&lt;/span&gt;&lt;span class="sr"&gt;$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;include&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;resolve&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;../src&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="na"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;awesome-typescript-loader&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="na"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.(&lt;/span&gt;&lt;span class="sr"&gt;woff|woff2|eot|ttf|otf|svg&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;file-loader&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="na"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;extensions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.tsx&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="s2"&gt;.ts&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="s2"&gt;.js&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;And we could use a minimal tsconfig.json that just extends from our root tsconfig.json, and puts the output in the &lt;code&gt;lib&lt;/code&gt;-folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"include"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"src"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"extends"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"../../tsconfig.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"compilerOptions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"outDir"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lib"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"declaration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows us to compile typescript-files and run storybook, but we want to do more! (to do less later on...)&lt;/p&gt;

&lt;p&gt;For our library project, we need to emit declaration files (the files ending in *.d.ts). Otherwise we’ll receive errors such as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Could not find a declaration file for module '@my-org/my-monorepo-ui-lib'. '.../lerna-typescript-cra-uilib-starter/packages/my-monorepo-ui-lib/lib/index.js' implicitly has an 'any' type.&lt;br&gt;
my-monorepo-cra-app:   Try &lt;code&gt;npm install @types/my-org__my-monorepo-ui-lib&lt;/code&gt; if it exists or add a new declaration (.d.ts) file containing &lt;code&gt;declare module '@my-org/my-monorepo-ui-lib';&lt;/code&gt;  TS7016&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For clarification: Webpack isn’t used in our build-process, tsc is. The Webpack we’re configuring is used by storybook.&lt;/p&gt;

&lt;h1&gt;
  
  
  Typescript with CRA
&lt;/h1&gt;

&lt;p&gt;The limits of centralizing our typescript configuration is determined by create-react-app’s use of typescript. At the time of writing this article, switching a CRA App from Javascript to Typescript is done by changing the index.js file to index.tsx and adding all the needed dependencies. Check CRA’s documentation for changes: &lt;a href="https://create-react-app.dev/docs/adding-typescript" rel="noopener noreferrer"&gt;https://create-react-app.dev/docs/adding-typescript&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Inside our CRA-package, we run&lt;/p&gt;

&lt;p&gt;&lt;code&gt;yarn add typescript @types/node @types/react @types/react-dom @types/jest -D&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;then we copy our minimal &lt;code&gt;tsconfig.json&lt;/code&gt; from the ui-lib over to the CRA App package. If we run&lt;/p&gt;

&lt;p&gt;&lt;code&gt;yarn start&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now, CRA’s &lt;code&gt;compilerOptions&lt;/code&gt; will be added to our &lt;code&gt;tsconfig.json&lt;/code&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Loading a component from our UI library
&lt;/h1&gt;

&lt;p&gt;Now it’s time to load our UI library into our CRA App, it will be installed by running:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;yarn lerna add @my-org/my-monorepo-ui-lib&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;But as you might have noticed, we haven’t done much build setup for the library yet. Why didn’t we do that earlier? The reason is pretty simple: CRA, lerna and Storybook are evolving, and so is typescript, npm and even Javascript. And with &lt;em&gt;ES6 modules&lt;/em&gt;, we have a powerful new feature built into the language, replacing earlier module management solutions. The only problem is that it’s not 100% adopted, but as we want to be a good library provider, we offer a fallback. So let’s export our library to ES6 modules - and an “older” module management system. Otherwise we’ll run into errors such as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Unexpected token “export” &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you want to deep-dive into that topic, &lt;a href="https://adrianmejia.com/getting-started-with-node-js-modules-require-exports-imports-npm-and-beyond/" rel="noopener noreferrer"&gt;this blog about nodejs modules and npm&lt;/a&gt; is a good start.&lt;/p&gt;

&lt;p&gt;Npm as our package management solution has also been around since before ES6 and typescript’s rise, so we can set different entry points for our library project inside &lt;code&gt;package.json&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“main” is the oldest one, it’ll point to our pre-ES6 export (“./lib/index.js”)&lt;/li&gt;
&lt;li&gt;“types” is the place where our type declarations can be found ("./lib/index.d.ts")&lt;/li&gt;
&lt;li&gt;“module” is the entrypoint for our ES6 modules ("./lib-esm/index.js")&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Our project is written in typescript from the start, so we’re bundling the declarations with our package. If you’ve seen yourself importing &lt;code&gt;@types&lt;/code&gt;-packages, this is because those projects are written in Javascript at the core, and type definitions have been added later on.&lt;/p&gt;

&lt;p&gt;So we set a &lt;code&gt;tsconfig.esm.json&lt;/code&gt; up to export as an ES6 module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"include"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"src"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"extends"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./tsconfig.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"compilerOptions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"outDir"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lib-esm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"esnext"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"esnext"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"moduleResolution"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"lib"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"dom"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"esnext"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"declaration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This does the following: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Our modules will go into the &lt;code&gt;lib-esm&lt;/code&gt;-folder, which we specified as our &lt;code&gt;module&lt;/code&gt;-entrypoint in &lt;code&gt;package.json&lt;/code&gt;. &lt;/li&gt;
&lt;li&gt;Our module resolution strategy is “node”. If we don’t set it we’ll get an error such as:&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;src/index.ts:1:15 - error TS2307: Cannot find module './utils'.&lt;br&gt;
1 export * from './utils';&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;Setting "esnext" targets latest supported ES proposed features: That means &lt;a href="https://github.com/tc39/proposals" rel="noopener noreferrer"&gt;“features to be developed and eventually included in the standard”&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This way, our library has one export for the latest Javascript features and one that is downwards compatible, so our library can have a bigger range of consumers. Note that for our own final App, CRA uses babel under the hood for compatibility in different browsers.&lt;/p&gt;

&lt;p&gt;We’re already emitting our declarations in the &lt;code&gt;lib&lt;/code&gt;-folder, so we won’t emit them another time here.&lt;/p&gt;

&lt;p&gt;Finally, we’ll add a library-build-script in our library &lt;code&gt;package.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"libbuild"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tsc &amp;amp;&amp;amp; tsc --build tsconfig.esm.json"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we’re ready to add our library package to our CRA package. We can set a wildcard for the package version so that it’s always going to be the latest version.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"dependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@my-org/my-monorepo-ui-lib"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In our CRA App we can now add the component from the library, fully type-checked:&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Febl6vlm4sm8rc8k0pu75.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Febl6vlm4sm8rc8k0pu75.png" alt="&amp;lt;MyNewComponent text="&gt;&lt;/a&gt; in a react render function"/&amp;gt;&lt;/p&gt;

&lt;p&gt;And because monorepos should make our lifes easier, we’ll add scripts in our root-&lt;code&gt;package.json&lt;/code&gt; to start storybook, and execute the library build before starting our CRA app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"story"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lerna run story --stream"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"prestart"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lerna run libbuild --stream"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lerna run start --stream"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will hopefully prevent the most common errors you can run into with this monorepo-setup. If you have additional tips, feel free to add them in the comments!&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>react</category>
      <category>monorepo</category>
      <category>storybook</category>
    </item>
  </channel>
</rss>
