<?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>DEV Community: Andreas Offenhaeuser</title>
    <description>The latest articles on DEV Community by Andreas Offenhaeuser (@anoff).</description>
    <link>https://dev.to/anoff</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%2F10109%2Fef386dc6-bcd9-4a32-8090-600d5a409b0e.png</url>
      <title>DEV Community: Andreas Offenhaeuser</title>
      <link>https://dev.to/anoff</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/anoff"/>
    <language>en</language>
    <item>
      <title>Five major changes when moving to vuetify 2.1</title>
      <dc:creator>Andreas Offenhaeuser</dc:creator>
      <pubDate>Sun, 06 Oct 2019 20:48:33 +0000</pubDate>
      <link>https://dev.to/anoff/migrating-vuetify-1-5-to-2-1-249n</link>
      <guid>https://dev.to/anoff/migrating-vuetify-1-5-to-2-1-249n</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This is a crosspost from my personal blog &lt;a href="https://blog.anoff.io/2019-10-migrating-vuetify-1-to-2/"&gt;blog.anoff.io&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I just migrated the &lt;a href="https://github.com/anoff/devradar/tree/master/editor"&gt;code&lt;/a&gt; for the &lt;a href="https://editor.devradar.io"&gt;devradar editor&lt;/a&gt; to the latest major version of vuetify.&lt;br&gt;
There is an officiel &lt;a href="https://vuetifyjs.com/en/getting-started/releases-and-migrations"&gt;migration guide&lt;/a&gt; that helped me solve 70% of the issues but here is a quick overview of the biggest issues I encountered and what actually changed.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🚨 This was written for a migration from &lt;code&gt;vuetify 1.5.14&lt;/code&gt; to &lt;code&gt;vuetify 2.1.1&lt;/code&gt;&lt;br&gt;
  Also all code samples are typescript, but should also work for Javascript projects&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Bootstrapping
&lt;/h2&gt;

&lt;p&gt;Looking at the new &lt;a href="https://vuetifyjs.com/en/getting-started/quick-start#quick-start"&gt;Quick start section&lt;/a&gt; of vuetify 2.x you will notice that the way vuetify is added to Vue.js has changed.&lt;br&gt;
Previously vuetify was just included via &lt;code&gt;Vue.use()&lt;/code&gt; now it also needs to be instantiated.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;//main.ts (vuetify 1.5)&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Vue&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;vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;App&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;./App.vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;~vuetify/src/stylus/main&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Vuetify&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;vuetify&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nx"&gt;Vue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Vuetify&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;appConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="cm"&gt;/* eslint-disable no-new */&lt;/span&gt;
&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Vue&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;el&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;App/&amp;gt;&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 new setup would look like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// main.ts (vuetify 2)&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Vue&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;vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Vuetify&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;vuetify/lib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;App&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;./App.vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vuetify/dist/vuetify.min.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nx"&gt;Vue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Vuetify&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;vuetifyOpts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;appConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/* eslint-disable no-new */&lt;/span&gt;
&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Vue&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;el&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;App/&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;vuetify&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Vuetify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;vuetifyOpts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&amp;lt;1&amp;gt; Load vuetify&lt;br&gt;
&amp;lt;2&amp;gt; include the &lt;code&gt;css&lt;/code&gt; instead of stylus sheet&lt;br&gt;
&amp;lt;3&amp;gt; Register vuetify with Vue.js&lt;br&gt;
&amp;lt;4&amp;gt; Configure and instantiate vuetify in the new vue instance&lt;/p&gt;
&lt;h2&gt;
  
  
  Loading styles
&lt;/h2&gt;

&lt;p&gt;This was already implicitly shown in the previous section but might be worth another mention.&lt;/p&gt;

&lt;p&gt;Whereas previously the vuetify styles were provided via a &lt;code&gt;styl(us)&lt;/code&gt; file they are now precompiled &lt;code&gt;css&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// vuetify 1.5&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vuetify/src/stylus/main.styl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// vuetify 2.x&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vuetify/dist/vuetify.min.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Also note that the npm module &lt;strong&gt;sass&lt;/strong&gt; is required and &lt;strong&gt;node-sass&lt;/strong&gt; no longer works.&lt;br&gt;
You may need to swap by running&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npm uninstall node-sass
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save-dev&lt;/span&gt; sass
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Adding vuetify types to typescript config
&lt;/h2&gt;

&lt;p&gt;If you get a compile error from typescript stating that &lt;code&gt;Argument of type '{..}' is not assignable to parameter of type 'ComponentOptions&amp;lt;..&amp;gt;'&lt;/code&gt; or &lt;code&gt;Object literal may only specify known properties, and 'vuetify' does not exist in type&lt;/code&gt; you need to register the vuetify types with typescript.&lt;br&gt;
This was something I had not done before and may only be necessary with the change to vuetify 2.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dQS5a0Cf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/c55xlqzcoyqk0x2sse5l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dQS5a0Cf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/c55xlqzcoyqk0x2sse5l.png" alt="vuetify typescript errors"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// tsconfig.json&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;compilerOptions&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;types&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;webpack-env&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;node&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;vuetify&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&amp;lt;1&amp;gt; add vuetify to the types property&lt;/p&gt;

&lt;h2&gt;
  
  
  Theme options
&lt;/h2&gt;

&lt;p&gt;If you are using a custom theme you might need to adapt to the new object structure that supports &lt;strong&gt;light and dark mode&lt;/strong&gt; for your app.&lt;br&gt;
It is mostly moving the color specification of the theme into another nested layer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gFygSP6c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/2n8nm19xiyxwig906y64.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gFygSP6c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/2n8nm19xiyxwig906y64.png" alt="theme schema"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Add MDI font
&lt;/h2&gt;

&lt;p&gt;Vuetify now uses the material design icons for default icons like the hamburger navigation menu.&lt;br&gt;
Install it as a dev dependency if you have not done so yet.&lt;br&gt;
Alternatively you could configure Vuetify to use another icon font, see the official getting started docs for infos on that.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save-dev&lt;/span&gt; @mdi/font
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then add it to your &lt;code&gt;main.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@mdi/font/css/materialdesignicons.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Component changes
&lt;/h2&gt;

&lt;p&gt;With the above changes your app should build correctly, however there will still be a lot of errors in the browser as many components have breaking changes.&lt;br&gt;
Below are the main changes I had to fix in my &lt;a href="https://devradar.io"&gt;devradar&lt;/a&gt; &lt;a href="https://editor.devradar.io"&gt;editor application&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Application Toolbar
&lt;/h3&gt;

&lt;p&gt;There is a new component &lt;code&gt;v-app-bar&lt;/code&gt; that should be used for application wide navigation toolbars.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// vuetify 1.5&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;toolbar&lt;/span&gt;
    &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="nx"&gt;dense&lt;/span&gt; &lt;span class="nx"&gt;scroll&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;off&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;
    &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;accent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="c1"&gt;// vuetify 2&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;bar&lt;/span&gt;
  &lt;span class="nx"&gt;scroll&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;off&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;
  &lt;span class="nx"&gt;dense&lt;/span&gt;
  &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;accent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  List view
&lt;/h3&gt;

&lt;p&gt;All components in the list category have been renamed from &lt;code&gt;list-tile-xyz&lt;/code&gt; to &lt;code&gt;list-item-xyz&lt;/code&gt;.&lt;br&gt;
Best just run a replace all operation and see if it broke anything 😉&lt;/p&gt;

&lt;h2&gt;
  
  
  Done
&lt;/h2&gt;

&lt;p&gt;These changes made my application compile and render the home app component without issues.&lt;br&gt;
Various components changed and you may need to consult the migration docs for specific cases -- or just look at the new API docs directly as they are way more detailed.&lt;/p&gt;

&lt;p&gt;If you stumbled upon this post, I hope it helped you. If it did not I would love to hear what you are missing in the comments or via &lt;a href="https://twitter.com/anoff_io"&gt;Twitter DM&lt;/a&gt; 👋&lt;/p&gt;

</description>
      <category>vue</category>
      <category>typescript</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Preview AsciiDoc with embedded PlantUML in VS Code</title>
      <dc:creator>Andreas Offenhaeuser</dc:creator>
      <pubDate>Sat, 11 May 2019 16:55:18 +0000</pubDate>
      <link>https://dev.to/anoff/preview-asciidoc-with-embedded-plantuml-in-vs-code-3e5j</link>
      <guid>https://dev.to/anoff/preview-asciidoc-with-embedded-plantuml-in-vs-code-3e5j</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Crosspost from &lt;a href="https://blog.anoff.io/2019-05-08-asciidoc-plantuml-vscode/" rel="noopener noreferrer"&gt;blog.anoff.io/2019-05-08-asciidoc-plantuml-vscode&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&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%2Fc9yxykv9cpvel6t4rc2t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc9yxykv9cpvel6t4rc2t.png" alt="logo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This post is for everyone that likes to write AsciiDoc in VS Code but also wants to inline PlantUML diagrams within their docs. In a previous post about &lt;a href="https://blog.anoff.io/2018-07-31-diagrams-with-plantuml/" rel="noopener noreferrer"&gt;diagrams with PlantUML&lt;/a&gt; I gave an intro into PlantUML and how to preview images in VS Code. With the latest release of the asciidoctor plugin for VS Code it is possible to easily preview embedded PlantUML images within AsciiDocs.&lt;/p&gt;

&lt;p&gt;Follow me on Twitter at &lt;a href="https://twitter.com/an0xff" rel="noopener noreferrer"&gt;@an0xff&lt;/a&gt; for future blog updates, not all of them make it to dev.to. Currently I am writing mostly about docs-as-code.&lt;/p&gt;

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

&lt;p&gt;You should already have &lt;a href="https://code.visualstudio.com/docs/setup/setup-overview" rel="noopener noreferrer"&gt;Visual Studio Code installed&lt;/a&gt; on your machine. At the time of writing this post I am using v1.33.1 on MacOS and also verified the setup on a Windows 10 machine.&lt;/p&gt;

&lt;p&gt;For the AsciiDoc preview to work we will use the &lt;a href="https://marketplace.visualstudio.com/items?itemName=joaompinto.asciidoctor-vscode" rel="noopener noreferrer"&gt;AsciiDoc extension&lt;/a&gt; that you can get by executing&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

code &lt;span class="nt"&gt;--install-extension&lt;/span&gt; joaompinto.asciidoctor-vscode


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 The feature we are going to use here is rather new and shipped with 2.6.0 of the AsciiDoc plugin.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The third thing you need is a PlantUML server. There are multiple options:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;use the public &lt;a href="http://plantuml.com/plantuml" rel="noopener noreferrer"&gt;plantuml.com/plantuml&lt;/a&gt; server&lt;/li&gt;
&lt;li&gt;deploy your own java &lt;a href="https://github.com/plantuml/plantuml-server" rel="noopener noreferrer"&gt;plantuml-server&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;run &lt;a href="https://hub.docker.com/r/plantuml/plantuml-server/" rel="noopener noreferrer"&gt;plantuml/plantuml-server&lt;/a&gt; docker container on your local machine&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For test cases option 1 works fine; even if the server claims it does not store any data I would advise you to host your own server if you are working on anything professionally that is not open source. Setting up a PlantUML server is rather easy if you are familiar with Docker, you can see an example setup in &lt;a href="https://blog.anoff.io/2019-03-24-self-hosted-gitea-drone/" rel="noopener noreferrer"&gt;my blog post from march 2019&lt;/a&gt;. Finally the third option of running it locally within docker is great if you are on the road or sitting somewhere without WiFi.&lt;/p&gt;

&lt;p&gt;This post will use option 1 as it just works out of the box while following these instructions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring the extension
&lt;/h2&gt;

&lt;p&gt;The option we will use for this feature is &lt;code&gt;asciidoc.preview.attributes&lt;/code&gt; that allows you to set arbitrary AsciiDoc attributes. These attributes will be injected into the preview. You could also set the attribute manually on each file but that is really something you do not want to do for generic configs like a server URL. Build systems in the AsciiDoc ecosystem like &lt;a href="https://antora.org/" rel="noopener noreferrer"&gt;Antora&lt;/a&gt; allow you to set attributes during the build process (see &lt;a href="https://github.com/anoff/antora-arc42/blob/master/playbook-remote.yml#L21" rel="noopener noreferrer"&gt;this example&lt;/a&gt;), so having a local editor that also injects these attributes is super handy.&lt;/p&gt;

&lt;p&gt;Under the hood the AsciiDoc VS Code extension relies on the javascript port of asciidoctor and the &lt;a href="https://github.com/eshepelyuk/asciidoctor-plantuml.js" rel="noopener noreferrer"&gt;asciidoctor-plantuml.js&lt;/a&gt; extension. This extension needs the &lt;code&gt;:plantuml-server-url:&lt;/code&gt; attribute to be set in the AsciiDoc document to become active and parse PlantUML blocks.&lt;/p&gt;

&lt;p&gt;So all you need to do in VS Code is to hop into your user settings and add the following entry&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;asciidoc.preview.attributes&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;plantuml-server-url&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;http://plantuml.com/plantuml&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;blockquote&gt;
&lt;p&gt;⚠️ The downside of using the public server is that it does not offer SSL encrypted endpoints and you must weaken your VS Code security settings to preview correctly.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The PlantUML images are served over &lt;code&gt;http://&lt;/code&gt; and you must allow your preview to include data from unsafe sources. To do this open your command palette (⌘+P, ctrl+P) and enter asciidoc preview security and choose &lt;em&gt;Allow insecure content&lt;/em&gt;. In case you are running a local PlantUML server you may choose Allow insecure local content.&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%2F1xoujtctyawqifb1wxjz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1xoujtctyawqifb1wxjz.png" alt="screeenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Figure 1. opening asciidoc preview security settings&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%2Fp0ng4u23r9rmgmr8ukzm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp0ng4u23r9rmgmr8ukzm.png" alt="screeenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Figure 2. allow insecure content&lt;/p&gt;

&lt;h2&gt;
  
  
  Live Preview AsciiDoc with embedded PlantUML
&lt;/h2&gt;

&lt;p&gt;To test it out just create an example file with some PlantUML content.&lt;br&gt;
puml&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%2Fblog.anoff.io%2Fassets%2Fasciidoc-plantuml%2Fpuml.svg" 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%2Fblog.anoff.io%2Fassets%2Fasciidoc-plantuml%2Fpuml.svg" alt="plantUML image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Figure 3. This image is rendered on the fly&lt;/p&gt;

&lt;p&gt;With the attribute set correctly the above code block renders as an image&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%2Fs5hzhslclyrbvc1uucie.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs5hzhslclyrbvc1uucie.png" alt="preview showing a diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;..without the attribute set or issues with the security settings you just see a code block&lt;/p&gt;

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

&lt;p&gt;Hope this post helped you. If you have any questions or know of better/alternative ways leave a comment 👋&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>tutorial</category>
      <category>documentation</category>
      <category>webdev</category>
    </item>
    <item>
      <title>What I learned from: How Google Works</title>
      <dc:creator>Andreas Offenhaeuser</dc:creator>
      <pubDate>Thu, 31 Jan 2019 08:00:00 +0000</pubDate>
      <link>https://dev.to/anoff/what-i-learned-from-how-google-works-pf2</link>
      <guid>https://dev.to/anoff/what-i-learned-from-how-google-works-pf2</guid>
      <description>&lt;p&gt;🤞 Cross posted from &lt;a href="https://blog.anoff.io/2019-01-31-book-google-schmidt/" rel="noopener noreferrer"&gt;https://blog.anoff.io/2019-01-31-book-google-schmidt/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As &lt;a href="https://blog.anoff.io/2019-01-15-book-2018-in-books" rel="noopener noreferrer"&gt;promised&lt;/a&gt; my book series will start with two books out of the &lt;a href="https://en.wikipedia.org/wiki/Facebook,_Apple,_Amazon,_Netflix_and_Google" rel="noopener noreferrer"&gt;FAANG&lt;/a&gt; league. The book &lt;em&gt;How Google works&lt;/em&gt; covers many aspects of the companies culture. The book has lots of management insights that - at the time of reading - were of little interest to me. Therefore I want to briefly highlight a few points about innovation that I find fascinating and then move to the human factors like recruiting and competence management that are also covered extensively in the book.&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%2Fblog.anoff.io%2Fimg%2Fassets%2Fbook-google%2Fcover.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%2Fblog.anoff.io%2Fimg%2Fassets%2Fbook-google%2Fcover.jpg" alt="How Google Works, Cover"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Innovation
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.anoff.io%2Fimg%2Fassets%2Fbook-google%2Fsharon-pittaway-117630-unsplash.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%2Fblog.anoff.io%2Fimg%2Fassets%2Fbook-google%2Fsharon-pittaway-117630-unsplash.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Photo by Sharon Pittaway on Unsplash&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The book makes some clear statements about what innovation really means for Google and might help you differentiate between bullshit bingo and real innovation:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Giving customers what they want is &lt;strong&gt;not innovative&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Relates to the Henry Ford (&lt;a href="https://hbr.org/2011/08/henry-ford-never-said-the-fast" rel="noopener noreferrer"&gt;or not&lt;/a&gt;) quote &lt;code&gt;“If I had asked people what they wanted, they would have said faster horses.”&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Instead surprise customers and provide something fundamentally useful they did not have before&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Multiple small non-innovative steps combined can lead to innovative products

&lt;ul&gt;
&lt;li&gt;One example given here is Google Mail that was not an innovation per se but the combination of cloud storage, web applications, artificial intelligence created a product that entered an existing market and disrupted it&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Innovators do not need to be told that they should be innovative

&lt;ul&gt;
&lt;li&gt;Innovation is not learned it is a personality trait&lt;/li&gt;
&lt;li&gt;You can foster innovation but you can not learn or &lt;em&gt;command&lt;/em&gt; it&lt;/li&gt;
&lt;li&gt;To achieve true innovation give innovators freedom to operate (time, resources, trust)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Large corporations attract &lt;strong&gt;risk-avoiding employees&lt;/strong&gt; that often tend to hinder innovation

&lt;ul&gt;
&lt;li&gt;Employees that seek a safe place hardly embrace change and even less actively bring it into an organization&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Management
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.anoff.io%2Fimg%2Fassets%2Fbook-google%2Frawpixel-743066-unsplash.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%2Fblog.anoff.io%2Fimg%2Fassets%2Fbook-google%2Frawpixel-743066-unsplash.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Photo by rawpixel on Unsplash&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As mentioned before there are a lot of management aspects covered in this book that did not make it into my personal notes. I might have to read those up again at some point. One deduction that stuck with me is about processes in an innovative environment:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;"If everything seems under control, you're just not going fast enough."&lt;/code&gt; (&lt;a href="https://en.wikipedia.org/wiki/Mario_Andretti" rel="noopener noreferrer"&gt;Mario Andretti&lt;/a&gt;, Race car driver)

&lt;ul&gt;
&lt;li&gt;In this context: If you do not experience any problems you are not innovating as much as you could&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Processes are slower than reality&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;YES, YES, YES&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Chaos is the normal not an exception

&lt;ul&gt;
&lt;li&gt;In an innovative environment what you consider realistic might change any day&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Order and processes can not be enforced

&lt;ul&gt;
&lt;li&gt;Even in innovative environments you need to establish some &lt;em&gt;rules&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;These rules need to be decided and implemented by everyone involved from top management to junior employee&lt;/li&gt;
&lt;li&gt;To have this open discussion and decision making you need to have a culture of &lt;strong&gt;openness, transparency, meritocracy and honesty&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Recruiting
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.anoff.io%2Fimg%2Fassets%2Fbook-google%2Fryoji-iwata-697773-unsplash.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%2Fblog.anoff.io%2Fimg%2Fassets%2Fbook-google%2Fryoji-iwata-697773-unsplash.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Photo by Ryoji Iwata on Unsplash&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A term that keeps reappearing in the book is &lt;strong&gt;the smart creative&lt;/strong&gt; that is described a person that belongs to&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"[..] the ambitious ones &lt;strong&gt;of all ages&lt;/strong&gt; who are eager (and able) to use the tools of technology to do a lot more. Their common characteristic is that they work hard and are &lt;strong&gt;willing to question the status quo&lt;/strong&gt; and attack things differently. This is why they can have such an impact.&lt;br&gt;
It is also why they are uniquely difficult to manage, especially under old school models, because no matter how hard you try, you &lt;strong&gt;can’t tell people like that how to think&lt;/strong&gt;. If you can’t tell someone how to think, then you have to learn to manage the environment where they think and to make it a place where they want to come every day."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This definition resonates so well with me because after reading it I realized that all the people I look up to within my personal network are not willing to accept the status quo and keep asking &lt;em&gt;Why do we need to do it this way?&lt;/em&gt;. I fought many battles myself, often I was close to giving up but in the end being persistent in your will to change something you will most likely succeed. Too often things are done in ways that are based on assumptions that are no longer valid or might not be applicable for you. Keep in mind to &lt;strong&gt;stay constructive&lt;/strong&gt; when going up against established rules though - try to understand their reasoning and explain why you think it may not apply to your cause. Playing naive also helps 😉&lt;/p&gt;

&lt;p&gt;The main points that the book makes about hiring such smart creatives are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Hire people that are life long learners

&lt;ul&gt;
&lt;li&gt;People you do not have to train for the new upcoming technology but those that want to bring it in and learn it out of curiosity&lt;/li&gt;
&lt;li&gt;One point I take away from this is to read CVs more carefully regarding things like off-work trainings, pet projects etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Generalists over experts

&lt;ul&gt;
&lt;li&gt;The reasoning is that IT changes so quickly it is likely the field you are an expert in now may no longer be relevant to the industry, your product or your company in the future. It is more important to excel at learning new concepts than to already master them.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Hiring (only) people you want to have as friends makes a bad team

&lt;ul&gt;
&lt;li&gt;This aspect might be controversial but given the reasoning I think it makes sense - and is &lt;em&gt;too often&lt;/em&gt; ignored&lt;/li&gt;
&lt;li&gt;People you like &lt;em&gt;as friends&lt;/em&gt; share your interests and your views on things&lt;/li&gt;
&lt;li&gt;This results in little diversity in opinions and paths of reasoning&lt;/li&gt;
&lt;li&gt;Without diversity you will never have &lt;em&gt;all possible options&lt;/em&gt; on the table when you need to make a decision&lt;/li&gt;
&lt;li&gt;This might lead to a sub-optimal decision because the best solution was not considered&lt;/li&gt;
&lt;li&gt;It matters more that you are able to have an interesting (objective) discussion with someone than to like them (LAX rule)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Hiring is every employees responsibility

&lt;ul&gt;
&lt;li&gt;To get the best you need to cast a wide net&lt;/li&gt;
&lt;li&gt;Every employee should be looking for new talent&lt;/li&gt;
&lt;li&gt;Make it part of your yearly employee reviews and bonuses for everyone, not just HR&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Always hire &lt;em&gt;the best&lt;/em&gt; 

&lt;ul&gt;
&lt;li&gt;Train your employees to hire people they look up to&lt;/li&gt;
&lt;li&gt;Once you start hiring someone you consider &lt;em&gt;only good&lt;/em&gt; and they themselves start hiring the collective skill will spiral downwards&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never&lt;/strong&gt; settle for good&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Competence Management
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.anoff.io%2Fimg%2Fassets%2Fbook-google%2Fhello-i-m-nik-687249-unsplash.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%2Fblog.anoff.io%2Fimg%2Fassets%2Fbook-google%2Fhello-i-m-nik-687249-unsplash.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Photo by Hello I'm Nik on Unsplash&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One point from the recruiting category also has a big impact on the competence management of employees. &lt;strong&gt;Life long learning&lt;/strong&gt; is something that needs to be enabled, supported and fostered by the organization. If you hire &lt;em&gt;smart creatives&lt;/em&gt; they will leave as soon as they realize there is nothing new to learn or - even worse - that learning new things might be frowned upon as &lt;em&gt;wasting time&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Another point made in the book that I find extremely tempting to implement in my own environment is the concept of &lt;strong&gt;snippets&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Every&lt;/strong&gt; employee writes a short weekly summary of what they were working on

&lt;ul&gt;
&lt;li&gt;Project managers might write about customers they talked about&lt;/li&gt;
&lt;li&gt;Developers write about tools they learned or bugs they found&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;0.5 - 1 page per week, bullet points&lt;/li&gt;
&lt;li&gt;Share it openly with everyone in your company

&lt;ul&gt;
&lt;li&gt;The organization needs to provide a central infrastructure where people can store their snippets&lt;/li&gt;
&lt;li&gt;Do not mail it to your manager&lt;/li&gt;
&lt;li&gt;Snippets need to be searchable by fulltext and person&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;If you are interested in a persons work, just look up their past snippets to see what they are working on exactly&lt;/li&gt;
&lt;li&gt;If you are looking for someone that might help you with &lt;em&gt;XY&lt;/em&gt; just search the snippets for it&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I think the act of writing these snippets is very little overhead to ones daily work if you get used to it but it can be such a powerful tool to boost network opportunities and knowledge exchange.&lt;/p&gt;




&lt;p&gt;Let me know if you liked this article or which parts you find especially fascinating by leaving a comment below, on &lt;a href="https://twitter.com/an0xff" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; or message me on &lt;a href="https://linkedin.com/in/offenhaeuser" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Stay tuned for the best parts of &lt;code&gt;Powerful&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;👋  Andreas&lt;/p&gt;

</description>
      <category>career</category>
      <category>discuss</category>
      <category>books</category>
    </item>
    <item>
      <title>Get the most out of hackathons</title>
      <dc:creator>Andreas Offenhaeuser</dc:creator>
      <pubDate>Sun, 09 Dec 2018 20:32:25 +0000</pubDate>
      <link>https://dev.to/anoff/get-the-most-out-of-hackathons-263f</link>
      <guid>https://dev.to/anoff/get-the-most-out-of-hackathons-263f</guid>
      <description>&lt;p&gt;Cross posted from &lt;a href="https://anoff.io/blog/2018-12-09-stuttgart-hackathon/" rel="noopener noreferrer"&gt;https://anoff.io/blog/2018-12-09-stuttgart-hackathon/&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;TL;DR; &lt;strong&gt;Network&lt;/strong&gt; and play with expensive toys&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Intro
&lt;/h1&gt;

&lt;p&gt;End of October I joined the &lt;a href="https://www.hackathon-stuttgart.de/" rel="noopener noreferrer"&gt;Stuttgart 🇩🇪 Hackathon&lt;/a&gt; for the second time. It was my overall 4th public Hackathon. In this blog post I want to tell you why I enjoy doing Hackathons and why you should join one too if you have the chance!&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%2Fanoff.io%2Fblog%2Fimg%2Fassets%2Fhackathons%2Fstuttgart2018.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%2Fanoff.io%2Fblog%2Fimg%2Fassets%2Fhackathons%2Fstuttgart2018.jpg" alt="Stuttgart Hackathon 2018"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  How I used to approach Hackathons
&lt;/h1&gt;

&lt;p&gt;The first Hackathon I ever participated in was hosted by Zeiss April 2017. Together with a &lt;a href="//github.com/timgrossmann"&gt;Tim Großmann&lt;/a&gt; I formed a team to compete in Munich for ideas and projects around Digitalization. We did not join to win but mostly to have fun and write a bit of code - so on our way to Munich we were discussing what would be fun to do. Not knowing the exact scope of the Hackathon we thought doing something with augmented reality on web apps would be fun. We also did not manage to get our hands any of the gadgets we wanted to include in our solution therefore we had to go with a pure code solution.&lt;/p&gt;

&lt;p&gt;So we built &lt;a href="https://github.com/anoff/microllaborators" rel="noopener noreferrer"&gt;microllaborators&lt;/a&gt; - an awful play of words with microscope and collaborators. We discussed the idea with the coaches and also came up with a pretty solid pitch with a working live demo including the audience. I still like the solution we built but sadly we only made 5th place 😢&lt;/p&gt;

&lt;p&gt;Last years Stuttgart Hackathon started off completely different. I did not join with a team but participated in the on site team building event to find new people to code with. Having found a group of four something simliar to the Zeisshack happened; &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;we did not get the gadgets we needed to implement the idea we came up with&lt;/p&gt;
&lt;/blockquote&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%2Fanoff.io%2Fblog%2Fimg%2Fassets%2Fhackathons%2FTokyo_Akihabara_gadgets.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%2Fanoff.io%2Fblog%2Fimg%2Fassets%2Fhackathons%2FTokyo_Akihabara_gadgets.jpg" alt="Gadgets"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So we fiddled around for 3 hours with the stuff we got + what we brought from our personal stashes and then decided we would not try to compete at all. Instead we spent another 10 hours teaching each other about languages, tools and programs that each of us used and left the Hackathon after 2/3.&lt;/p&gt;

&lt;h1&gt;
  
  
  What changed
&lt;/h1&gt;

&lt;p&gt;In the past I mostly joined Hackathons to gather experience with new languages, frameworks or gadgets like Alexa, Hue etc. &lt;/p&gt;

&lt;p&gt;By now I go there for two main reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;networking&lt;/li&gt;
&lt;li&gt;play with things I would never get my hands on otherwise&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As I also do a lot of side projects at home I want Hackathons to give me something that my personal projects do not. Therefore I do not want to build a fancy blockchain based, AWS hosted IoT solution. I can do that at home for very little money 💸.&lt;br&gt;
What I can not do at home is meet awesome people that hang out at such events or get my hands on some prototypes or industrial equipment worth several thousand Euros 💰.&lt;/p&gt;

&lt;h1&gt;
  
  
  Stuttgart Hackathon 2018
&lt;/h1&gt;

&lt;p&gt;At this years Stuttgart Hackathon my team did exactly this. We all joined with the clear intention to &lt;em&gt;not compete to win&lt;/em&gt;. If we end up with a solution that would be pitch worthy we agreed we should pitch it. But we did not make it our primary goal. We wanted to have fun - and we got our hands on one of these badasses: The Festo &lt;a href="https://www.festo.com/group/en/cms/12746.htm" rel="noopener noreferrer"&gt;Bionic Cobot&lt;/a&gt; a humanoid robot arm powered by air pressure instead of electrical motors.&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%2Fanoff.io%2Fblog%2Fimg%2Fassets%2Fhackathons%2Fbioniccobot.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%2Fanoff.io%2Fblog%2Fimg%2Fassets%2Fhackathons%2Fbioniccobot.jpg" alt="Festo Bionic Cobot, copyright by Festo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After a few hours of fun with the robot we eventually thought about an actual project that would give the robot a purpose. Thus &lt;a href="https://github.com/anoff/ric/" rel="noopener noreferrer"&gt;R.I.C the robot interaction companion&lt;/a&gt; was born. Using this prototype robot was really different from any side project I have done so far because it meant not only using a single gadget but understanding the complex system. The following diagram shows the system setup where the gray &lt;code&gt;Client Code&lt;/code&gt; is the part that we coded ourself to control the robot. The great thing was that several experts were on site to help us with understanding and customizing the individual components of the Cobot.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://camo.githubusercontent.com/d894c6fa7acc16193425edf1b75cf1bc400fa265/687474703a2f2f7777772e706c616e74756d6c2e636f6d2f706c616e74756d6c2f70726f78793f63616368653d6e6f267372633d68747470733a2f2f7261772e6769746875622e636f6d2f616e6f66662f7269632f6d61737465722f6173736574732f73797374656d2e69756d6c" class="article-body-image-wrapper"&gt;&lt;img src="https://camo.githubusercontent.com/d894c6fa7acc16193425edf1b75cf1bc400fa265/687474703a2f2f7777772e706c616e74756d6c2e636f6d2f706c616e74756d6c2f70726f78793f63616368653d6e6f267372633d68747470733a2f2f7261772e6769746875622e636f6d2f616e6f66662f7269632f6d61737465722f6173736574732f73797374656d2e69756d6c" alt="Cobot system"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We even managed to integrate a bunch of gadgets like LiDAR scanners and Echo dots with Alexa integration. We came up with a funny story to pitch and our presenter did an awesome job at the final presentations.&lt;/p&gt;

&lt;h1&gt;
  
  
  My future Hackathon strategy
&lt;/h1&gt;

&lt;p&gt;In the future I will keep attending hackathons with the goal of getting my hands on non consumer grade hardware and &lt;strong&gt;networking&lt;/strong&gt;. If you are a student you might be tempted by the prizes available at Hackathons but I seriously encourage you to use the time to meet new people and exchange ideas and experiences with others - this is the most valuable thing you can get out of such events.&lt;/p&gt;

&lt;p&gt;Drop me a message on &lt;a href="https://twitter.com/an0xff" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; or leave a comment. Would love to hear your thoughts on Hackathons! 💬&lt;/p&gt;

&lt;h2&gt;
  
  
  Image sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Stuttgart Hackathon - my own&lt;/li&gt;
&lt;li&gt;Gadgets - &lt;a href="https://de.wikipedia.org/wiki/Datei:Tokyo_Akihabara_gadgets.jpg" rel="noopener noreferrer"&gt;Wikipedia&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Cobot - &lt;a href="https://www.festo.com/group/en/repo/assets/00393-bioniccobot-1532x900px.jpg" rel="noopener noreferrer"&gt;Festo&lt;/a&gt; found on their &lt;a href="https://www.festo.com/group/en/cms/12746.htm" rel="noopener noreferrer"&gt;website&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>career</category>
      <category>discuss</category>
    </item>
    <item>
      <title>RBAC with Google Firestore</title>
      <dc:creator>Andreas Offenhaeuser</dc:creator>
      <pubDate>Sun, 12 Aug 2018 07:00:00 +0000</pubDate>
      <link>https://dev.to/anoff/rbac-with-google-firestore-jnc</link>
      <guid>https://dev.to/anoff/rbac-with-google-firestore-jnc</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tepJeTWr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://anoff.io/blog/img/assets/rbac-firestore/logo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tepJeTWr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://anoff.io/blog/img/assets/rbac-firestore/logo.png" alt="Zombies not allowed"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This post will explain how to implement role based access control (&lt;a href="https://en.wikipedia.org/wiki/Role-based_access_control"&gt;RBAC&lt;/a&gt;) using the Google Firestore serverless database. Firebase and Firestore in particular with the concept presented in this post offers the most seamless integration of serverless infrastructure with a mobile client at this point. It has become my go to backend for all web apps I build.&lt;/p&gt;

&lt;h2&gt;
  
  
  Firestore basics
&lt;/h2&gt;

&lt;p&gt;Firestore is database that is part of Googles Firebase suite for mobile app development. It is currently in &lt;em&gt;beta&lt;/em&gt; and has the potential to replace the current Firebase Realtime Database due to its superior API and features.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://firebase.google.com/docs/firestore/"&gt;Cloud Firestore&lt;/a&gt; is a flexible, scalable database for mobile, web, and server development from Firebase and Google Cloud Platform&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For those that never used a Firebase database; it is a NoSQL document oriented database. Firestore allows you to nest documents by creating multiple collections inside a document.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GCLGBenI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://anoff.io/blog/img/assets/rbac-firestore/firestore-documents.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GCLGBenI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://anoff.io/blog/img/assets/rbac-firestore/firestore-documents.png" width="640px" alt="Screenshot of a Firestore database with nested collections"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Firebase suite is built for mobile development and provides SDKs for all major languages. JavaScript/Node.js, Swift, Objective C, Android, Java, Python, Ruby, Go. The SDKs allow add, query or delete data as well as other operations required when interacting with a database as a client. One feature I really like is the possiblity to register your client to receive &lt;a href="https://firebase.google.com/docs/firestore/query-data/listen"&gt;updates&lt;/a&gt; automatically. This allows you to build &lt;strong&gt;three way data binding&lt;/strong&gt; in realtime applications easily. This is a feature I used in my &lt;a href="https://github.com/anoff/microllaborators"&gt;first project with Firebase&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In combination with the Firebase &lt;a href="https://firebase.google.com/docs/auth/"&gt;authentication provider&lt;/a&gt; you can limit access the database to people that are logged in. The auth provider also provides an SDK and requires only a few lines of code to implement in a web app.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EvKMmWzm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://anoff.io/blog/img/assets/rbac-firestore/dist/arch.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EvKMmWzm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://anoff.io/blog/img/assets/rbac-firestore/dist/arch.svg" alt="Firebase application design"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Firestore Security Rules
&lt;/h2&gt;

&lt;p&gt;The ability to create a detailed rule set make Firestore enable use cases for a serverless database without any backend code and still keeping data secure. It is also the foundation for building a role based access control.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;All roles and authorization rules will be enforced by the Firestore server&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Security rules are written in a JavaScript-like syntax but have their own methods. First you nest &lt;code&gt;match&lt;/code&gt; operators to specify the document level you want to be affected by this rule. Use &lt;code&gt;{wildcards}&lt;/code&gt; that can later on be referenced in the rule definition. Granting/denying access is done via an &lt;code&gt;allow &amp;lt;method&amp;gt; if&lt;/code&gt; statement that grants access if it returns &lt;code&gt;true&lt;/code&gt; or otherwise blocks the transaction.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;service&lt;/span&gt; &lt;span class="nx"&gt;cloud&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firestore&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;match&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;databases&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="sr"&gt;/documents &lt;/span&gt;&lt;span class="err"&gt;{
&lt;/span&gt;    &lt;span class="nx"&gt;allow&lt;/span&gt; &lt;span class="nx"&gt;read&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;write&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;some_condition&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;allow&lt;/span&gt; &lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="kc"&gt;false&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;There are &lt;strong&gt;five methods&lt;/strong&gt; that can be specified:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;get&lt;/code&gt;: retrieve a single document&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;list&lt;/code&gt;: read an entire collection or perform queries&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;create&lt;/code&gt;: write to non existing documents&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;update&lt;/code&gt;: write to existing documents&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;delete&lt;/code&gt;: remove a document&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The modifying operations 3-5 can be addressed using the &lt;code&gt;write&lt;/code&gt; method instead of specifying them individually, &lt;code&gt;read&lt;/code&gt; applies both &lt;em&gt;get&lt;/em&gt; and &lt;em&gt;list&lt;/em&gt;. If multiple rules match for a request only one needs to resolve to &lt;code&gt;true&lt;/code&gt; for the request to be successful.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// allow anyone to read but only signed in users to create/update; only a specific user can delete&lt;/span&gt;
&lt;span class="nx"&gt;service&lt;/span&gt; &lt;span class="nx"&gt;cloud&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firestore&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;match&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;databases&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="sr"&gt;/documents &lt;/span&gt;&lt;span class="err"&gt;{
&lt;/span&gt;    &lt;span class="nx"&gt;match&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="o"&gt;=**&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;allow&lt;/span&gt; &lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;if&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;allow&lt;/span&gt; &lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;update&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;auth&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="nx"&gt;allow&lt;/span&gt; &lt;span class="na"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uid&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;an0xff&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  RBAC example scenario
&lt;/h2&gt;

&lt;p&gt;We will setup RBAC for a simple content site with &lt;em&gt;posts&lt;/em&gt; that can be commented with the following roles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;admin&lt;/strong&gt;: can assign roles&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;writer&lt;/strong&gt;: can create new posts, can modify its own posts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;editor&lt;/strong&gt;: can edit any post, delete comments&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;user&lt;/strong&gt;: can create and modify its own comments, can modify his user settings&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On the root level of Firestore we add three different collections. One holds the content, one for user details and one that implements the roles per user. The reason to separate role assignments from the user document is to easily allow users to modify their own details without giving them the possibility to grant themselves new roles.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--33EySEIU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://anoff.io/blog/img/assets/rbac-firestore/dist/document-classes.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--33EySEIU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://anoff.io/blog/img/assets/rbac-firestore/dist/document-classes.svg" alt="Class diagram of the database structure"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Required security rules for RBAC
&lt;/h2&gt;

&lt;p&gt;Given the collection setup and the above role definition we can define the rules we need to implement for each collection.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;User&lt;/th&gt;
&lt;th&gt;Roles&lt;/th&gt;
&lt;th&gt;Posts&lt;/th&gt;
&lt;th&gt;Comments&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;anyone&lt;/td&gt;
&lt;td&gt;user (own), admin&lt;/td&gt;
&lt;td&gt;anyone&lt;/td&gt;
&lt;td&gt;anyone&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;list&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;noone&lt;/td&gt;
&lt;td&gt;admin&lt;/td&gt;
&lt;td&gt;anyone&lt;/td&gt;
&lt;td&gt;anyone&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;create&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;noone&lt;/td&gt;
&lt;td&gt;noone&lt;/td&gt;
&lt;td&gt;writer&lt;/td&gt;
&lt;td&gt;user&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;update&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;user (own profile)&lt;/td&gt;
&lt;td&gt;admin&lt;/td&gt;
&lt;td&gt;writer (own), editor&lt;/td&gt;
&lt;td&gt;user (own)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;delete&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;user (own profile)&lt;/td&gt;
&lt;td&gt;noone&lt;/td&gt;
&lt;td&gt;writer (own)&lt;/td&gt;
&lt;td&gt;user (own), editor&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Implementing security rules for RBAC
&lt;/h2&gt;

&lt;p&gt;We start with a default &lt;em&gt;DENY ALL&lt;/em&gt; policy setup for the collections in our project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;service&lt;/span&gt; &lt;span class="nx"&gt;cloud&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firestore&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;match&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;databases&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="sr"&gt;/documents &lt;/span&gt;&lt;span class="err"&gt;{
&lt;/span&gt;    &lt;span class="c1"&gt;// this addresses any entry in the user collection&lt;/span&gt;
    &lt;span class="nx"&gt;match&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;allow&lt;/span&gt; &lt;span class="nx"&gt;read&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;write&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// rules for the roles setup&lt;/span&gt;
    &lt;span class="nx"&gt;match&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;roles&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;allow&lt;/span&gt; &lt;span class="nx"&gt;read&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;write&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;match&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;allow&lt;/span&gt; &lt;span class="nx"&gt;read&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;write&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;match&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="sr"&gt;/comments/&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;allow&lt;/span&gt; &lt;span class="nx"&gt;read&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;write&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  RBAC helper functions
&lt;/h3&gt;

&lt;p&gt;We start by implementing a few custom functions that help us define role based rules.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;service&lt;/span&gt; &lt;span class="nx"&gt;cloud&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firestore&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;match&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;databases&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="sr"&gt;/documents &lt;/span&gt;&lt;span class="err"&gt;{
&lt;/span&gt;    &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="c1"&gt;// the request object contains info about the authentication status of the requesting user&lt;/span&gt;
    &lt;span class="c1"&gt;// if the .auth property is not set, the user is not signed in&lt;/span&gt;
    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;isSignedIn&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;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;auth&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="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// return the current users entry in the roles collection&lt;/span&gt;
    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getRoles&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="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/databases/&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;documents&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;roles&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uid&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="c1"&gt;// check if the current user has a specific role&lt;/span&gt;
    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;hasRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;role&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;isSignedIn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;getRoles&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// check if the user has any of the given roles (list)&lt;/span&gt;
    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;hasAnyRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;roles&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;isSignedIn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;getRoles&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;hasAny&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With these functions in the security rules you can now easily implement security roles based on the users roles. &lt;/p&gt;

&lt;h3&gt;
  
  
  Users collection
&lt;/h3&gt;

&lt;p&gt;First we make sure only the user itself can modify its data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;match&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// anyone can see a specific users profile data (name, email etc), in a real scenario you might want to make this more granular&lt;/span&gt;
  &lt;span class="nx"&gt;allow&lt;/span&gt; &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// noone can query for users&lt;/span&gt;
  &lt;span class="nx"&gt;allow&lt;/span&gt; &lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// users can modify their own data&lt;/span&gt;
  &lt;span class="nx"&gt;allow&lt;/span&gt; &lt;span class="nx"&gt;update&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uid&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Roles collection
&lt;/h3&gt;

&lt;p&gt;Next we enforce the rules for the role collection&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;match&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;roles&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;allow&lt;/span&gt; &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uid&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;hasRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;admin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;allow&lt;/span&gt; &lt;span class="na"&gt;list&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;hasRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;admin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;allow&lt;/span&gt; &lt;span class="na"&gt;update&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;hasRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;admin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;allow&lt;/span&gt; &lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Posts collection
&lt;/h3&gt;

&lt;p&gt;This one is a little trickier because we first need to figure out who actually created the post if we want to enforce the &lt;em&gt;update&lt;/em&gt; rule.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;match&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;allow&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;list&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;if&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;allow&lt;/span&gt; &lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;hasRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;writer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// check if the post author is identical to requesting user&lt;/span&gt;
  &lt;span class="nx"&gt;allow&lt;/span&gt; &lt;span class="na"&gt;update&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hasRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;writer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;hasRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;editor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;allow&lt;/span&gt; &lt;span class="na"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;hasRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;writer&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Comments (Sub)collection
&lt;/h3&gt;

&lt;p&gt;Make sure that users can modify or delete their comments and editors can moderate anyones comments.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;match&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="sr"&gt;/comments/&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;allow&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;list&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;if&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;allow&lt;/span&gt; &lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;hasRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// check if the comment author is identical to requesting user&lt;/span&gt;
  &lt;span class="nx"&gt;allow&lt;/span&gt; &lt;span class="na"&gt;update&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;allow&lt;/span&gt; &lt;span class="na"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;hasRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;editor&lt;/span&gt;&lt;span class="dl"&gt;'&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="nx"&gt;hasRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uid&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;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;You created a Firestore ruleset that enforces &lt;strong&gt;data privacy and integrity&lt;/strong&gt; by limiting access to resources based according to a role design. &lt;br&gt;
To bring RBAC to your application you need to consider a few things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://firebase.google.com/docs/firestore/security/get-started#use_the_firebase_cli"&gt;Deploy&lt;/a&gt; the security rules into your Firebase project, highly recommend using the CLI or Travis&lt;/li&gt;
&lt;li&gt;Implement a Firebase function that creates an entry in the &lt;code&gt;users&lt;/code&gt; and &lt;code&gt;roles&lt;/code&gt; collection for each new user using the &lt;a href="https://firebase.google.com/docs/functions/auth-events"&gt;authentication trigger&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Log in to your app and then manually grant yourself &lt;em&gt;admin&lt;/em&gt; rights via the Firebase console&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To see a similar design implemented you can check out my &lt;a href="https://github.com/anoff/techradar"&gt;techradar&lt;/a&gt; project which also showcases how to implement RBAC on the frontend and setting up Travis CI to deploy Firebase rules. There are additional features available to secure Firestore data for example checking which parts of a resource are being changed in an update process to allow users to update only specific properties. &lt;/p&gt;

&lt;p&gt;Drop me a message on &lt;a href="https://twitter.com/an0xff"&gt;Twitter&lt;/a&gt; if you have any feedback about this post. Also check out my previous post on creating &lt;a href="https://anoff.io/blog/2018-07-31-diagrams-with-plantuml/"&gt;PlantUML diagrams&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>firebase</category>
      <category>serverless</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Markdown native diagrams with PlantUML</title>
      <dc:creator>Andreas Offenhaeuser</dc:creator>
      <pubDate>Tue, 31 Jul 2018 07:00:00 +0000</pubDate>
      <link>https://dev.to/anoff/markdown-native-diagrams-with-plantuml-ik4</link>
      <guid>https://dev.to/anoff/markdown-native-diagrams-with-plantuml-ik4</guid>
      <description>&lt;p&gt;&lt;em&gt;This post was originally published on my blog under &lt;a href="https://anoff.io/blog/2018-07-31-diagrams-with-plantuml/"&gt;https://anoff.io/blog/2018-07-31-diagrams-with-plantuml/&lt;/a&gt; and crossposted for&lt;/em&gt; 💖&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This post will cover PlantUML basics and how it can be used in GitLab or GitHub projects as well as a seamless local development environment using Visual Studio Code.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is a post I have been wanting to write for months. Lately I have been using PlantUML extensively at work but also in my private projects. You can see it being used in my &lt;a href="https://github.com/anoff/plantbuddy#main-features"&gt;plantbuddy&lt;/a&gt; and &lt;a href="https://github.com/anoff/techradar#design"&gt;techradar&lt;/a&gt; projects on GitHub. Using it in different places and for various purposes I came across a bunch of issues that I want to share in this post.&lt;/p&gt;

&lt;h1&gt;
  
  
  PlantUML Basics 👨‍🎨
&lt;/h1&gt;

&lt;p&gt;For those that do not know &lt;a href="http://plantuml.com/"&gt;PlatUML&lt;/a&gt;: It is an open source tool that allows you to define UML diagrams with plain text. There are different &lt;a href="http://plantuml.com/sitemap-language-specification"&gt;diagram types&lt;/a&gt; available being described with custom syntax but following a common scheme. This post will not go into the details of each of those diagram types because the PlantUML website does a pretty good job at describing &lt;a href="http://plantuml.com/sequence-diagram"&gt;sequence&lt;/a&gt;, &lt;a href="http://plantuml.com/component-diagram"&gt;component&lt;/a&gt;, &lt;a href="http://plantuml.com/activity-diagram-beta"&gt;activity&lt;/a&gt; and the other diagram types.&lt;/p&gt;

&lt;p&gt;A basic component diagram showing data flow can be built using the following markup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@startuml component
actor client
node app
database db

db -&amp;gt; app
app -&amp;gt; client
@enduml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Mvpt6QlT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://anoff.io/blog/img/assets/plantuml/diagrams/dist/component.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Mvpt6QlT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://anoff.io/blog/img/assets/plantuml/diagrams/dist/component.svg" alt="basic component diagram showing data flowing from a database via an app to a client"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Reasons to love PlantUML 🤗
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Versioning 📦
&lt;/h3&gt;

&lt;p&gt;A very important aspect for developing software and writing documentation is to keep it in sync. One part is to update documentation if the code itself is updated. Another important part is versioning - usually software is versioned using &lt;code&gt;git&lt;/code&gt; or similar systems. By putting the documentation into the same repository as the code you make sure to always look at the correct state of documentation for a respective point in time.&lt;/p&gt;

&lt;p&gt;For that reason I love putting all my documentation either within the sourcecode as comments or as Markdown files next to the sourcecode. One thing I was always lacking with this approach is visualizing things. Putting PowerPoint/Keynote/Visio/Enterprise Architect.. files into a repository does make sure your diagrams are always versioned with the code - but they are not browsable in Web UIs. Come PlantUML and GitLab rendering to the rescue: GitLab allows you to &lt;a href="https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/administration/integration/plantuml.md"&gt;inline PlantUML diagrams&lt;/a&gt; directly into your Markdown files and they will be rendered on the fly when viewing the files in the browser.&lt;/p&gt;

&lt;p&gt;One other benefit PlantUML has over the mentioned tools is that by defining your diagrams in plain text you make them diff-able in pull requests. Reviewers can always see what changes have been made and easily compare changes to the diagram with changes made inside the code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Syntax 🐟
&lt;/h3&gt;

&lt;p&gt;The basic syntax of PlantUML is very concise and builds a good foundation for the different diagram types. It is also well very smart in the way that it allows diagrams to be written with different flavors e.g. you can declare/instantiate all nodes at the top, but if you do not declare them they will be inferred automatically. Same goes for &lt;a href="http://plantuml.com/preprocessing"&gt;macros and definitions&lt;/a&gt; that allow you to compose larger diagrams or a common library for your team.&lt;/p&gt;

&lt;p&gt;I recently created a &lt;a href="http://anoff.io/blog/img/puml-cheatsheet.pdf"&gt;PlantUML Cheatsheet&lt;/a&gt; for a lot of useful tricks - it does however not cover the very basics of PlantUML syntax. You can browse the &lt;a href="https://github.com/anoff/blog/raw/master/img/assets/plantuml/puml-cheatsheet.pdf"&gt;latest version&lt;/a&gt; or the &lt;a href="https://github.com/anoff/blog/blob/master/img/assets/plantuml/puml-cheatsheet.tex"&gt;LaTeX sourcecode&lt;/a&gt; on GitHub.&lt;/p&gt;

&lt;h3&gt;
  
  
  Layouting 🏗
&lt;/h3&gt;

&lt;p&gt;Compared with WYSIWYG editors PlantUML diagrams only define components and their relationship but not the actual layout of the diagram. Instead the diagram is inferred by a deterministic algorithm in the rendering process. This is beneficial when specifying the diagram because you only focus on the content - comparable to writing a LaTeX document.&lt;br&gt;
Sadly the layouting engine is not as good as you sometimes wish it to be and especially in component diagrams with 10+ nodes you might end up spending a lot of time enforcing specific layouts manually.&lt;/p&gt;

&lt;p&gt;For sequence and activity diagrams the automatic layouting works great even for very large diagrams. After you built a few diagrams and notice how easy it is to just move lines of code up and down and have changes in the code immediately reflect in your documentation you will love the automatic layouting.&lt;/p&gt;
&lt;h3&gt;
  
  
  Share anywhere 📱
&lt;/h3&gt;

&lt;p&gt;If you want to &lt;em&gt;freeze&lt;/em&gt; a diagram version and send it to someone outside your organization you can simply send them an insanely long url (e.g. &lt;a href="http://www.plantuml.com/plantuml/png/5Son3G8n34RXtbFyb1GiG9MM6H25XGsnH9p8SGgslrpxzFILcHovse-yYw8QdlJl2v--N93rJ2Bg4EDlSBlG0pn6wDiu5NiDcAU6piJzTgKN5NNPu040"&gt;http://www.plantuml.com/plantuml/png/5Son3G8n34RXtbFyb1GiG9MM6H25XGsnH9p8SGgslrpxzFILcHovse-yYw8QdlJl2v--N93rJ2Bg4EDlSBlG0pn6wDiu5NiDcAU6piJzTgKN5NNPu040&lt;/a&gt;) that encodes the entire diagram definition. You can also just embed this URL inside an HTML &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag. If anyone ever needs to work with the image all you have to do is swap &lt;code&gt;/plantuml/png&lt;/code&gt; to &lt;code&gt;/plantuml/uml&lt;/code&gt; and you will see the &lt;a href="http://www.plantuml.com/plantuml/uml/5Son3G8n34RXtbFyb1GiG9MM6H25XGsnH9p8SGgslrpxzFILcHovse-yYw8QdlJl2v--N93rJ2Bg4EDlSBlG0pn6wDiu5NiDcAU6piJzTgKN5NNPu040"&gt;definition&lt;/a&gt; of the diagram.&lt;/p&gt;

&lt;p&gt;This gives the entire PlantUML toolstack an extremely versatile way of passing information as well as viewable images.&lt;/p&gt;
&lt;h1&gt;
  
  
  Local development 💻
&lt;/h1&gt;

&lt;p&gt;The fastest, platform agnostic and easiest way to start creating PlantUML diagrams is using their &lt;a href="http://www.plantuml.com/plantuml/uml/SoWkIImgAStDuShBJqbLA4ajBk5oICrB0Oe00000"&gt;online editor&lt;/a&gt; (btw. you can easily host it on prem using the &lt;a href="https://hub.docker.com/r/plantuml/plantuml-server/"&gt;plantuml-server Docker image&lt;/a&gt;). This is fine for creating simple diagrams with a few nodes but larger diagrams require a lot of &lt;em&gt;previewing&lt;/em&gt; which is annoying in the online editor.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you have any other local setups please let me know via &lt;a href="https://twitter.com/an0xff"&gt;Twitter&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Visual Studio Code
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;In case you already use VS Code this is a no brainer to set up. Otherwise you might seriously want to consider using it for the purpose of editing PlantUML diagrams (in Markdown) only because it is a super smooth experience.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;All you need to do is to get the &lt;a href="https://marketplace.visualstudio.com/items?itemName=jebbs.plantuml"&gt;PlantUML extension&lt;/a&gt; to enable codes native &lt;a href="https://code.visualstudio.com/docs/languages/markdown"&gt;Markdown preview feature&lt;/a&gt; to also parse inline diagrams.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ez59kPHX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://anoff.io/blog/img/assets/plantuml/code-rendering.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ez59kPHX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://anoff.io/blog/img/assets/plantuml/code-rendering.png" alt="Screenshot of Visual Studio Code showing rendered PlantUML diagram in Markdown preview"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By default the plugin requires a local PlantUML process to be running and accepting the rendering requests. I recommend switching it to use a server for rendering; this could be the official plantuml.com server, an on premise instance or a locally running &lt;a href="https://hub.docker.com/r/plantuml/plantuml-server/"&gt;container&lt;/a&gt;. After installing the plugin go to the VS Code options (&lt;code&gt;ctrl/⌘ + ,&lt;/code&gt;) and change the &lt;code&gt;plantuml.render&lt;/code&gt; property.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// PlantUMLServer: Render diagrams by server which is specified with "plantuml.server". It's much faster, but requires a server.&lt;/span&gt;
&lt;span class="c1"&gt;// Local is the default configuration.&lt;/span&gt;
&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;plantuml.render&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;PlantUMLServer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

&lt;span class="c1"&gt;// Plantuml server to generate UML diagrams on-the-fly.&lt;/span&gt;
&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;plantuml.server&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;http://www.plantuml.com/plantuml&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 ever go off the grid and still want to work remember to &lt;code&gt;docker run -d -p 8080:8080 plantuml/plantuml-server:jetty&lt;/code&gt; while you still have an internet connection. The image is &lt;code&gt;~250MB&lt;/code&gt; to download. Afterwards set &lt;code&gt;plantuml.server&lt;/code&gt; to &lt;code&gt;http://localhost:8080/&lt;/code&gt; and you're set for an offline adventure.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;On my MacBook I sometimes experience a lot of CPU consumption from the running container - even when not actively rendering. Restarting the container helps 🤷‍&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Render to SVG/PDF
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;This method only works if diagrams are defined explicitly in files and not inlined into Markdown.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To write this blog post and build the &lt;a href=""&gt;Cheatsheet&lt;/a&gt; I played around with non-realtime ways of rendering PlantUML diagrams into images. You can use the &lt;a href="https://github.com/anoff/blog/blob/master/img/assets/plantuml/Makefile"&gt;Makefile&lt;/a&gt; and &lt;a href="https://github.com/anoff/blog/blob/master/img/assets/plantuml/diagrams/convert.sh"&gt;Shell script&lt;/a&gt; to convert an entire &lt;a href="https://github.com/anoff/blog/tree/master/img/assets/plantuml/diagrams"&gt;folder&lt;/a&gt; of PlantUML diagrams with &lt;code&gt;.puml&lt;/code&gt; extension into &lt;code&gt;.svg&lt;/code&gt; and &lt;code&gt;.pdf&lt;/code&gt; &lt;a href="https://github.com/anoff/blog/tree/master/img/assets/plantuml/diagrams/dist"&gt;files&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The script essentially runs the diagram definition through a dockerized PlantUML process which outputs an &lt;code&gt;.svg&lt;/code&gt; and then uses Inkscape to create a &lt;code&gt;.pdf&lt;/code&gt; file for importing it into LaTeX documents for example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;
&lt;span class="c"&gt;# converts all puml files to svg&lt;/span&gt;

&lt;span class="nv"&gt;BASEDIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="nv"&gt;$BASEDIR&lt;/span&gt;/dist
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nv"&gt;$BASEDIR&lt;/span&gt;/dist/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;FILE &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$BASEDIR&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;.puml&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nb"&gt;echo &lt;/span&gt;Converting &lt;span class="nv"&gt;$FILE&lt;/span&gt;..
  &lt;span class="nv"&gt;FILE_SVG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;FILE&lt;/span&gt;&lt;span class="p"&gt;//puml/svg&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
  &lt;span class="nv"&gt;FILE_PDF&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;FILE&lt;/span&gt;&lt;span class="p"&gt;//puml/pdf&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
  &lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="nv"&gt;$FILE&lt;/span&gt; | docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; think/plantuml &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$FILE_SVG&lt;/span&gt;
  docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nv"&gt;$PWD&lt;/span&gt;:/diagrams productionwentdown/ubuntu-inkscape inkscape /diagrams/&lt;span class="nv"&gt;$FILE_SVG&lt;/span&gt; &lt;span class="nt"&gt;--export-area-page&lt;/span&gt; &lt;span class="nt"&gt;--without-gui&lt;/span&gt; &lt;span class="nt"&gt;--export-pdf&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/diagrams/&lt;span class="nv"&gt;$FILE_PDF&lt;/span&gt; &amp;amp;&amp;gt; /dev/null
&lt;span class="k"&gt;done
&lt;/span&gt;&lt;span class="nb"&gt;mv&lt;/span&gt; &lt;span class="nv"&gt;$BASEDIR&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;.svg &lt;span class="nv"&gt;$BASEDIR&lt;/span&gt;/dist/
&lt;span class="nb"&gt;mv&lt;/span&gt; &lt;span class="nv"&gt;$BASEDIR&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;.pdf &lt;span class="nv"&gt;$BASEDIR&lt;/span&gt;/dist/
&lt;span class="nb"&gt;echo &lt;/span&gt;Done
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  GitLab integration
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;This feature is currently only available with on-prem installations of GitLab, enabling it on gitlab.com is &lt;a href="https://gitlab.com/gitlab-com/infrastructure/issues/2163"&gt;an open issue&lt;/a&gt;. See the GitHub integration for a workaround.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Using PlantUML within GitLab is super fun. All you have to do is &lt;a href="https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/administration/integration/plantuml.md"&gt;set up&lt;/a&gt; a render server to use and you can just commit Markdown files with inlined PlantUML diagrams and they will render for everyone visiting the GitLab web UI.&lt;/p&gt;

&lt;p&gt;What's great is that this does not only works in Markdown files committed into a git repository but in all other fields within GitLab that render markdown - virtually everything. You can have small diagrams helping illustrate things in issues as well.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wSWARklz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://anoff.io/blog/img/assets/plantuml/puml-issue.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wSWARklz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://anoff.io/blog/img/assets/plantuml/puml-issue.png" alt="Screenshot of PlantUML syntax in a GitLab issue"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--stOvpDRX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://anoff.io/blog/img/assets/plantuml/puml-issue-rendered.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--stOvpDRX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://anoff.io/blog/img/assets/plantuml/puml-issue-rendered.png" alt="Rendered PlantUML diagram in a GitLab issue"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  GitHub integration
&lt;/h1&gt;

&lt;p&gt;There is no native PlantUML integration for GitHub and gitlab.com available. To maintain the advantages listed above it is obviously not a valid workaround to just render the files locally and commit them into git.&lt;/p&gt;

&lt;p&gt;Instead make use of the PlantUML &lt;a href="http://plantuml.com/server"&gt;proxy service&lt;/a&gt; as described in &lt;a href="https://stackoverflow.com/questions/32203610/how-to-integrate-uml-diagrams-into-gitlab-or-github"&gt;this stackoverflow discussion&lt;/a&gt;. The way this works is that instead of passing the PlantUML server the diagram content within the URL we define a &lt;em&gt;remote URL&lt;/em&gt; where the content can be fetched from e.g. &lt;code&gt;http://www.plantuml.com/plantuml/proxy?src=https://raw.github.com/plantuml/plantuml-server/master/src/main/webapp/resource/test2diagrams.txt&lt;/code&gt;. This URL can be embedded in an HTML &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag or within Markdown image syntax &lt;code&gt;![]()&lt;/code&gt;. To leverage this feature when using GitHub, simply point the &lt;em&gt;remote URL&lt;/em&gt; to a raw link of the PlantUML diagram in your repository.&lt;/p&gt;

&lt;p&gt;You can see this method in action in the &lt;a href="https://github.com/anoff/plantbuddy#main-features"&gt;plantbuddy&lt;/a&gt; and &lt;a href="https://github.com/anoff/techradar#design"&gt;techradar&lt;/a&gt; projects of my GitHub account.&lt;/p&gt;

&lt;p&gt;The following diagram shows what will happen when you open a Markdown page hosted on GitHub that contains such a link:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0-fq-CvJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://anoff.io/blog/img/assets/plantuml/diagrams/dist/plantuml-proxy.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0-fq-CvJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://anoff.io/blog/img/assets/plantuml/diagrams/dist/plantuml-proxy.svg" alt="sequence diagram showing how PlantUML proxy service works"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/anoff/plantbuddy/blame/master/readme.md#L12"&gt;This example&lt;/a&gt; shows that adding a &lt;code&gt;?cache=no&lt;/code&gt; might be a good idea because of GitHubs Camo &lt;a href="http://forum.plantuml.net/7163/githubs-aggressive-caching-prevent-diagrams-updated-markdown"&gt;caching strategy&lt;/a&gt; which will prevent your images from updating if you change the sourcecode.&lt;/p&gt;

&lt;p&gt;The downside of this approach is that it will always render the latest commmit in your repository even if you browse old versions. If browsing old versions is a &lt;em&gt;strong&lt;/em&gt; requirement for you when using an integration with GitHub then you might need to build your own plugin/renderer or optimize the local development environment because after all the correct diagram version will always be with the sourcecode you checked out.&lt;/p&gt;

&lt;p&gt;To use the proxy service integration simply use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;![cached image](http://www.plantuml.com/plantuml/proxy?src=https://raw.github.com/plantuml/plantuml-server/master/src/main/webapp/resource/test2diagrams.txt)

![uncached image](http://www.plantuml.com/plantuml/proxy?cache=no&amp;amp;src=https://raw.github.com/plantuml/plantuml-server/master/src/main/webapp/resource/test2diagrams.txt)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;p&gt;There are two fundamental ways of keeping PlantUML diagrams&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;inline into Markdown&lt;/li&gt;
&lt;li&gt;keep as individual &lt;code&gt;.puml&lt;/code&gt; files&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Depending on your toolstack one of those should be your preferred option to work with diagrams in your repository. It is highly recommend to keep diagrams as close to the code as possible and not create artificial documentation repositories.&lt;/p&gt;

&lt;p&gt;This post covered how to write and render files locally in &lt;strong&gt;VS Code&lt;/strong&gt;, using &lt;strong&gt;Docker&lt;/strong&gt; containers and how to integrate into &lt;strong&gt;GitLab on prem&lt;/strong&gt; as well as publich &lt;strong&gt;GitHub&lt;/strong&gt; and &lt;strong&gt;GitLab&lt;/strong&gt; instances.&lt;/p&gt;

&lt;p&gt;There is a lot more to tell about PlantUML but I hope this article gave you enough infos to get started on whatever platform you are using. I recommend this &lt;a href="http://anoff.io/blog/img/puml-cheatsheet.pdf"&gt;PlantUML Cheatsheet&lt;/a&gt; which will help you to cover an even wider range of use cases.&lt;/p&gt;

&lt;p&gt;Tell me about your experiences with PlantUML or alternative integrations on &lt;a href="https://twitter.com/an0xff"&gt;Twitter 🐦&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>tutorial</category>
      <category>github</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Building realtime apps with Vue and nodeJS</title>
      <dc:creator>Andreas Offenhaeuser</dc:creator>
      <pubDate>Wed, 18 Apr 2018 07:00:00 +0000</pubDate>
      <link>https://dev.to/anoff/building-realtime-apps-with-vue-and-nodejs-33ol</link>
      <guid>https://dev.to/anoff/building-realtime-apps-with-vue-and-nodejs-33ol</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This is a crosspost of my blog at &lt;a href="https://anoff.io/blog/2018-04-18-node-vue-websockets/" rel="noopener noreferrer"&gt;anoff.io/blog&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;Set up a basic Node.js server&lt;/li&gt;
&lt;li&gt;Initialize Vue.js project&lt;/li&gt;
&lt;li&gt;Adding Material Design library&lt;/li&gt;
&lt;li&gt;
Bring in websockets

&lt;ul&gt;
&lt;li&gt;frontend: vue-socket.io&lt;/li&gt;
&lt;li&gt;backend: socket-io / server.js&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Showtime 🍿&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Wanting to build a simple &lt;a href="https://en.wikipedia.org/wiki/Single-page_application" rel="noopener noreferrer"&gt;SPA&lt;/a&gt; as a side project I struggled with things that might annoy a lot of newcomers that do not want to go full vanilla. Which &lt;em&gt;web framework&lt;/em&gt;, which &lt;em&gt;style library&lt;/em&gt;, which &lt;em&gt;server framework&lt;/em&gt; - and more importantly how does it all work together?&lt;/p&gt;

&lt;p&gt;In this post we will put together a bunch of great tools out there to build a realtime web app with a few single lines of code. A quick introduction into the tools that will be used:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://nodejs.org/en/" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt;: Javascript runtime to build server applications&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://vuejs.org/" rel="noopener noreferrer"&gt;Vue.js&lt;/a&gt;: A webapp framework&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://material.io/" rel="noopener noreferrer"&gt;Material Design&lt;/a&gt;: Set of styled web components by Google using the &lt;a href="https://vuematerial.io/" rel="noopener noreferrer"&gt;vue-material&lt;/a&gt; library&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://socket.io/" rel="noopener noreferrer"&gt;socket.io&lt;/a&gt;: Client &amp;amp; server library for websockets&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://serverjs.io/" rel="noopener noreferrer"&gt;servers.js&lt;/a&gt;: An opinionated server framework for Node.js based on &lt;a href="http://expressjs.com/" rel="noopener noreferrer"&gt;express&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Set up a basic Node.js server
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fanoff.io%2Fblog%2Fimg%2Fassets%2Fnode-vue-websockets%2Fhello.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%2Fanoff.io%2Fblog%2Fimg%2Fassets%2Fnode-vue-websockets%2Fhello.png" alt="Hello World Node.js"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first thing we'll do is set up a node server to provide a backend. Using the &lt;a href="https://serverjs.io/" rel="noopener noreferrer"&gt;servers.js&lt;/a&gt; library a basic API service can be build with a few lines of code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# in any empty directory&lt;/span&gt;
npm init &lt;span class="c"&gt;# initialize npm project&lt;/span&gt;
npm &lt;span class="nb"&gt;install &lt;/span&gt;server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Creating a &lt;em&gt;Hello World&lt;/em&gt; example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// server.js&lt;/span&gt;
&lt;span class="c1"&gt;// load the server resource and route GET method&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;server&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;server&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="p"&gt;{&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="p"&gt;}&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;server/router&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// get server port from environment or default to 3000&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;

&lt;span class="nf"&gt;server&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="nf"&gt;get&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="nx"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;h1&amp;gt;Hello you!&amp;lt;/h1&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&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;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="s2"&gt;`Server running at http://localhost:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;port&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running the code with &lt;code&gt;node server&lt;/code&gt; gives the following output and the website showing &lt;em&gt;Hello World!&lt;/em&gt; will be reachable at &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;localhost:3000&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Server running at http://localhost:3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For easier development install &lt;code&gt;npm install nodemon&lt;/code&gt; in the project and change the start command to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// package.json&lt;/span&gt;
&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;scripts&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;start&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;nodemon -i myapp/ server.js&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're struggling take a look at this &lt;a href="https://github.com/anoff/node-vue-websockets/tree/4dc36fc8fac8ee3d179379c0286ee2dfe58f4261" rel="noopener noreferrer"&gt;code&lt;/a&gt; for reference&lt;/p&gt;

&lt;h2&gt;
  
  
  Initialize Vue.js project
&lt;/h2&gt;

&lt;p&gt;The easiest way to set up a vue project is to use the &lt;code&gt;vue&lt;/code&gt;-CLI which is available via &lt;code&gt;npm install -g vue-cli&lt;/code&gt;. To initialize a project using &lt;code&gt;webpack&lt;/code&gt; as a bundler run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vue init webpack myapp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Answer the questionnaire with the default question or disable tests you do not want to implement. I chose not to install any test frameworks for this tutorial.&lt;/p&gt;

&lt;p&gt;Webpack comes with it's own development server with &lt;em&gt;hotreloading&lt;/em&gt; functionality so you see changes in the browser right away. Try it out by starting the server with &lt;code&gt;npm run dev&lt;/code&gt; (in the &lt;code&gt;myapp/&lt;/code&gt; directory) and opening the Vue.js template at &lt;a href="http://localhost:8080" rel="noopener noreferrer"&gt;localhost:8080&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fanoff.io%2Fblog%2Fimg%2Fassets%2Fnode-vue-websockets%2Fvue.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%2Fanoff.io%2Fblog%2Fimg%2Fassets%2Fnode-vue-websockets%2Fvue.png" alt="Vue.js template project"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;output of the webpack dev server &lt;code&gt;npm run dev&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&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%2Fanoff.io%2Fblog%2Fimg%2Fassets%2Fnode-vue-websockets%2Fvue-template.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%2Fanoff.io%2Fblog%2Fimg%2Fassets%2Fnode-vue-websockets%2Fvue-template.png" alt="Vue.js template project"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Template Vue.js project at &lt;a href="http://localhost:8080" rel="noopener noreferrer"&gt;http://localhost:8080&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When modifying the Vue.js components the web page will automatically reload&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// myapp/src/components/HelloWorld.vue&lt;/span&gt;

&lt;span class="c1"&gt;// chnage the script content to&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="o"&gt;&amp;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="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;HelloWorld&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nf"&gt;data &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Welcome to MY first Vue.js App&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/script&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By simply saving the file the development server will propagate the changes to any open browser window which will automatically reload to&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%2Fanoff.io%2Fblog%2Fimg%2Fassets%2Fnode-vue-websockets%2Fvue-template2.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%2Fanoff.io%2Fblog%2Fimg%2Fassets%2Fnode-vue-websockets%2Fvue-template2.png" alt="modified Vue.js template project"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Modified template with custom message&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;💡 If you're struggling take a look at this &lt;a href="https://github.com/anoff/node-vue-websockets/tree/3e19ae3fd902d719251cf42721ccc83fa27fb394" rel="noopener noreferrer"&gt;code&lt;/a&gt; for reference&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Material Design library
&lt;/h2&gt;

&lt;p&gt;To install &lt;code&gt;vue-material&lt;/code&gt; run the following command in the Vue.js directory &lt;code&gt;myapp/&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;vue-material@beta &lt;span class="nt"&gt;--save&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the following lines to &lt;code&gt;myapp/src/main.js&lt;/code&gt; to load the &lt;code&gt;vue-material&lt;/code&gt; components into the app.&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;VueMaterial&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;vue-material&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vue-material/dist/vue-material.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vue-material/dist/theme/black-green-light.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nx"&gt;Vue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;VueMaterial&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ℹ️ You might have to restart the dev server for this new plugin to take effect&lt;/p&gt;

&lt;p&gt;Create a new Vue.js component making use of several &lt;code&gt;vue-bootstrap&lt;/code&gt; components like the &lt;a href="https://vuematerial.io/components/app" rel="noopener noreferrer"&gt;app&lt;/a&gt; container.&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="c"&gt;&amp;lt;!-- myapp/src/components/Chat.vue--&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;template&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"page-container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;md-app&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;md-app-toolbar&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"md-primary"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"md-toolbar-row"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"md-title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;My Chat App&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/md-app-toolbar&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;md-app-content&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;md-field&lt;/span&gt; &lt;span class="na"&gt;:class=&lt;/span&gt;&lt;span class="s"&gt;"messageClass"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;label&amp;gt;&lt;/span&gt;Messages&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;md-textarea&lt;/span&gt; &lt;span class="na"&gt;v-model=&lt;/span&gt;&lt;span class="s"&gt;"textarea"&lt;/span&gt; &lt;span class="na"&gt;disabled&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/md-textarea&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/md-field&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;md-field&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;label&amp;gt;&lt;/span&gt;Your message&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;md-input&lt;/span&gt; &lt;span class="na"&gt;v-model=&lt;/span&gt;&lt;span class="s"&gt;"message"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/md-input&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;md-button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"md-primary md-raised"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Submit&lt;span class="nt"&gt;&amp;lt;/md-button&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/md-field&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/md-app-content&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/md-app&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;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="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;HelloWorld&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nf"&gt;data &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;textarea&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dummy text&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;blup&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;dummy text&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="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- Add "scoped" attribute to limit CSS to this component only --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;style &lt;/span&gt;&lt;span class="na"&gt;scoped&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nc"&gt;.md-app&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;800px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;#000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;.12&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.md-textarea&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;300px&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;/style&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To load the new component modify the router at &lt;code&gt;myApp/src/router/index.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// change HelloWorld -&amp;gt; Chat&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Vue&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;vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Router&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;vue-router&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Chat&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;@/components/Chat&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nx"&gt;Vue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Router&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Router&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;routes&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;path&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="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Chat&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Chat&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;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fanoff.io%2Fblog%2Fimg%2Fassets%2Fnode-vue-websockets%2Fchat.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%2Fanoff.io%2Fblog%2Fimg%2Fassets%2Fnode-vue-websockets%2Fchat.png" alt="Basic chat app"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;💡 If you're struggling take a look at this &lt;a href="https://github.com/anoff/node-vue-websockets/tree/99fa39d4761ddaa779192a7f9751820ab7952356" rel="noopener noreferrer"&gt;code&lt;/a&gt; for reference&lt;/p&gt;

&lt;h2&gt;
  
  
  Bring in websockets
&lt;/h2&gt;

&lt;p&gt;For the following development the web application will consume from two different endpoints. The &lt;code&gt;webpack-dev-server&lt;/code&gt; sends the web app sources (HTML, CSS, Javascript) and the node server will supply the &lt;code&gt;socket-io&lt;/code&gt; endpoint. This is typically not something you want to do in production but since we want both the node server and Vue frontend to be hot reloaded we need two systems - webpack and nodemon.&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%2Fwww.plantuml.com%2Fplantuml%2Fpng%2FNP0z3i8m34PtdyBgn58KYGKn8JX9sXWef77b12A4k3ik3RzBfBpdptQoZibAElSU0Zl2AbCpsFQ4ZYuOIIua5Tg8hUyef58pyVanue5ZUle95Ty8PmKLG1SIoSwsX8Ua8pxN72E07bWxpg5-vSUg5oeZeNJ3kfwDiHKEB0aNnfWVDKQBMvgbWNZgmc35zdW3n76nZRvhBtmERikU1Q_aFMULLhJFn1glHOhUc_w7VDVJZsTn9D_XEwmfEFtH1m00" 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%2Fwww.plantuml.com%2Fplantuml%2Fpng%2FNP0z3i8m34PtdyBgn58KYGKn8JX9sXWef77b12A4k3ik3RzBfBpdptQoZibAElSU0Zl2AbCpsFQ4ZYuOIIua5Tg8hUyef58pyVanue5ZUle95Ty8PmKLG1SIoSwsX8Ua8pxN72E07bWxpg5-vSUg5oeZeNJ3kfwDiHKEB0aNnfWVDKQBMvgbWNZgmc35zdW3n76nZRvhBtmERikU1Q_aFMULLhJFn1glHOhUc_w7VDVJZsTn9D_XEwmfEFtH1m00" alt="development setup"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  frontend: vue-socket.io
&lt;/h3&gt;

&lt;p&gt;For the Vue app to communicate with the websocket backend the socket.io library needs to be installed into &lt;code&gt;cd myApp/&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;vue-socket.io
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the node backend running on port &lt;code&gt;3000&lt;/code&gt; modify your vue application in &lt;code&gt;myApp/src/main.js&lt;/code&gt; to connect to the backend&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;VueSocketIO&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;vue-socket.io&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nx"&gt;Vue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;VueSocketIO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:3000&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;To bring some very basic functionality into the app we will show messages that were send from other instances in a list and add the ability to send messages.&lt;br&gt;
For sending messages we need to give the &lt;code&gt;Submit&lt;/code&gt; button an action once it is triggered by adding a &lt;code&gt;v-on:click&lt;/code&gt; method&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;md-button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"md-primary md-raised"&lt;/span&gt; &lt;span class="na"&gt;v-on:click=&lt;/span&gt;&lt;span class="s"&gt;"sendMessage()"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Submit&lt;span class="nt"&gt;&amp;lt;/md-button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;sendMessage()&lt;/code&gt; function and the socket interactions are specified in the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="o"&gt;&amp;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="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Chat&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nf"&gt;data &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;textarea&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="na"&gt;message&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="na"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="na"&gt;sockets&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;
    &lt;span class="nf"&gt;connect &lt;/span&gt;&lt;span class="p"&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;connected to chat server&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;count &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="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nf"&gt;message &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// this function gets triggered once a socket event of `message` is received&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textarea&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="c1"&gt;// append each new message to the textarea and add a line break&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="na"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;sendMessage &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// this will emit a socket event of type `function`&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// send the content of the message bar to the server&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt; &lt;span class="c1"&gt;// empty the message bar&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/script&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  backend: socket-io / server.js
&lt;/h3&gt;

&lt;p&gt;Server.js already comems with socket-io bundled into it. The only thing to do in the backend to enable a basic chat operation is to react to a &lt;code&gt;message&lt;/code&gt; event sent from the UI and propagate this to all connected sockets.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// modify server.js to include the socket methods&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;socket&lt;/span&gt; &lt;span class="p"&gt;}&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;server/router&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;server&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="nf"&gt;get&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="nx"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;h1&amp;gt;Hello you!&amp;lt;/h1&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nf"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Send the message to every socket&lt;/span&gt;
    &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="nf"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;connect&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;client connected&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sockets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sockets&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;count&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;msg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;HI U&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sockets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sockets&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&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;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="s2"&gt;`Server running at http://localhost:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;port&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After running &lt;code&gt;npm start&lt;/code&gt; in the server directory the server will now create logs for every web page that gets opened. It logs the list of currently open sockets.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note there is no disconnect event registered yet in this tutorial so the &lt;code&gt;count&lt;/code&gt; event will only be emitted when a new website connects.&lt;/p&gt;
&lt;/blockquote&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%2Fanoff.io%2Fblog%2Fimg%2Fassets%2Fnode-vue-websockets%2Fclients.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%2Fanoff.io%2Fblog%2Fimg%2Fassets%2Fnode-vue-websockets%2Fclients.png" alt="Server logs for connecting clients"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Showtime 🍿
&lt;/h2&gt;

&lt;p&gt;Running the demo in two browsers/separate devices in the same network will look like this. It is a very, very, very basic but totally anonymous chat system.&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%2Fanoff.io%2Fblog%2Fimg%2Fassets%2Fnode-vue-websockets%2F2chats.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fanoff.io%2Fblog%2Fimg%2Fassets%2Fnode-vue-websockets%2F2chats.gif" alt="Very basic chat system"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can find a &lt;a href="https://github.com/anoff/node-vue-websockets/commits/master" rel="noopener noreferrer"&gt;repository on github&lt;/a&gt; containing this demo code.&lt;/p&gt;

&lt;p&gt;I hope this blog helped you:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;set up an easy node server&lt;/li&gt;
&lt;li&gt;bootstrap a Vue project with &lt;code&gt;vue-cli&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;get fancy UI elements in Vue using material design&lt;/li&gt;
&lt;li&gt;integrate websockets to provide realtime communication&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What to do next:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;add tests to backend/frontend&lt;/li&gt;
&lt;li&gt;store state/sessions in frontend&lt;/li&gt;
&lt;li&gt;possibly add authentication&lt;/li&gt;
&lt;li&gt;improve UI (e.g. register enter button on message bar)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Feel free to leave a comment or reach out on twitter 🐦&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>node</category>
      <category>vue</category>
      <category>javascript</category>
    </item>
    <item>
      <title>GitLab CI/CD for GitHub — How and Why?</title>
      <dc:creator>Andreas Offenhaeuser</dc:creator>
      <pubDate>Fri, 30 Mar 2018 20:08:23 +0000</pubDate>
      <link>https://dev.to/anoff/gitlab-cicd-for-githubhow-and-why-4bik</link>
      <guid>https://dev.to/anoff/gitlab-cicd-for-githubhow-and-why-4bik</guid>
      <description>&lt;h3&gt;
  
  
  GitLab CI/CD for GitHub — How and Why?
&lt;/h3&gt;

&lt;p&gt;When creating a git project that you want to share with others you traditionally had the choice between GitHub with its huge community and tons of integrations, GitLab with a great overall dev experience from issues to one of the best CI/CD solutions out there and BitBucket being one of the friends you have since kindergarden. My personal decision was to host all my personal projects on &lt;a href="https://github.com/anoff" rel="noopener noreferrer"&gt;🦑 GitHub&lt;/a&gt;. For projects that need CI/CD I tinkered around with &lt;a href="https://travis-ci.org/" rel="noopener noreferrer"&gt;👷‍♂️ Travis CI&lt;/a&gt; and &lt;a href="https://circleci.com/" rel="noopener noreferrer"&gt;🅾️ Circle CI&lt;/a&gt; on top of GitHub.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F912%2F1%2AlaA78JtrefVUbLNEXjw_cg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F912%2F1%2AlaA78JtrefVUbLNEXjw_cg.png"&gt;&lt;/a&gt;It is no longer GitHub OR GitLab&lt;/p&gt;

&lt;p&gt;But recently &lt;a href="https://about.gitlab.com/features/github/" rel="noopener noreferrer"&gt;GitLab announced&lt;/a&gt; that their amazing CI/CD solution can now be combined with git projects hosted on Github. Having used a fullstack GitLab solution at work for the last 1.5 years I was really psyched to test it for my personal projects. Now you can finally have the best of both worlds — GitHubs reach in the community and GitLabs CI tooling 💃&lt;/p&gt;

&lt;h3&gt;
  
  
  How to enable a GitLab CI/CD pipeline for a GitHub project
&lt;/h3&gt;

&lt;p&gt;At first glance GitLab integrates like any other CI/CD service into your project. Using webhooks that the service either adds for you automatically or you define manually. GitLab did a pretty good job on describing &lt;a href="https://docs.gitlab.com/ee/ci/ci_cd_for_external_repos/github_integration.html" rel="noopener noreferrer"&gt;how to set those up&lt;/a&gt; so I wont repeat the steps again in detail.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F729%2F1%2A0yMTq5LZdnbUwp7ykpHppw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F729%2F1%2A0yMTq5LZdnbUwp7ykpHppw.png"&gt;&lt;/a&gt;CI/CD integration into GitHub via webhook&lt;/p&gt;

&lt;p&gt;To set it up you first need to have your project on GitHub. I set up a small &lt;a href="https://github.com/anoff/node-ci-dummy" rel="noopener noreferrer"&gt;node.JS web app&lt;/a&gt; to play around with the integration. Feel free to fork it or create your own. Next you head over to &lt;a href="https://gitlab.com/" rel="noopener noreferrer"&gt;GitLab&lt;/a&gt; sign in — I suggest using GitHub OAUTH to keep your code credentials in one place. To set up your GitLab CI/CD project simply create a new project and choose the option on the far right CI/CD for external repo and click GitHub.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F670%2F1%2AZNUtH4F0iN7XtRScmcRQcw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F670%2F1%2AZNUtH4F0iN7XtRScmcRQcw.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AiIzoz5QkpUTdz8nMOYFmtg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AiIzoz5QkpUTdz8nMOYFmtg.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You will see a list of your GitHub repos and hit Connect on the one that you want to use for this integration. In my case it is the repo anoff/node-ci-dummy&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2A1vQDUTpD07NZ-A4_44LrqA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2A1vQDUTpD07NZ-A4_44LrqA.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2A8cQIG5Jw6i_FlEfcVE-MJg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2A8cQIG5Jw6i_FlEfcVE-MJg.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After doing this you will see that GitLab actually cloned &lt;a href="https://gitlab.com/anoff/node-ci-dummy" rel="noopener noreferrer"&gt;your entire project&lt;/a&gt;. There are two settings that make sure this magic 🧙‍♀️ works. One is the previously mentioned webhook on the GitHub side that will trigger as soon as you git push onto your GitHub repository. The other is the repository settings on your newly created GitLab project that is set to PULL from a remote repository — namely the GitHub project you selected.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AKJfhmNFMagGt6AxTTOj0eg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AKJfhmNFMagGt6AxTTOj0eg.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2A2lI94T72HdmERMgxYJKr_Q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2A2lI94T72HdmERMgxYJKr_Q.png"&gt;&lt;/a&gt;GitHub webhook (left) and GitLab repo mirror options (right)&lt;/p&gt;

&lt;h3&gt;
  
  
  How to configure a CI pipeline
&lt;/h3&gt;

&lt;p&gt;The final thing to do is to set up an actual CI pipeline in your repository. GitLab will automatically recognize the pipeline definition and run the defined jobs. If you are unfamiliar with GitLab CI you might want to start with a minimalist pipeline definition. Just create a .gitlab-ci.yml in your root directory. Example content to test a node.JS app:&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;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node:9&lt;/span&gt;
&lt;span class="na"&gt;lint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;npm install&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;npm run lint&lt;/span&gt;

&lt;span class="na"&gt;unit test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;npm install&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;npm test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In my dummy repository this will run &lt;a href="https://standardjs.com/" rel="noopener noreferrer"&gt;standard&lt;/a&gt; linting and &lt;a href="https://github.com/avajs/ava" rel="noopener noreferrer"&gt;ava&lt;/a&gt; unit tests in parallel. Below you can see the commit that introduced this file and the respective &lt;a href="https://gitlab.com/anoff/node-ci-dummy/pipelines/19765773" rel="noopener noreferrer"&gt;pipeline that was triggered&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F966%2F1%2AeeV-370AqhtP_u7pn4vUyw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F966%2F1%2AeeV-370AqhtP_u7pn4vUyw.png"&gt;&lt;/a&gt;GitHub commit view with a pending GitLab pipeline&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AkyIC64kaAg1JGARmQnKieQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AkyIC64kaAg1JGARmQnKieQ.png"&gt;&lt;/a&gt;GitLab view of the pipeline in process&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F272%2F1%2AMx5QNLDV3WDJbLVQzg8DjA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F272%2F1%2AMx5QNLDV3WDJbLVQzg8DjA.png"&gt;&lt;/a&gt;Success marker on GitHub after the CI pipeline finished&lt;/p&gt;

&lt;p&gt;Check out &lt;a href="https://docs.gitlab.com/ee/ci/yaml/" rel="noopener noreferrer"&gt;the docs&lt;/a&gt; for more info writing custom pipelines. For example introducing stages might help you get a handle on more complex pipeline workflows. If you are running a public GitHub project and you want guests to see your pipeline results in detail you should make sure your GitLab project is set to  &lt;strong&gt;Public&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F882%2F1%2ACwOSDGb_fuuDyIEeBYzHyA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F882%2F1%2ACwOSDGb_fuuDyIEeBYzHyA.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Final thoughts
&lt;/h3&gt;

&lt;p&gt;This post covered how to set up a basic integration workflow between GitHub and GitLab: not a lot of work 🏋️‍♀️ for you actually.&lt;/p&gt;

&lt;p&gt;Things to do next:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;introduce &lt;a href="https://docs.gitlab.com/ee/ci/yaml/#stages" rel="noopener noreferrer"&gt;multiple stages&lt;/a&gt; to your pipeline&lt;/li&gt;
&lt;li&gt;test against various &lt;a href="https://docs.gitlab.com/ee/ci/yaml/#image-and-services" rel="noopener noreferrer"&gt;runtimes&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;make use of the service tag in GitLab pipelines to spawn a database to run smaller integration tests&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In my opinion the only downside of the GitLab CI/CD compared to other solutions like Travis, Circle, Drone is that the fact that GitLab creates a fully featured project for you might create some confusion for people. One thing you should definitely do is follow the permission settings above and disable &lt;em&gt;issues&lt;/em&gt;, &lt;em&gt;wiki&lt;/em&gt;, &lt;em&gt;pull requests&lt;/em&gt; on your GitLab repositiry in this case. That way your permissions will prevent people from interacting with you via GitLab. In addition your project automatically shows up as Mirrored from which links back to your GitHub master project. If that is not enough for you, you can always add a link in your README and your contribution guidelines.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F989%2F1%2A_pSl5B56gb6Ft7-4IHAmjg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F989%2F1%2A_pSl5B56gb6Ft7-4IHAmjg.png"&gt;&lt;/a&gt;GitLab project showing link to the original project&lt;/p&gt;

&lt;p&gt;Oh btw if you are still looking for the &lt;strong&gt;Why&lt;/strong&gt; that the title promised: GitLabs CI/CD solution is just one of the most comprehensive out there imo.&lt;/p&gt;

&lt;p&gt;Feel free to discuss on &lt;a href="https://twitter.com/an0xff" rel="noopener noreferrer"&gt;twitter&lt;/a&gt; or comments 👍&lt;/p&gt;

</description>
      <category>devops</category>
      <category>node</category>
      <category>gitlab</category>
      <category>github</category>
    </item>
    <item>
      <title>Automated dev workflow for using Data Science VM on Azure</title>
      <dc:creator>Andreas Offenhaeuser</dc:creator>
      <pubDate>Wed, 21 Mar 2018 22:37:16 +0000</pubDate>
      <link>https://dev.to/anoff/automated-dev-workflow-for-using-data-science-vm-on-azure-2h55</link>
      <guid>https://dev.to/anoff/automated-dev-workflow-for-using-data-science-vm-on-azure-2h55</guid>
      <description>&lt;p&gt;&lt;em&gt;This is crosspost from &lt;a href="https://medium.com/@an0xff/automated-dev-workflow-for-using-data-science-vm-on-azure-13c1a5b56f91"&gt;Medium&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;tl;dr; I put together a bunch of scripts on Github that let you deploy a VM from your command line as well as sync code from your local directory to the VM easily to be able to use local IDE and git but execute on the powerful remote machine. Perfect for Data Science applications based around jupyter notebook.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://dev.to/anoff/deploy-datascience-infrastructure-on-azure-using-terraform-12kl-temp-slug-8098101"&gt;my previous blog post&lt;/a&gt; I explained how to do &lt;a href="https://www.terraform.io/intro/index.html"&gt;Terraform&lt;/a&gt; deployment of an &lt;a href="https://docs.microsoft.com/en-us/azure/machine-learning/data-science-virtual-machine/overview"&gt;Azure Data Science Virtual Machine&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://cdn-images-1.medium.com/max/1012/1*Bx6KlPw3otLxrY_nYQdAQg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://cdn-images-1.medium.com/max/1012/1*Bx6KlPw3otLxrY_nYQdAQg.png" alt=""&gt;&lt;/a&gt;Overview of available commands&lt;/p&gt;

&lt;h3&gt;
  
  
  Motivation 😓
&lt;/h3&gt;

&lt;p&gt;Recently I started to do some #deeplearning 🔮 as part of my Udacity Artificial Intelligence Nanodegree. When I was working on the #deeplearning Nanodegree last year I started to &lt;a href="https://github.com/anoff/nd101/blob/master/gan_mnist/Makefile"&gt;script starting/stopping an AWS GPU VM&lt;/a&gt; and rsyncing code around. This time I felt like giving the Azure Cloud a try. Mostly because my daytime job lets me look at a lot of their services I wanted to venture deeper into the Azure Data Science offerings. Being more of a software developer by trait and less of a data scientist 👨‍🔬 I often feel like my standards for versioning, testing and ease of development are beyond those that the ML ecosystem offers by default (hope that doesn’t offend the data wizards out there). My development machine is a small MacBook without GPU support. So to train neural networks I wanted to get a Virtual Machine with a GPU on board. Azure offers VMs with a &lt;a href="https://azure.microsoft.com/en-us/services/virtual-machines/data-science-virtual-machines/"&gt;prebaked Ubuntu image&lt;/a&gt; containing all of today's Data Science tools: Python, Conda, Jupyter, GPU Neuralnet libs etc.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://cdn-images-1.medium.com/max/562/1*Oau5dKnDt_e7wFfOlX2llA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://cdn-images-1.medium.com/max/562/1*Oau5dKnDt_e7wFfOlX2llA.png" alt=""&gt;&lt;/a&gt;Top features, see &lt;a href="https://docs.microsoft.com/en-us/azure/machine-learning/data-science-virtual-machine/overview"&gt;this list&lt;/a&gt; for full stack available on DSVM (&lt;a href="https://azuremarketplace.microsoft.com/en-us/marketplace/apps/microsoft-ads.linux-data-science-vm-ubuntu"&gt;source&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Having the perfect target for running my code I was wondering how to actually keep my local machine my main development machine — meaning I don’t want to setup git on the VM to version my code. This is where our friend &lt;a href="https://en.wikipedia.org/wiki/Rsync"&gt;rsync&lt;/a&gt; comes into the picture 🖼. It lets you sync two directories over SSH.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rsync [OPTION]... SRC [SRC]... [USER@]HOST::DEST
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  The Goal 🏁
&lt;/h3&gt;

&lt;p&gt;Being prone to over-engineering 🙄 my side projects I started my journey of automating my VM workflow with the following goals:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Deploy (and delete) an entire VM by a template that I can version on Github&lt;/li&gt;
&lt;li&gt;Start/Stop the VM from my command line so I don’t pay for it if I don’t need it (GPU VMs are 💰💰💰)&lt;/li&gt;
&lt;li&gt;Get code changes I make on the VM using jupyter notebook synchronized to my local machine so I can git commit&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Deploy infrastructure 📦
&lt;/h3&gt;

&lt;p&gt;Again I opted for Terraform to deploy the VM. As mentioned in my previous blog post you could also use &lt;a href="https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-overview"&gt;Azure Resource Manager Templates&lt;/a&gt; for that, but my personal favorite is &lt;a href="https://www.terraform.io/"&gt;Terraform&lt;/a&gt; 😍. So I continued from my previous findings to build the following Terraform recipe. The suggested setup is to place the script into an infra folder into your projects working directory.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/anoff/vm-automation/blob/master/azure_dsvm.tf"&gt;anoff/vm-automation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It creates several resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;resource group: &lt;em&gt;logical grouping on Azure that contains all the resources below&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;virtual network: &lt;em&gt;well..a virtual private network that your resources use to communicate*&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;network subnet: &lt;em&gt;the subnet that your VPN will use*&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;network interface: &lt;em&gt;a network interface so your VM can bind against the virtual network&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;virtual machine: &lt;em&gt;the actual compute resource (will spawn disk resources for file system)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;public IP address: &lt;em&gt;a static IP that will make your VM reachable from the internet&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;local executor (null resource): &lt;em&gt;used to write some results of the VM creation process onto your disk&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;* sorry if I did not explain those correctly tbh I am not 💯% sure I understand correctly what they do either 😊&lt;/p&gt;

&lt;p&gt;&lt;a href="https://cdn-images-1.medium.com/max/729/1*ffSnnPoJkPq2BnXqUfixFQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://cdn-images-1.medium.com/max/729/1*ffSnnPoJkPq2BnXqUfixFQ.png" alt=""&gt;&lt;/a&gt;This are the created resources&lt;/p&gt;

&lt;p&gt;&lt;a href="https://cdn-images-1.medium.com/max/749/1*TT4fEhGizjadrx17c0Ynew.png" class="article-body-image-wrapper"&gt;&lt;img src="https://cdn-images-1.medium.com/max/749/1*TT4fEhGizjadrx17c0Ynew.png" alt=""&gt;&lt;/a&gt;Variables in the Terraform recipe&lt;/p&gt;

&lt;p&gt;Leveraging &lt;a href="https://www.terraform.io/docs/configuration/variables.html"&gt;Terraform variables&lt;/a&gt; some of the properties of this recipe can be customized.&lt;/p&gt;

&lt;p&gt;The easiest way to change the variable values is to &lt;code&gt;config.auto.tfvars&lt;/code&gt; file that contains all the variable names and their description as well.&lt;/p&gt;

&lt;p&gt;You can find it in the &lt;a href="https://github.com/anoff/vm-automation/blob/master/config.auto.tfvars"&gt;Github repo&lt;/a&gt; right next to the Terraform recipe itself. As you can see all variables have a default value even if you not specify the .tfvars properties. The ones you most likely want to modify are &lt;code&gt;admin_public_key&lt;/code&gt; and &lt;code&gt;admin_private_key&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;They are a &lt;a href="https://help.github.com/articles/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent/"&gt;SSH key pair&lt;/a&gt; that you will use to authenticate when connecting to the virtual machine later. During the Terraform process the public key will be stored on the VM so it will later recognize it as a valid key. The private key will be used to SSH into the machine near the end of the process to actually prepare the local file system for later file transfers — namely create a &lt;code&gt;~/work&lt;/code&gt; directory. You might also want to modify the admin username or the resource location.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://cdn-images-1.medium.com/max/693/1*xBors1HBFd35O0ZDrVqnGQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://cdn-images-1.medium.com/max/693/1*xBors1HBFd35O0ZDrVqnGQ.png" alt=""&gt;&lt;/a&gt;config.auto.tfvars&lt;/p&gt;

&lt;h4&gt;
  
  
  Signing the license terms for Data Science VM ⚖️
&lt;/h4&gt;

&lt;p&gt;You might see the following error when trying to run the Terraform script without having read this far.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://cdn-images-1.medium.com/max/1024/0*rBLvs8PYsESCoQM0." class="article-body-image-wrapper"&gt;&lt;img src="https://cdn-images-1.medium.com/max/1024/0*rBLvs8PYsESCoQM0." alt=""&gt;&lt;/a&gt;Terraform error due to missing license agreement&lt;/p&gt;

&lt;p&gt;The problem is that the DSVM is published via the Azure market place and even though it does not incur additional charges on top of the Azure VM resources you need to read and agree to the license terms. You can do this as described in the error message via Powershell. The complete process of opening a Powershell is explained in this &lt;a href="https://github.com/anoff/vm-automation#sign-the-terms-of-service-%EF%B8%8F"&gt;Readme&lt;/a&gt;. The short version if you already have Powershell open is to run:&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Use this command to view the current license agreement

$ Get-AzureRmMarketplaceTerms -Publisher "microsoft-ads" -Product "linux-data-science-vm-ubuntu" -Name "linuxdsvmubuntu" # If you feel confident to agree to the agreement use the following command to enable the offering for your subscription

$ Get-AzureRmMarketplaceTerms -Publisher "microsoft-ads" -Product "linux-data-science-vm-ubuntu" -Name "linuxdsvmubuntu" | Set-AzureRmMarketplaceTerms -Accept
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;After successfully signing the license terms you should see the following output in your shell&lt;/p&gt;

&lt;p&gt;&lt;a href="https://cdn-images-1.medium.com/max/1024/0*dJGkO860Y2VCzaDW.png" class="article-body-image-wrapper"&gt;&lt;img src="https://cdn-images-1.medium.com/max/1024/0*dJGkO860Y2VCzaDW.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Run Terraform 🏃‍♂️
&lt;/h4&gt;

&lt;p&gt;Once the license terms are signed you can initialize Terraform using &lt;code&gt;terraform init&lt;/code&gt; and then can run &lt;code&gt;terraform apply&lt;/code&gt; to bring up the infrastructure resources on azure. It may take up 5~10 minutes to fully provision the virtual machine.&lt;/p&gt;

&lt;p&gt;After running it you may notice two new files being created. Both contain a link to the created virtual machine. &lt;code&gt;.vm-ip&lt;/code&gt; contains the public IP address that was created and will be used to SSH into the machine. &lt;code&gt;.vm-id&lt;/code&gt; is the Azure Resource ID of your virtual machine and is a unique identifier that we will use to start/stop the machine later. Both are plain text files and only contain one line, feel free to check them out. The machine is now up and running and you can work with it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bring code onto the VM 💁‍♀️
&lt;/h3&gt;

&lt;p&gt;Before doing any work you might want to upload some bootstrap code onto the virtual machine — or you just want to run an existing jupyter notebook there. Again the Github repository holds a small script that will help you do this (works out of the box on Mac/Unix machines otherwise you need to install make and rsync first).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/anoff/vm-automation/blob/master/Makefile"&gt;anoff/vm-automation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Place the &lt;code&gt;Makefile&lt;/code&gt; into the working directory of your code and make sure to update the PATH definitions to the two files mentioned at the end of the last chapter containing the details of the newly created virtual machine. If you had the Terraform script in a subfolder named infra there is nothing to do. Otherwise you should either copy over the two files into such a directory or modify the PATH definition in the Makefile.&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;make syncup&lt;/code&gt; in your working directory (where you placed the Makefile) to sync your local directory content onto the remote machine. You can see the command that is being executed and what the remote directory will be named. In my case it is ~/work/AIND-RNN which is one of my Nanodegree projects. You can also see that the command automatically ignores all files that are defined in your &lt;code&gt;.gitignore&lt;/code&gt; which means anything you do not want to version will also not be copied around. This is especially useful for artifacts created during neural net training processes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://cdn-images-1.medium.com/max/865/1*Dq4e1DsOEc8LIQv8iMR2UQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://cdn-images-1.medium.com/max/865/1*Dq4e1DsOEc8LIQv8iMR2UQ.png" alt=""&gt;&lt;/a&gt;Output of make syncup&lt;/p&gt;

&lt;h3&gt;
  
  
  Run Jupyter Notebook 📒
&lt;/h3&gt;

&lt;p&gt;Let’s assume that your project also holds a Jupyter notebook you want to execute on the remote machine and access from your local browser. &lt;em&gt;You could also use a similar process to execute ANY kind of script on the remote machine.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;First you need to SSH into the machine using &lt;code&gt;make ssh&lt;/code&gt; which will also do port forwarding for the Jupyter Port &lt;strong&gt;8888&lt;/strong&gt; onto your local machine so you can open &lt;a href="http://localhost:8888"&gt;http://localhost:8888&lt;/a&gt; in your local browser (my MacBook) and connect to the webserver that listens on this port on the virtual machine (Jupyter Notebook). Now that you have a shell running on the DSVM manipulate the file system, install missing packages via pip/conda or just start a process.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://cdn-images-1.medium.com/max/1020/1*yTi-8TPBnLCdysDuJT3Yaw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://cdn-images-1.medium.com/max/1020/1*yTi-8TPBnLCdysDuJT3Yaw.png" alt=""&gt;&lt;/a&gt;Starting jupyter notebook on the VM&lt;/p&gt;

&lt;p&gt;The Jupyter notebook process started above is linked to the lifecycle of the interactive shell that we opened with the SSH connection. Closing the SSH connection will kill the Jupyter server as well. &lt;em&gt;All your code should still be there as Jupyter regularly saves to disk but your python kernel will be gone and all the memory objects (state of notebook execution) will be lost. You will need to execute the notebook again from beginning after you SSH again into the machine and start Jupyter up.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Commit your changes 💾
&lt;/h3&gt;

&lt;p&gt;After you did some changes and you want to git commit like a good developer you need to get those changes you did on the virtual machine to your local development environment. You can do this using make syncdown which will copy all changed remote files onto your local working directory — again only those under git version control.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;_🚨_Make sure you exit the SSH connection first&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://cdn-images-1.medium.com/max/876/1*d9lbCykuPCtKQhe1sDdsBw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://cdn-images-1.medium.com/max/876/1*d9lbCykuPCtKQhe1sDdsBw.png" alt=""&gt;&lt;/a&gt;Copy remote changes to local filesystem&lt;/p&gt;

&lt;p&gt;The remote file &lt;code&gt;LOOK_MOMMY_I_CHANGED_A_FILE&lt;/code&gt; has now been copied to my local working directory and I can use git commit -am "everyone uses meaningful commit messages right?" to commit my changes or use my local tooling to execute unit tests, check codestyle, add some comments…&lt;/p&gt;

&lt;h3&gt;
  
  
  Start and Stop the Virtual Machine 🎬 🛑
&lt;/h3&gt;

&lt;p&gt;If you have not checked already, you should look up how much the Virtual Machine that you provisioned actually costs you. The &lt;strong&gt;Standard_NC6&lt;/strong&gt; (which is the cheapest GPU instance) will cost you a small holiday if you keep it running for a month. That is the reason why I wanted an easy way to stop it when I don’t need it and get it back up quickly if I want to continue working.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://cdn-images-1.medium.com/max/1024/1*l5o4sU8CQMVF29JDpviaNA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://cdn-images-1.medium.com/max/1024/1*l5o4sU8CQMVF29JDpviaNA.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Makefile comes with three commands for managing the state of the virtual machine itself. They all require the unique Azure Resource ID located in the .vm-id to select the correct VM in your Azure subscription:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;make stop&lt;/code&gt; will stop stop the virtual machine &lt;em&gt;AND&lt;/em&gt; deallocate the resources which will significantly reduce the costs as you only pay for the disks that hold your data.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;make start&lt;/code&gt; tells Azure to allocate new resources and spawn up the virtual machine again&lt;/p&gt;

&lt;p&gt;&lt;code&gt;make status&lt;/code&gt; will tell you if the virtual machine is up or not&lt;/p&gt;

&lt;p&gt;&lt;a href="https://cdn-images-1.medium.com/max/642/1*fWfS39LrqSt3BVnjTVdk-Q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://cdn-images-1.medium.com/max/642/1*fWfS39LrqSt3BVnjTVdk-Q.png" alt=""&gt;&lt;/a&gt;Virtual Machine start/status/stop&lt;/p&gt;

&lt;p&gt;The screenshot shows you how long stopping and starting the VM might take. However as soon as you see the CLI saying Running \ you can shut down your local machine as Azure started deallocating your resources.&lt;/p&gt;

&lt;h4&gt;
  
  
  Reduce risk of bankruptcy 💸
&lt;/h4&gt;

&lt;p&gt;If you are afraid of the bill that might come flying in if you miss stopping the virtual machine you should take a closer look at the &lt;strong&gt;auto shutdown&lt;/strong&gt; features that Azure offers you. It lets you specify a time at which the VM will automatically shut down every day.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://cdn-images-1.medium.com/max/224/1*eCQ-Uvq0dzVIK8EiqdtFvQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://cdn-images-1.medium.com/max/224/1*eCQ-Uvq0dzVIK8EiqdtFvQ.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://cdn-images-1.medium.com/max/514/1*q7Mp8WkWI_0rtsKkMAQIUw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://cdn-images-1.medium.com/max/514/1*q7Mp8WkWI_0rtsKkMAQIUw.png" alt=""&gt;&lt;/a&gt;Virtual Machine Auto-Shutdown&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;But let me tell you from experience — if you accidentally keep it up for a weekend and see the bill the next week you will *always* shutdown from then on. That was actually one of the reasons why I wanted to make this workflow as easy as possible.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Summary 📚
&lt;/h3&gt;

&lt;p&gt;Well I hope you liked this article and found some helpful tips. The general workflow can also be done with AWS machines but the Terraform template will look different. Feel free to submit a PR to my Repo and I will add the option to also use AWS resources.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/anoff/vm-automation"&gt;anoff/vm-automation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I would love to hear feedback via &lt;a href="https://github.com/anoff/vm-automation/issues/new"&gt;Issues&lt;/a&gt;, &lt;a href="https://twitter.com/an0xff"&gt;Twitter 🐦&lt;/a&gt; or comments. &lt;em&gt;One thought I have is to bundle all the commands into a binary CLI so it works cross platform and can just be installed by copying around a single file. If you’re interested please let me know&lt;/em&gt;😻&lt;/p&gt;

&lt;p&gt;Here is another look at all the commands you can use🧙‍♀️&lt;/p&gt;

&lt;p&gt;&lt;a href="https://cdn-images-1.medium.com/max/1012/1*Bx6KlPw3otLxrY_nYQdAQg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://cdn-images-1.medium.com/max/1012/1*Bx6KlPw3otLxrY_nYQdAQg.png" alt=""&gt;&lt;/a&gt;Available commands&lt;/p&gt;

&lt;p&gt;/andreas&lt;/p&gt;

</description>
      <category>datascience</category>
      <category>azure</category>
      <category>devops</category>
      <category>terraform</category>
    </item>
    <item>
      <title>How to create a streaming HTTP interface in Python?</title>
      <dc:creator>Andreas Offenhaeuser</dc:creator>
      <pubDate>Fri, 16 Mar 2018 14:51:52 +0000</pubDate>
      <link>https://dev.to/anoff/how-to-create-a-streaming-http-interface-in-python--ceh</link>
      <guid>https://dev.to/anoff/how-to-create-a-streaming-http-interface-in-python--ceh</guid>
      <description>&lt;p&gt;Hey folks,&lt;/p&gt;

&lt;p&gt;lately I'm playing around a lot with artificial intelligence and Python. What I want to build now is a web interface for a python based ML model (keras etc). For full flexibility I want the app to a) take a stream of input data b) return a stream of output data. Each chunk in the input stream would be run through the ML model and the corresonding output streamed back as a chunk.&lt;/p&gt;

&lt;p&gt;However I could not find any resources online that tell me how to SEND a stream response in a production grade python web framework. Anyone got experience how to send a HTTP stream response in python?&lt;/p&gt;

&lt;p&gt;Currently I'm focusing on the combination of falcon + gunicorn to create a web app as Flask doesn't seem to be a production grade framework. Although my requirements are not production-grade I would love to figure out how to do this at such a level.&lt;/p&gt;

&lt;p&gt;/andreas&lt;/p&gt;

</description>
      <category>help</category>
      <category>python</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
