<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Monisnap</title>
    <description>The latest articles on Forem by Monisnap (@monisnap).</description>
    <link>https://forem.com/monisnap</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%2Forganization%2Fprofile_image%2F915%2Fbc82ab17-6eba-4fe7-8205-7f1bb80c43ef.png</url>
      <title>Forem: Monisnap</title>
      <link>https://forem.com/monisnap</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/monisnap"/>
    <language>en</language>
    <item>
      <title>How we built a themeable multi-brand design system with Vue.js</title>
      <dc:creator>Robin Lambell</dc:creator>
      <pubDate>Tue, 21 Jun 2022 09:00:43 +0000</pubDate>
      <link>https://forem.com/monisnap/how-we-built-a-themeable-multi-brand-design-system-with-vuejs-4flk</link>
      <guid>https://forem.com/monisnap/how-we-built-a-themeable-multi-brand-design-system-with-vuejs-4flk</guid>
      <description>&lt;p&gt;At Monisnap our goal is to enable users to support their friends and family abroad by providing remittance services where traditional banking services are unavailable.&lt;/p&gt;

&lt;p&gt;To this end, we build products under our own brand but we also have partners who  leverage our expertise to enable their users to transfer money without the complexities that arise when dealing with multiple methods of international remittance.&lt;/p&gt;

&lt;p&gt;In 2021 the front-end team at Monisnap built the &lt;strong&gt;Monisnap Interface Kit&lt;/strong&gt; (&lt;strong&gt;Moniik&lt;/strong&gt; for short). In the following article we’ll go over its creation, from the first meetings to the first projects in production.&lt;/p&gt;

&lt;p&gt;For the sake of brevity we are focusing more on the process and reasoning rather than deep-diving the technical details but feel free to get in touch if your curiosity is piqued! &lt;/p&gt;

&lt;h2&gt;
  
  
  Why build a design system ?
&lt;/h2&gt;

&lt;p&gt;The main reasons organizations decide to build a design system are &lt;a href="https://www.invisionapp.com/inside-design/guide-to-design-systems/" rel="noopener noreferrer"&gt;myriad&lt;/a&gt; &lt;a href="https://material.io/design/introduction" rel="noopener noreferrer"&gt;and&lt;/a&gt; &lt;a href="https://medium.com/@broccolini/design-systems-at-github-c8e5378d2542" rel="noopener noreferrer"&gt;well documented&lt;/a&gt;. For the team at Monisnap our top priorities were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enabling the tech team to deliver new products under the Monisnap brand in a small time frame while preserving the visual consistency of the Monisnap brand.&lt;/li&gt;
&lt;li&gt;Delivering a complete turn-key remittance product to our partners with a short turn-around time. Our back-end services are stable and require only small configuration adjustments for new partners, we wanted an equally fast process for the front-end.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The kaleidoscope of design possibilities
&lt;/h2&gt;

&lt;p&gt;The elephant in our design-system-room is of course the "themeable multi-brand" part. Our partners want a seamless experience when their users switch between the partner's main application and the Monisnap application. We need to make our product look and behave like our partner's and follow their brand guidelines. &lt;/p&gt;

&lt;p&gt;But doesn't building a design system, without knowing the full extent of customization that will be required of it before-hand seem at odds with the basic idea of finding common patterns in designs, describing and re-using them?&lt;/p&gt;

&lt;p&gt;Is Moniik really a bonafide design system? Maybe not, but we don’t get too hung up about it. We're happy with the productivity we've gained since and it's met our objective of cutting down on development time by orders of magnitude.&lt;/p&gt;

&lt;p&gt;For example, here is the same recipient form themed for Monisnap and multiple partners. The screenshot below is from Figma, but with a handful of SASS variables we achieved the exact same thing in our production deployments.&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%2F1c2cq2gpg6xcy3cvjzgb.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%2F1c2cq2gpg6xcy3cvjzgb.png" alt="Variations of the same recipient form"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Goyav, our first step towards a design system.
&lt;/h2&gt;

&lt;p&gt;Before Moniik most of our front-end code was in a single git repository and we created a CSS-only toolkit we called Goyav that we applied to project components as needed. &lt;/p&gt;

&lt;p&gt;This was a great first step towards outlining the components that would be most important to us in the future and thinking about how to make them generic enough for re-use. After several product launches and new partnerships however it became apparent that we were limited by a single repository.&lt;/p&gt;

&lt;p&gt;We decided to split our front-end code into multiple repositories which lead us to reconsider how we shared common design elements and UI behaviors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design principals
&lt;/h2&gt;

&lt;p&gt;The guiding principle was that of &lt;a href="https://bradfrost.com/blog/post/atomic-web-design/" rel="noopener noreferrer"&gt;atomic design&lt;/a&gt; and we built up the different stages in the atomic design method as separate files: atoms, molecules and organisms.&lt;/p&gt;

&lt;p&gt;We also decided to include an extra stage before the atoms: variables (also called &lt;a href="https://bradfrost.com/blog/post/extending-atomic-design/" rel="noopener noreferrer"&gt;Design Tokens&lt;/a&gt;). Colors, margins, borders, font-sizes are all set as overrideable SASS variables. These our the first step for customization for a partner.&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%2F91f4p1fd4sv188scs3ty.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%2F91f4p1fd4sv188scs3ty.png" alt="The Atomic Design pipeline"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Moniik doesn’t include template or page layout components, only some helpers. We were afraid that we would spend more time fighting against layout components, and reading their documentation than using the handful of helpers and variables you need for most layouts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting the ball rolling
&lt;/h2&gt;

&lt;p&gt;In the summer of 2021 we started testing several options for our design system. The only technical decision we made beforehand was to implement it with Vue.js (more on this later).&lt;/p&gt;

&lt;h3&gt;
  
  
  CSS Framework
&lt;/h3&gt;

&lt;p&gt;We wanted to hit the ground running so opted to go with an existing SASS/CSS framework. The two contenders we tested exhaustively were Tailwind and Bulma. In the end Bulma fit our use case the best giving us base classes and helpers to build our components, but also allowing us to re-export them easily without the extra build step required by Tailwind.&lt;/p&gt;

&lt;h3&gt;
  
  
  Choosing Vue.js.
&lt;/h3&gt;

&lt;p&gt;Vue.js SFCs (or Single File Components) and their surrounding ecosystem allow us to extend and override templates, scripts and styles in consumer projects with ease.&lt;/p&gt;

&lt;p&gt;Although everyone on the team has more or less experience with the increasingly industry-standard React, we decided to go with Vue.js for the following reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SFC’s allow us to swap-out or override styles quickly across a whole project&lt;/li&gt;
&lt;li&gt;Vue.js templating system allows us to override components on a name by name basis. This becomes &lt;em&gt;very&lt;/em&gt; useful when we want to replace a nested component. For example, we can swap out the &lt;code&gt;&amp;lt;TextInput&amp;gt;&lt;/code&gt; component that is depended on by the &lt;code&gt;&amp;lt;PhoneInput&amp;gt;&lt;/code&gt; component without reimplementing the whole &lt;code&gt;&amp;lt;PhoneInput&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Nuxt.js is a great framework and does exactly what we need. SSG, SSR and SPA rendering are all supported.&lt;/li&gt;
&lt;li&gt;Monisnap’s existing front-ends are written in Vue using Nuxt and everyone was satisfied with the experience so far.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, Vue.js also comes with its fair share of challenges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When we created Moniik (and arguably still today) Vue 2 is the most used version and until Nuxt 3 is released and stable we’ll be sticking with it, and missing out on many of the innovations of Vue 3.&lt;/li&gt;
&lt;li&gt;Typescript support for Vue 2 and &lt;code&gt;.vue&lt;/code&gt; files wasn’t stable enough to allow us to build with confidence. Notably absent at the time was a good solution for command line type checking and loading named exports. For this reason, and because of the relative simplicity of UI component logic compared to the rest of our front-end code, we decided to skip Typescript for now.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Developing Moniik
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Development environment
&lt;/h3&gt;

&lt;p&gt;Once we were set on technical choices, we built the development environment around the project.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Jest unit tests to avoid breaking changes in the components' public &lt;code&gt;props&lt;/code&gt; and &lt;code&gt;events&lt;/code&gt; API&lt;/li&gt;
&lt;li&gt;Jest snapshot tests allow us to detect and avoid unwanted changes in the rendered HTML.&lt;/li&gt;
&lt;li&gt;Storybook component tests &amp;amp; documentation. As static version of the storybook is also hosted on GitLab for easier references.&lt;/li&gt;
&lt;li&gt;A sandbox project to play with multiple components in ways Storybook does not offer.&lt;/li&gt;
&lt;li&gt;GitLab package registry publishing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Component development
&lt;/h3&gt;

&lt;p&gt;Over the next 8 weeks we developed the different components for the design system starting at the atomic level and working our way up to the organisms.&lt;/p&gt;

&lt;p&gt;The pipeline we created that each component went through is illustrated by this diagram:&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%2Fou5cvk5d09euqiquoezg.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%2Fou5cvk5d09euqiquoezg.png" alt="Component pipeline"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For each component we created a separate &lt;code&gt;.js&lt;/code&gt;, &lt;code&gt;.vue&lt;/code&gt; and &lt;code&gt;.scss&lt;/code&gt; files, Storybook and Jest test files and TypeScript &lt;code&gt;.d.ts&lt;/code&gt; declaration files.&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%2Fl0a35wp2b29oaaxxm4ca.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%2Fl0a35wp2b29oaaxxm4ca.png" alt="The Storybook page for the Button component"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After the intial HTML + SASS integration, we extracted most CSS property values to variables that can optionally be overriden in consumer projects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s1"&gt;'../../scss/variables'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Control&lt;/span&gt;
&lt;span class="nv"&gt;$text-field-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;58px&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;default&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$text-field-padding-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;default&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$text-field-icon-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;24px&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;default&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$text-field-spacing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;spacing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;default&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$text-field-font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;6&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;default&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Control Border&lt;/span&gt;
&lt;span class="nv"&gt;$text-field-border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$radius-small&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;default&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$text-field-border-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$grey-lighter&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;default&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  First projects
&lt;/h3&gt;

&lt;p&gt;Overlapping the end of the component development period, we started our first real project with Moniik. This was crucial to uncovering any issues that only arise when building products in the real world. We fixed some issues with tracking variables and some weird Nuxt compatibility bugs but integration was otherwise very smooth.&lt;/p&gt;

&lt;p&gt;In December 2021 we started our first partner project and took three days to add their look and feel to Moniik. This was mainly due to adding SASS variables to values we didn't initially expect to have to modify.&lt;/p&gt;

&lt;p&gt;In March 2022 we started our third partner project and with the increased maturity of Moniik’s code base, it took us a single afternoon to customize for our partner.&lt;/p&gt;

&lt;h2&gt;
  
  
  Moving forward
&lt;/h2&gt;

&lt;p&gt;Moving forward we have the following challenges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Better CSS loading: Within the given timeframe to develop the Design System we paid less attention to CSS performance than code clarity. There is a good amount of unused or redundant CSS that we would like to remove eventually.&lt;/li&gt;
&lt;li&gt;The move to Vue 3 &amp;amp; TypeScript. Like many, we are huge fans of TypeScript and would love to have a homogenous TS codebase across all our projects.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We’re really happy with how Moniik has turned out and the projects it has allowed us to implement. We’re looking forward to building many more over the coming years while progressively improving the developer experience. If you would like to help us, &lt;a href="https://www.welcometothejungle.com/en/companies/monisnap/jobs" rel="noopener noreferrer"&gt;take a look at our careers page&lt;/a&gt;&lt;/p&gt;

</description>
      <category>designsystem</category>
      <category>vue</category>
      <category>javascript</category>
      <category>sass</category>
    </item>
    <item>
      <title>💡 Vue Typescript State Management : We can do better than “isLoading” in 2022</title>
      <dc:creator>Julien</dc:creator>
      <pubDate>Tue, 04 Jan 2022 10:57:22 +0000</pubDate>
      <link>https://forem.com/monisnap/vue-typescript-state-management-we-can-do-better-than-isloading-in-2022-2n5d</link>
      <guid>https://forem.com/monisnap/vue-typescript-state-management-we-can-do-better-than-isloading-in-2022-2n5d</guid>
      <description>&lt;p&gt;Sorry for the clickbait title, but I needed your attention 👀 &lt;/p&gt;

&lt;p&gt;Have you ever encountered code like this one :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&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="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;info&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;errored&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;currencydecimal &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&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;mounted &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;axios&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;https://api.coindesk.com/v1/bpi/currentprice.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&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;bpi&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&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="nx"&gt;error&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;errored&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="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;loading&lt;/span&gt; &lt;span class="o"&gt;=&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;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;app&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Bitcoin Price Index&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;section&lt;/span&gt; &lt;span class="na"&gt;v-if&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"errored"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;We're sorry, we're not able to retrieve this information at the moment, please try back later&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;section&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;section&lt;/span&gt; &lt;span class="na"&gt;v-else&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;v-if&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"loading"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Loading...&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;
      &lt;span class="na"&gt;v-else&lt;/span&gt;
      &lt;span class="na"&gt;v-for&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"currency in info"&lt;/span&gt;
      &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"currency"&lt;/span&gt;
    &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;:
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"lighten"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;v-html&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"currency.symbol"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rate_float&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;currencydecimal&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;section&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It’s an example I found in the Vue documentation here &lt;a href="https://vuejs.org/v2/cookbook/using-axios-to-consume-apis.html#Dealing-with-Errors" rel="noopener noreferrer"&gt;https://vuejs.org/v2/cookbook/using-axios-to-consume-apis.html#Dealing-with-Errors&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What if you have multiple things that could load, do you add a &lt;code&gt;loading2&lt;/code&gt; variable ? 👀&lt;/p&gt;

&lt;p&gt;To solve this issue, you can use a variable for each async actions you have with this 4 “states” :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;IDLE : the user didn’t trigger the action yet&lt;/li&gt;
&lt;li&gt;WAITING : the action is ongoing&lt;/li&gt;
&lt;li&gt;ERROR : there was an error&lt;/li&gt;
&lt;li&gt;DONE : The action succeeded&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using an enum with the different states, and better naming, the code can be rewritten like this :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kr"&gt;enum&lt;/span&gt; &lt;span class="nx"&gt;AsyncState&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;IDLE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;IDLE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;WAITING&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;WAITING&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;ERROR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ERROR&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;DONE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DONE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&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="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;info&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

      &lt;span class="nx"&gt;AsyncState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;currentPriceLoadState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AsyncState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WAITING&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;filters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;currencydecimal &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&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;mounted &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;axios&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;https://api.coindesk.com/v1/bpi/currentprice.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&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;bpi&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;currentPriceLoadState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;AsyncState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DONE&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&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="nx"&gt;error&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;currentPriceLoadState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;AsyncState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ERROR&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;app&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Bitcoin Price Index&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;v-if&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"currentPriceLoadState === AsyncState.WAITING"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Loading...&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;section&lt;/span&gt; &lt;span class="na"&gt;v-else-if&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"currentPriceLoadState === AsyncState.ERROR"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;We're sorry, we're not able to retrieve this information at the moment, please try back later&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;section&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;section&lt;/span&gt; &lt;span class="na"&gt;v-else&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;v-for&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"currency in info"&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;:
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"lighten"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;v-html&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"currency.symbol"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rate_float&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;currencydecimal&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;section&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With better naming and this simple enum, you can almost cover all the use cases where you need to load one or multiple things and manage errors ✨&lt;/p&gt;

&lt;p&gt;If you want to manage different error messages, you can add another variable with an enum type like this :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kr"&gt;enum&lt;/span&gt; &lt;span class="nx"&gt;CurrentPriceLoadErrors&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;INVALID_CURRENCY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;INVALID_CURRENCY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;API_LIMIT_REACHED&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;API_LIMIT_REACHED&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;DEFAULT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DEFAULT&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;p&gt;Tell me in the comment section if you like this trick or not, and if you have an even better technique !&lt;/p&gt;

&lt;p&gt;And don’t forget to like and share this post if you liked it 💙&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>vue</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>DON’T PANIC! Averting the DDOS crisis in under 5 minutes with Cloudflare</title>
      <dc:creator>Maxence</dc:creator>
      <pubDate>Fri, 02 Jul 2021 09:42:22 +0000</pubDate>
      <link>https://forem.com/monisnap/don-t-panic-averting-the-ddos-crisis-in-under-5-minutes-with-cloudflare-10lh</link>
      <guid>https://forem.com/monisnap/don-t-panic-averting-the-ddos-crisis-in-under-5-minutes-with-cloudflare-10lh</guid>
      <description>&lt;p&gt;As your business gains traction, your success will inevitably bring its share of malicious actors who will try everything to breach your systems and considerably slow down your operations.&lt;/p&gt;

&lt;p&gt;One such threat are distributed denial-of-service (DDOS) attacks whereby the perpetrator floods a server with high network traffic loads coming from multiple sources, effectively disrupting the service and preventing legitimate users from accessing it.&lt;/p&gt;

&lt;p&gt;Startups and SMEs are particularly exposed because they seldom have the resources to prepare for the unexpected and the impact of something seen as unlikely is often underestimated if not completely overlooked.&lt;/p&gt;

&lt;p&gt;This guide will help you mitigate the risk in no time with a hassle-free, fully-managed and affordable solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Meet Cloudflare
&lt;/h2&gt;

&lt;p&gt;Security can be tricky and requires extreme meticulousness. A system is only as strong as its weakest component, and the slightest negligence can lead to disastrous consequences.&lt;/p&gt;

&lt;p&gt;Going managed can be a sensible choice if you are not positive you can handle security on your own. It ensures your infrastructure security is kept to the highest quality standards as new threats keep emerging.&lt;/p&gt;

&lt;p&gt;Cloudflare offers a battle-tested DDOS protection service &lt;a href="https://www.cloudflare.com/case-studies/"&gt;trusted by major companies throughout the world&lt;/a&gt;. The set-up can be done in a jiffy and their &lt;a href="https://www.cloudflare.com/plans/"&gt;pricing plans&lt;/a&gt; start with a free tier.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;Cloudflare acts as a gatekeeper between your server and the end-user trying to access it. It analyzes incoming requests and validates them against well-known threats and other inferences it has made over time from its experience of dealing with vast amounts of traffic worldwide.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--m9DTGxvI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sf1bdwo2dxr3hf5diay2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--m9DTGxvI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sf1bdwo2dxr3hf5diay2.png" alt="How Cloudflare works"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can choose how to deal with suspicious traffic, for example by presenting the user with a CAPTCHA or by blocking requests that match certain characteristics.&lt;/p&gt;

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

&lt;p&gt;When configured properly, transitioning to Cloudflare can be done without any downtime. Please make sure to understand every step perfectly and prepare a contingency plan before proceeding.&lt;/p&gt;

&lt;p&gt;You will need to have editor access to your Domain Name Server (DNS), typically found on your registrar’s dashboard. You may determine your registrar via &lt;a href="https://lookup.icann.org/"&gt;WHOIS&lt;/a&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Create a &lt;a href="https://dash.cloudflare.com/sign-up"&gt;Cloudflare account&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Once on the dashboard, proceed to &lt;strong&gt;adding your domain&lt;/strong&gt; by clicking on “Add a Site”.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Select the plan&lt;/strong&gt; that you deem best fit to your needs. DDOS protection is included in the free plan. &lt;a href="https://www.cloudflare.com/plans/"&gt;Other plans&lt;/a&gt; typically feature additional optimizations (caching, image compression etc) and more responsive support.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;List your DNS records&lt;/strong&gt; (in layman’s terms, instructions about how to handle requests passing through your domain). Clouflare will automatically try to scan your current configuration. However, please make sure that it matches the configuration you see on your DNS. Add missing records manually if needed. At this point the cloud icons should be greyed out, meaning Cloudflare is not yet proxying the traffic. You may skip the next screen about changing your nameserver for now; we’ll get back to it later.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---WevvJjI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8njo30jt7ehymjw4xsjh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---WevvJjI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8njo30jt7ehymjw4xsjh.png" alt="Listing DNS records"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;IMPORTANT - Before moving on to the next steps, make sure to &lt;strong&gt;whitelist IP addresses&lt;/strong&gt; necessary for the proper functioning of your service (typically those of other microservices or partners it needs to communicate with). Failing to do so might disrupt your services. To do so, head over to the “Firewall &amp;gt; Tools” section and add the set of IP addresses to allow.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7_85wFkw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/on1n0j7opmz0a5hlx5hg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7_85wFkw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/on1n0j7opmz0a5hlx5hg.png" alt="Whitelisting IP addresses"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now you’ll need to &lt;strong&gt;configure your DNS to point to the Cloudflare servers&lt;/strong&gt;. This step might vary depending on your registrar (or DNS provider), but the interface should look similar. For example on Namecheap, you will need to switch to “Custom DNS” and replace the existing nameservers with Cloudflare’s.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YsBAw3QV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xt8y6rurv4ac7br887p6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YsBAw3QV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xt8y6rurv4ac7br887p6.png" alt="Configuring a custom DNS on Namecheap"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cloudflare’s nameservers you should be pointed at can be found in the “Overview” section (the values may differ from the ones in the screenshot).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Dl00siNG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/83f1r8stzqgt1x9wbpqo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Dl00siNG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/83f1r8stzqgt1x9wbpqo.png" alt="Find Cloudflare's nameservers"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The new DNS configuration takes some time to propagate (from a few minutes up to a couple of hours depending on your registrar).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;At this point you’re ready to activate Cloudflare. Until now, traffic was rerouted directly onto your servers without any filtering. Turning on Cloudflare’s proxy on the DNS records you listed in step 4 will effectively activate the protection. This is the step where it all comes together. &lt;strong&gt;Make sure everything is correct&lt;/strong&gt; for the umpteenth time before proceeding. Once you are confident, head over to the “DNS” section and &lt;strong&gt;turn on the proxy on all the records&lt;/strong&gt;. Cloud icons should now appear orange, meaning the DNS record is properly proxied by Cloudflare. If anything were to go wrong at this stage, immediately turn off the proxy on every DNS record.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--e5jtlDfk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2irx2g2w00sflncnxgye.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--e5jtlDfk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2irx2g2w00sflncnxgye.png" alt="Turning on Cloudflare's proxy"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You are almost done. Until now, your server’s IP address was directly exposed to the public. Cloudflare adds a new layer on top of your server and obfuscates its IP address to new users. However the original IP address is still accessible, and Cloudflare cannot do anything to protect you from direct-access attacks. A good strategy is to &lt;strong&gt;configure your server to only accept requests forwarded by Cloudflare&lt;/strong&gt;. Additionally you might want to allocate a new IP address to your server for good measure.  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Congratulations! You made it through and &lt;strong&gt;your server is now protected against DDOS attacks&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Please let me know if you run into any problems or if you’re interested in diving deeper into the subject. Cloudflare offers many more features you might be interested in such as caching and compression which can improve performance and eventually save money.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>🚀 Flutter CI/CD using Git Tags in 5 minutes</title>
      <dc:creator>Julien</dc:creator>
      <pubDate>Fri, 14 May 2021 09:50:20 +0000</pubDate>
      <link>https://forem.com/monisnap/flutter-ci-cd-using-git-tags-in-5-minutes-5bb1</link>
      <guid>https://forem.com/monisnap/flutter-ci-cd-using-git-tags-in-5-minutes-5bb1</guid>
      <description>&lt;h2&gt;
  
  
  Table of contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;What you will need&lt;/li&gt;
&lt;li&gt;What you will do&lt;/li&gt;
&lt;li&gt;Step 1: Deployment script&lt;/li&gt;
&lt;li&gt;Step 2: Git push with tag&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What you will need &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;📲 A Flutter app (obviously)&lt;/li&gt;
&lt;li&gt;📦 A Github, Gitlab or Bitbucket account&lt;/li&gt;
&lt;li&gt;✨ A &lt;a href="https://codemagic.io/start/" rel="noopener noreferrer"&gt;Codemagic&lt;/a&gt; account (linked to your app repository)&lt;/li&gt;
&lt;li&gt;😁 Your best smile&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What you will do &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1a378jsuikoj6q03ilil.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1a378jsuikoj6q03ilil.png" alt="Alt Text" width="800" height="155"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;You will create a git tag containing your new app version, then push it to your repository. It will automatically trigger a Codemagic build, and release your app on the Play Store 🚀&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Create a deployment script 🛠 &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Below you'll find code to configure the CI/CD. You just have to add it to the root of your repository in a file named &lt;strong&gt;codemagic.yaml&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;I've used a script instead of the workflow editor (Codemagic GUI) for multiple reasons (versioned, faster...) but mainly because the version handling isn't possible using the editor.&lt;/em&gt;&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="c1"&gt;# codemagic.yaml&lt;/span&gt;

&lt;span class="c1"&gt;# ... &amp;lt;- Here you will include the "reusable" parts that are described afterward&lt;/span&gt;

&lt;span class="na"&gt;workflows&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;play-store&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Play Store Release&lt;/span&gt;
    &lt;span class="na"&gt;max_build_duration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;
    &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*caching&lt;/span&gt;

    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;flutter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*flutter_version&lt;/span&gt;
      &lt;span class="na"&gt;xcode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;latest&lt;/span&gt;
      &lt;span class="na"&gt;cocoapods&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
      &lt;span class="na"&gt;vars&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*gcp_service_credentials&lt;/span&gt;
        &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*keystore_release&lt;/span&gt;

    &lt;span class="c1"&gt;# ! THE IMPORTANT PART IS HERE !&lt;/span&gt;
    &lt;span class="na"&gt;triggering&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;events&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;tag&lt;/span&gt;
      &lt;span class="na"&gt;branch_patterns&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;master"&lt;/span&gt;
          &lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
          &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;tag_patterns&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*"&lt;/span&gt;
          &lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

    &lt;span class="na"&gt;scripts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="nv"&gt;*android_key_properties_setup&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="nv"&gt;*flutter_android_properties_setup&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="nv"&gt;*flutter_pub_get&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="nv"&gt;*flutter_test&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="nv"&gt;*flutter_build_play_store_release&lt;/span&gt;

    &lt;span class="na"&gt;artifacts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;      
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;build/**/outputs/**/*.aab&lt;/span&gt;

    &lt;span class="na"&gt;publishing&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;google_play&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*play_console_credentials&lt;/span&gt;
        &lt;span class="na"&gt;track&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;alpha&lt;/span&gt;
        &lt;span class="na"&gt;in_app_update_priority&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0&lt;/span&gt;      
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next are the reusable parts, to be clean and not repeat yourself ✨. Replace the encrypted variables using your credentials and the &lt;a href="https://docs.codemagic.io/building/encrypting/" rel="noopener noreferrer"&gt;Codemagic encrypting tool&lt;/a&gt;.&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="c1"&gt;# codemagic.yaml&lt;/span&gt;

&lt;span class="na"&gt;reusable&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;flutter_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;flutter_version&lt;/span&gt; &lt;span class="s"&gt;1.22.6&lt;/span&gt;

  &lt;span class="na"&gt;environment-variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;keystore_release&lt;/span&gt;
      &lt;span class="na"&gt;FCI_KEYSTORE_PATH&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/tmp/keystore.keystore&lt;/span&gt;
      &lt;span class="na"&gt;FCI_KEYSTORE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Encrypted(...)&lt;/span&gt;
      &lt;span class="na"&gt;FCI_KEYSTORE_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Encrypted(...)&lt;/span&gt;
      &lt;span class="na"&gt;FCI_KEY_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Encrypted(...)&lt;/span&gt;
      &lt;span class="na"&gt;FCI_KEY_ALIAS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Encrypted(...)&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;gcp_service_credentials&lt;/span&gt;
      &lt;span class="na"&gt;GCLOUD_SERVICE_ACCOUNT_CREDENTIALS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Encrypted(...)&lt;/span&gt;      

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;play_console_credentials&lt;/span&gt; &lt;span class="s"&gt;Encrypted(...)&lt;/span&gt;

  &lt;span class="na"&gt;scripts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;android_key_properties_setup&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Android - Setup key.properties&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;echo $FCI_KEYSTORE | base64 --decode &amp;gt; $FCI_KEYSTORE_PATH&lt;/span&gt;
        &lt;span class="s"&gt;cat &amp;gt;&amp;gt; "$FCI_BUILD_DIR/android/key.properties" &amp;lt;&amp;lt;EOF&lt;/span&gt;
        &lt;span class="s"&gt;storePassword=$FCI_KEYSTORE_PASSWORD&lt;/span&gt;
        &lt;span class="s"&gt;keyPassword=$FCI_KEY_PASSWORD&lt;/span&gt;
        &lt;span class="s"&gt;keyAlias=$FCI_KEY_ALIAS&lt;/span&gt;
        &lt;span class="s"&gt;storeFile=/tmp/keystore.keystore&lt;/span&gt;
        &lt;span class="s"&gt;EOF&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;flutter_android_properties_setup&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Flutter x Android - Setup local.properties&lt;/span&gt;
      &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;echo "flutter.sdk=$HOME/programs/flutter" &amp;gt; "$FCI_BUILD_DIR/android/local.properties"&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;flutter_pub_get&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Flutter - Get dependencies&lt;/span&gt;
      &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;flutter packages pub get&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;flutter_test&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Flutter - Run tests&lt;/span&gt;
      &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;flutter test --machine&lt;/span&gt;

    &lt;span class="c1"&gt;# ! THE IMPORTANT PART IS HERE !&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;flutter_build_play_store_release&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build AAB for Play Store release&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;GCLOUD_SERVICE_ACCOUNT_CREDENTIALS=$(echo $GCLOUD_SERVICE_ACCOUNT_CREDENTIALS | base64 --decode)&lt;/span&gt;
        &lt;span class="s"&gt;NEW_BUILD_NUMBER=$(($(google-play get-latest-build-number --package-name 'com.company.example') + 1))&lt;/span&gt;
        &lt;span class="s"&gt;NEW_VERSION_NAME=$(git describe --tags)&lt;/span&gt;

        &lt;span class="s"&gt;echo $NEW_VERSION_NAME&lt;/span&gt;
        &lt;span class="s"&gt;echo $NEW_BUILD_NUMBER&lt;/span&gt;

        &lt;span class="s"&gt;flutter build appbundle --build-name=$NEW_VERSION_NAME --build-number=$NEW_BUILD_NUMBER  --obfuscate --split-debug-info=$FCI_BUILD_DIR/debug_files&lt;/span&gt;

  &lt;span class="na"&gt;caching&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;caching&lt;/span&gt;
    &lt;span class="na"&gt;cache_paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;$HOME/.gradle/caches&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;$FLUTTER_ROOT/.pub-cache&lt;/span&gt;

&lt;span class="c1"&gt;# ... &amp;lt;- The workflow part described before should be here&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the previous script, the &lt;strong&gt;"flutter_build_play_store_release"&lt;/strong&gt; script handle the versioning :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Version&lt;/strong&gt; is retrieved from the tag.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build number&lt;/strong&gt; is retrieved from the play console (fetching the highest build number, and incrementing it for this release).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Don't forget to replace &lt;strong&gt;com.company.example&lt;/strong&gt; with your app package name&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You can now push this script onto your repository. It should then be visible from Codemagic and you should be able to trigger it manually.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Add git tag 🔖 -&amp;gt; push it 🚀 &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Creating a git tag is very easy. We will use 1.0.0 as our version, and tag.&lt;br&gt;
You just have to run the following commands :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git tag &lt;span class="nt"&gt;-a&lt;/span&gt; 1.0.0 &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Release 1.0.0"&lt;/span&gt;
git push origin 1.0.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it! You should see a build running in Codemagic 🏄‍♂️&lt;/p&gt;

&lt;h3&gt;
  
  
  What's next?
&lt;/h3&gt;

&lt;p&gt;You can add as many workflows as you want using Codemagic! You can use the GUI editor, and then extract to code to have more control over the code. &lt;/p&gt;

&lt;p&gt;Here we deploy on Alpha, so you can change to Production when you feel ready. And then, you can add an iOS workflow with mostly the same code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Leave a comment, a like or even a unicorn 🦄 if you've achieved this tutorial! 🚀🔖&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>git</category>
      <category>codemagic</category>
      <category>android</category>
    </item>
    <item>
      <title>Universal Apps on Serverless ? Yes.</title>
      <dc:creator>Jason</dc:creator>
      <pubDate>Wed, 08 Jul 2020 12:55:54 +0000</pubDate>
      <link>https://forem.com/monisnap/universal-apps-on-serverless-yes-1bm6</link>
      <guid>https://forem.com/monisnap/universal-apps-on-serverless-yes-1bm6</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;The serverless architecture might be one of the most trending tech paradigms nowadays.&lt;br&gt;
It has been popularized by AWS with its “AWS Lambda platform” and powers millions of APIs worldwide.&lt;/p&gt;

&lt;p&gt;We have been using it at &lt;a href="https://www.monisnap.com/fr"&gt;Monisnap&lt;/a&gt; for the past year on our micro-services architecture and it has been a blast !&lt;br&gt;
Now, imagine if you could also power your front end apps ( mostly Universal Apps ) with a serverless architecture ?&lt;br&gt;
Sounds great, right ?&lt;/p&gt;
&lt;h2&gt;
  
  
  Why serverless ?
&lt;/h2&gt;

&lt;p&gt;You might ask, “Why would I want to do that ? I could just use a static assets provider like Netlify or a good ol’ server running on a vm on the cloud ! ”.&lt;/p&gt;

&lt;p&gt;Yes you could but there are a few drawbacks :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A static assets provider is very useful when you have only a few pages to serve ( or do not care about SEO and just use SPAs ) but imagine if you have millions of generated pages, it is just not imaginable to generate all of them before deployment.&lt;/li&gt;
&lt;li&gt;Running on a server works and has been proven to be quite effective in the past ( and still is to some extent ) but we can do differently now and particularly way cheaper :).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The serverless paradigm gives us a lot of pros but also cons. I’m not gonna get into details about them as it is not the subject of this article but here is a &lt;a href="https://www.cloudflare.com/learning/serverless/why-use-serverless/"&gt;pretty good overview by Cloudflare&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can use any Front-end framework ( or an homemade one ) as long as it allows some kind of programmatic function to be plugged into a node framework / server ( e.g Express ).&lt;/p&gt;

&lt;p&gt;You also need your serverless provider to support returning binary types ( as we will be returning html, js , css, etc.. files.&lt;/p&gt;

&lt;p&gt;At Monisnap we use &lt;a href="https://nuxtjs.org/"&gt;NuxtJS&lt;/a&gt;, the &lt;a href="https://www.serverless.com/"&gt;Serverless&lt;/a&gt; framework and AWS lambda as our serverless provider.&lt;/p&gt;

&lt;p&gt;This is what we're going to cover today :)&lt;/p&gt;
&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;( I’m gonna assume you have at least NodeJS installed :) )&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;First we will need some dependencies to make it work on a serverless environments :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -S serverless-http express serverless-apigw-binary
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now we are going to init a new NuxtJS project ( Choose the options that suit your needs when asked ) :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx create-nuxt-app hello-world
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now cd into the directory :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd hello-world
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And then start the project :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You should see this if you go to &lt;a href="http://localhost:3000/"&gt;http://localhost:3000/&lt;/a&gt; on your web browser :)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--osp-Gncy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/i5j117jeqixcsvgnmnqj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--osp-Gncy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/i5j117jeqixcsvgnmnqj.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The 'Server' code
&lt;/h2&gt;

&lt;p&gt;Now to make it runnable on our “custom” server, we are going to create a folder “server” at the root of the project and create a new file “index.js” inside it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Monisnap/nuxtjs-serverless-sample/blob/master/server/index.js"&gt;index.js&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The configuration is pretty straightforward.&lt;/p&gt;

&lt;p&gt;We first import the required dependencies :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;path&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="nx"&gt;loadNuxt&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nuxt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;serverless&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;serverless-http&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;express&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;Then we init the express app :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Next we have the module.exports.nuxt which export the "handler" function that is going to be used by serverless.&lt;/p&gt;

&lt;p&gt;Inside this export we have the first app.use which links the static assets from the directory where the assets are built to the /_nuxt/ path ( this is the default path where Nuxt looks for the assets ) :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/_nuxt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.nuxt&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;dist&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;client&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;We then get the Nuxt instance ( start indicates that we want to use the production package ) :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nuxt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;loadNuxt&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And we tell express to use it for every routes via the nuxt.render method which allows us to render each route based on our routes params, pretty useful.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&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;nuxt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In the last part we use the serverless-http package which will wrap our Express app to be understandable by Lambda. We also need to pass some configuration options to allow any content-type we wish to serve with our application :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;serverless&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="p"&gt;{&lt;/span&gt;
   &lt;span class="na"&gt;binary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
     &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/javascript&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/octet-stream&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/xml&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;font/eot&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;font/opentype&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;font/otf&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image/jpeg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image/png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image/svg+xml&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text/comma-separated-values&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text/css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text/html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text/javascript&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text/plain&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text/text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text/xml&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;font/woff&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;font/woff2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
   &lt;span class="p"&gt;]&lt;/span&gt;
 &lt;span class="p"&gt;})(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And this is it for the server code ! ( pretty easy right ? :))&lt;/p&gt;

&lt;h2&gt;
  
  
  The Serverless part
&lt;/h2&gt;

&lt;p&gt;Now let’s focus on the Serverless part.&lt;br&gt;
Create a the root of the directory a file named serverless.yml.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Monisnap/nuxtjs-serverless-sample/blob/master/serverless.yml"&gt;serverless.yml&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a pretty basic Serverless configuration :&lt;/p&gt;

&lt;p&gt;First we define the service name ( used by lambda and API Gateway ) :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hello-world&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then we have the provider specific configurations :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The provider name ( AWS in our case ),&lt;/li&gt;
&lt;li&gt;The nodejs runtime we want our code to run on&lt;/li&gt;
&lt;li&gt;The AWS lambda region where our lambdas will we provided
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws&lt;/span&gt;
  &lt;span class="na"&gt;runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nodejs12.x&lt;/span&gt;
  &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;eu-west-1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We then have a custom variable set called apigwBinary which will be used by the plugin serverless-apigw-binary to let API Gateway know that we want to support binaries as responses.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;custom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;apigwBinary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*/*"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The next block define our function and the associated http events, we set two event, one will be handling all the requests from the root path and the other any additional path we want to support.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;functions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;nuxt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;server/index.nuxt&lt;/span&gt;
    &lt;span class="na"&gt;memorySize&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;512&lt;/span&gt;
    &lt;span class="na"&gt;events&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
          &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;any&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/{proxy+}&lt;/span&gt;
          &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;any&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And finally we tell Serverless we want to use the serverless-apigw-binary plugin :)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
 &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;serverless-apigw-binary&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Before deploying the app, you need to have an account ( you can create one for free &lt;a href="https://aws.amazon.com/resources/create-account/"&gt;here&lt;/a&gt;) and &lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html"&gt;set up&lt;/a&gt; the AWS cli tool ( you can use the free tier with lambdas ) &lt;/p&gt;

&lt;p&gt;Once you’ve configured the cli tool, you are ready to build and deploy the app :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run build &amp;amp;&amp;amp; sls deploy
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And you can now hit the url shown in the console output and you should see your Nuxt app appearing :)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://&amp;lt;api-gateway-id&amp;gt;.execute-api.&amp;lt;region&amp;gt;.amazonaws.com/dev/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;There is a little caveat though... If you check your web browser console you should see a lot of console errors related to the loading of resources.&lt;br&gt;
Nuxt tries to load them from the /_nuxt/ path but API gateway exposes the lambda with a “stage” prefix ( e.g dev ), so obviously it does not find them.&lt;/p&gt;

&lt;p&gt;To effectively fix this problem, you need to add a custom domain name to API gateway ( You will need a domain name and a tls certificate ) which will allow you to serve the app from the root of your domain.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Serverless allows us to compile and serve our Nuxt application at a very low cost and with no worries about scalability ( among other benefits ).&lt;br&gt;
Of course it comes with some drawbacks which can easily be overcome.&lt;/p&gt;

&lt;p&gt;Drop a comment if this has been useful or if you have any questions :)&lt;/p&gt;

&lt;p&gt;Have a good one !&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>serverless</category>
      <category>aws</category>
      <category>vue</category>
    </item>
    <item>
      <title>5 min TypeScript NPM package</title>
      <dc:creator>Jason</dc:creator>
      <pubDate>Fri, 14 Feb 2020 15:40:41 +0000</pubDate>
      <link>https://forem.com/monisnap/5-min-typescript-npm-package-4ce4</link>
      <guid>https://forem.com/monisnap/5-min-typescript-npm-package-4ce4</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;As developers we might want to create a new framework or package reusables components, functions, etc...&lt;br&gt;
Hopefully as JavaScript developers we have a NPM !&lt;/p&gt;

&lt;p&gt;We may be also using &lt;a href="https://www.typescriptlang.org/"&gt;TypeScript&lt;/a&gt; to add a layer of safety on top of Javascript plus a more advanced OOP design&lt;/p&gt;

&lt;p&gt;So what about we create a NPM package which will be available to&lt;br&gt;
JavaScript projects but most importantly Typescript projects ?&lt;/p&gt;

&lt;p&gt;Let's go and let's see how we do it at &lt;a href="https://www.monisnap.com/fr"&gt;Monisnap&lt;/a&gt; !&lt;/p&gt;
&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;( I assume you have NodeJS and Typescript installed )&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;First create a new folder and open a terminal tab and type :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm init -y
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;em&gt;This will basically initialize your npm package by creating a package.json with some default options ( we will get back to that later )&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;and&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tsc --init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;em&gt;This also initialize the project to use TypeScript by creating a tsconfig.json which holds important options defining how your TypeScript code will be handled.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So now you should have this&lt;/strong&gt; :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yBMqsJGG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/dw6dor5u8fut5zndddi0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yBMqsJGG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/dw6dor5u8fut5zndddi0.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Good ? next !&lt;/p&gt;

&lt;p&gt;Now we can write some code :)&lt;/p&gt;

&lt;p&gt;Create a "src" folder and two files inside it "index.ts" and "unicorn.ts" ( yes I like unicorns )&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/monisnap-jason/unicorn-says-hello-world/blob/master/src/unicorn.ts"&gt;unicorn.ts&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;Unicorn&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;sayHelloTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`🦄 Hello &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; !`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://github.com/monisnap-jason/unicorn-says-hello-world/blob/master/src/index.ts"&gt;index.ts&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./unicorn&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;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Kj7-qt2z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/yxsql9mxwc4fxxcm042c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Kj7-qt2z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/yxsql9mxwc4fxxcm042c.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We now need to edit the &lt;a href="https://github.com/monisnap-jason/unicorn-says-hello-world/blob/master/tsconfig.json"&gt;tsconfig.json&lt;/a&gt; ( copy / paste the following )&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"compilerOptions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"declaration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"strictNullChecks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"es5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; 
    &lt;/span&gt;&lt;span class="nl"&gt;"outDir"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dist"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"commonjs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"moduleResolution"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sourceMap"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"lib"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"es2015"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dom"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"rootDir"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"src"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"include"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"src"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"exclude"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"node_modules"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dist"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;let's cover the important options :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;declaration:&lt;/strong&gt; It tells typescript to generate the typings ( important if we want some "automatic" docs for our code&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;target:&lt;/strong&gt; specify the ES target version ( I chose ES5 here because I target nodeJS )&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;outDir:&lt;/strong&gt; The compiled files destination&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;module / module resolution:&lt;/strong&gt; I use commonJS / nodeJS as its the module system on NodeJS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;sourceMap:&lt;/strong&gt; Important if you want the source map to be able to debug Typescript code directly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;rootDir:&lt;/strong&gt; The root folder where our code is.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now we can edit the &lt;a href="https://github.com/monisnap-jason/unicorn-says-hello-world/blob/master/package.json"&gt;package.json&lt;/a&gt; :&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"unicorn-says-hello-world"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"A unicorn that says hello world"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dist/index.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"types"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dist/index.d.ts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"files"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"dist"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tsc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"prepare"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npm run build"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"repository"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"git"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"git+https://github.com/monisnap-jason/unicorn-says-hello-world.git"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"keywords"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"author"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"monisnap-jason"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"license"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ISC"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Again the important options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;name:&lt;/strong&gt; the name of your package on NPM&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;main:&lt;/strong&gt; the entry point ( our code will be compiled in the dist folder )&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;types:&lt;/strong&gt; the path of our code typings&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;files:&lt;/strong&gt; The files we want to include in our package&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;scripts:&lt;/strong&gt; 

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;build:&lt;/strong&gt; tsc to compile our code&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;prepare:&lt;/strong&gt; this is a NPM hook which executes a command before publishing to 
npm ( we tell it the execute the build command above )&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;repository:&lt;/strong&gt; the options about the repository which holds the package code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We also need a &lt;a href="https://github.com/monisnap-jason/unicorn-says-hello-world/blob/master/.gitignore"&gt;.gitignore&lt;/a&gt; file ( as we don't want to include some folders into our repository ):&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;\dist
\node_modules
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And lastly you can create &lt;a href="https://github.com/monisnap-jason/unicorn-says-hello-world/blob/master/README.md"&gt;README.md&lt;/a&gt; to tell the world how to use your package.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We're almost done !&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now the final touch :&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm publish
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;you should see this in your terminal output : &lt;br&gt;
"+ &lt;a href="mailto:your-package-name@1.0.0"&gt;your-package-name@1.0.0&lt;/a&gt;"&lt;/p&gt;

&lt;p&gt;And voilà your package is on &lt;a href="https://www.npmjs.com/"&gt;NPM&lt;/a&gt;&lt;br&gt;
&lt;em&gt;here is mine for reference &lt;a href="https://www.npmjs.com/package/unicorn-says-hello-world"&gt;unicorn-says-hello-world&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now if I want to use my brand new package I just to need &lt;em&gt;npm install unicorn-says-hello-world&lt;/em&gt; in a new or existing project, and I can use it like that :&lt;/p&gt;


&lt;div class="ltag__replit"&gt;
  &lt;iframe height="550px" src="https://repl.it/@monisnapjason/NextRadiantTests?lite=true"&gt;&lt;/iframe&gt;
&lt;/div&gt;



&lt;p&gt;Drop a comment below if you have any questions !&lt;/p&gt;

&lt;p&gt;Have a good one :)&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>npm</category>
      <category>javascript</category>
      <category>github</category>
    </item>
    <item>
      <title>🔎🔍  AWS Cloudwatch - Top 5 things you need to know</title>
      <dc:creator>Edouard Le Juge</dc:creator>
      <pubDate>Fri, 31 Jan 2020 16:35:54 +0000</pubDate>
      <link>https://forem.com/monisnap/aws-cloudwatch-top-5-things-you-need-to-know-1gik</link>
      <guid>https://forem.com/monisnap/aws-cloudwatch-top-5-things-you-need-to-know-1gik</guid>
      <description>&lt;p&gt;If you can rely on your logging, monitoring and alerts you'll be free to code fearless, how does that look?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/iHgznkhwC6XYs/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/iHgznkhwC6XYs/giphy.gif" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At &lt;a href="https://www.monisnap.com" rel="noopener noreferrer"&gt;Monisnap&lt;/a&gt; our entire backend is built using AWS features like API Gateway, Lambdas, SQS, SNS... It made sense for us to use Cloudwatch as our monitoring system. In this article I will share with you a few tricks I use within cloudwatch. Nothing fancy at all, just the basics, enough to feel the power of cloudwatch and find what you are looking for.&lt;/p&gt;

&lt;h1&gt;
  
  
  📖 Search through your logs in cloudwatch
&lt;/h1&gt;

&lt;p&gt;Before we dig in the log search itself I would like to advice any of you to always log JSON objects instead of plain text. You can grep for texts in strings but you loose the power of more specific filtering, grouping or sorts that will get your logging game to the next level.&lt;/p&gt;

&lt;h2&gt;
  
  
  📃 Search a specific log group
&lt;/h2&gt;

&lt;p&gt;If you go to a specific log group, either a log group that you created yourself or from a specific lambda, click on the 'Search Log group' button. You will now be able to search in all the log streams within this log group. &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-images.s3-eu-west-1.amazonaws.com%2Fcloudwatch%2Fimage_one.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-images.s3-eu-west-1.amazonaws.com%2Fcloudwatch%2Fimage_one.jpg" alt="search group"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's now dive into how to filter those logs.&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-images.s3-eu-west-1.amazonaws.com%2Fcloudwatch%2Fimage_two.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-images.s3-eu-west-1.amazonaws.com%2Fcloudwatch%2Fimage_two.jpg" alt="search box"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ✅ Find specific value, or filter out a specific value
&lt;/h2&gt;

&lt;p&gt;Looking for ERROR lines? You could just type ERROR in the search bar. But, as a reflex I always use the double quotes "ERROR", in case the value I am looking for has a '-' or any possible interpreted character. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    "ERROR"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;What will the '-' could do to my search? It would filter it out. If you don't want all the 'INFO' logs, you could simply do this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    -"INFO"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  ✅✅ Find specific values (AND)
&lt;/h2&gt;

&lt;p&gt;You now want all the ERRORs from the requestId 'c8ae6b2c-1750-4f5d-bfda-14403cb978a0' but without the ones from a 'NotImportantException'. In that case you can just put the seraching operation one after the other:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"ERROR" "c8ae6b2c-1750-4f5d-bfda-14403cb978a0" -"NotImportantException"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  ✅🔘 Find one of the values (OR)
&lt;/h2&gt;

&lt;p&gt;If you want to have all the logs with ERROR or WARN, you can use the question mark as a OR operator. In our case this search would like like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;?"ERROR" ?"WARN"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: I haven't find a way to filter using both AND and OR together. If you know how, please leave your trick in the comments section below and I will add it to my article so the entire community can benefit from your wiseness :)&lt;/p&gt;

&lt;h2&gt;
  
  
  ⎨⎬ Search on JSON attributes
&lt;/h2&gt;

&lt;p&gt;If, as a smart logger, you are using JSON objects and not plain text you can now apply those filters on specific attributes of your object using the {$.} notation. {} means that you will be searching on the object itself and $. is the root of your logged object. the OR operator will now be '||', and, you guessed it, the AND will be '&amp;amp;&amp;amp;''&lt;/p&gt;

&lt;p&gt;Example: If you have the following logs:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    A {"line": 'first', "type": "1"}
    B {"line": 'second', "type": "2"}
    C {"line": 'third' , "type": "3"}  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Let's see some example here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{$.line="first" || $.line="second"}
 -&amp;gt; Returns line A and B.

{$.type&amp;gt;2}          
 -&amp;gt; Returns line C. 

{$.line="first" &amp;amp;&amp;amp; $.type!=3}
-&amp;gt; Returns line A.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I will let you play with this and query for more complex object with a list maybe? Something in that taste:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{$.type.subtype[0].value=1}&amp;gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  🕵️‍♀️ Insights
&lt;/h1&gt;

&lt;p&gt;Now that you are able to find logs in a specific log group, let's explore the power of the Insight feature that AWS offers. Insight let you run search among several log groups in a query like fashion. Yes, you will now be able to sort, group and all. &lt;/p&gt;

&lt;p&gt;First of all, go to you cloudwatch UI, click on Insight menu on the left of the screen. You will end up with an empty log group bar and a basic query. &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%2Fi%2Fbpn0425rwdfbkwn2mrgi.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fbpn0425rwdfbkwn2mrgi.jpg" alt="Insight home"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add the log group(s) you want to run query on. A specific lambda is totally fine for now. Once you have selected the log group(s) you want to run queries on, you will be able to see on the right side of your screen all the commands and fields that you can play out with. &lt;br&gt;
I will, in this article, only go through the basic ones. We just want to get a taste of what are the possibilties here. For now, we will only look into the commands: fields, filter, sort, and limit. I invite you to look into stats and parse though, very powerful features !!&lt;/p&gt;

&lt;p&gt;All the commands that you will add in the query will be treated one after the other. It's a suite of pipe operations.&lt;/p&gt;
&lt;h2&gt;
  
  
  📝 Fields
&lt;/h2&gt;

&lt;p&gt;It kind of speaks for itself: the list of fields you want to have the value of in the output of the pipe operation. If you want to see all the fields you have access to, run the basic query that Insight gives you and you will see a lot more in the Discovered fields on the right side of the screen.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fields @timestamp, @message, @requestId
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🛃 Filter
&lt;/h2&gt;

&lt;p&gt;Pretty obvious as well, this will filter the logs as needed. If you want all the logs of the requestId '':&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fields @timestamp, @message
    | filter @requestId='47dbd193-c8ad-47b5-8668-6045668ce472' 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Among those logs, only the line with our own logType set to WARN OR ERROR are interesting to you? You can use the 'like' operator to apply a new filter on the result of the previous pipe operation. 'like' operator uses the regex synthax to filter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fields @timestamp, @message
    | filter @requestId='47dbd193-c8ad-47b5-8668-6045668ce472' 
    | filter logType like /(ERROR|WARN)/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🔻 Sort
&lt;/h2&gt;

&lt;p&gt;If you want to see the latest ERRORs that happend on this (or these) log group(s) you can apply a sort operation to the mix and use it on timestamp field. ASC and DESC sort types are allowed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fields @timestamp, @message
    | filter logType like /ERROR/
    | sort @timestamp desc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  ⇥ Limit
&lt;/h2&gt;

&lt;p&gt;Too many results? limit the number of results you want in you result by using the limit operator and only show the last 10 ones:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fields @timestamp, @message
    | filter logType like /ERROR/
    | sort @timestamp desc
    | limit 10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  👨‍🚒 Dashboards
&lt;/h1&gt;

&lt;p&gt;Cloudwatch also gives you the ability to gather in one place all the monitoring you feel you should be keeping an eye on. Those dashboards can, not only contain result from log queries that we saw in the section above but also aggregate values of operations on your API gateway, SQS queues, S3 files and much more.&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%2Flearn2flyct.com%2Fwp-content%2Fuploads%2F2019%2F02%2FSteam-Sim-Pic.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%2Flearn2flyct.com%2Fwp-content%2Fuploads%2F2019%2F02%2FSteam-Sim-Pic.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In our team we have several type of dashboards: &lt;/p&gt;

&lt;h2&gt;
  
  
  👨‍⚕️ Regular monitoring
&lt;/h2&gt;

&lt;p&gt;For health monitoring purpose we have a few general dashboards, general monitoring showing 2XX, 4XX, 5XX and other HTTP codes we want to monitor. A tailing of the latest errors. Anything that could give us a feel of what is going on on our system. And what would show us that something is odd right away. Nothing fancy, just enough to notice and diagnose where the blood is coming from if we have alerts firing.&lt;/p&gt;

&lt;h2&gt;
  
  
  👩‍🔧 Specific features monitoring
&lt;/h2&gt;

&lt;p&gt;For any impactful feature we try our best to add graphs or tailing logs that can monitor the impact of the feature by tracking a specific KPI. It can totally be a specific log nomenclature that we can track for a certain amount of time. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On a recurring error from a partner we added the specific log "error 0234 occurred"&lt;/li&gt;
&lt;li&gt;We created a query that populate a graph based on it&lt;/li&gt;
&lt;li&gt;We confirmed based on this that the impact of this specific error was pretty bad&lt;/li&gt;
&lt;li&gt;We implemented the ability to recover from that issue &lt;/li&gt;
&lt;li&gt;We added in our code a new log saying "recovered from error 0234"&lt;/li&gt;
&lt;li&gt;Created a new query to monitor this implementation &lt;/li&gt;
&lt;li&gt;We are now able to monitor how many requests got recovered from this specific error or not.&lt;/li&gt;
&lt;li&gt;close the bug if the fix stopped our problem&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  🚨 Alarms
&lt;/h1&gt;

&lt;p&gt;Last but not least in our article is the ability to set Alerts using the very helpful Alarms feature Cloudwatch gives us. You can set Alerts on any of your AWS resources: API gateway, SQS, lamdbas and many more. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/gBW8Qgfaa2ije/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/gBW8Qgfaa2ije/giphy.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ⏰ Create an alarm
&lt;/h2&gt;

&lt;p&gt;To add an Alert simply go to the Alarms menu on the left side menu. Click on the 'create alarm' red button.&lt;/p&gt;

&lt;h3&gt;
  
  
  📈Choose your metric
&lt;/h3&gt;

&lt;p&gt;You will then be asked to select the metric(s) on which you want your alarm to be set on. A metric is usually a combinaison of an object (api gateway endpoint, lambda function, sqs topic...) and an operation on it (sum of execution, sum of errors, number of deletion, average duration...) &lt;/p&gt;

&lt;p&gt;You might have to apply your alerts based on a duration type on a period, per second, per minutes, per 5 minutes...&lt;/p&gt;

&lt;h3&gt;
  
  
  📛Threshold type
&lt;/h3&gt;

&lt;p&gt;Two type of threshold can then be applied: a static number reached, or an anomaly gets detected. We only have used the static alerts for now and it is quite straight forward, you set the value above/under which you want to receive an alert. The anomaly type applies an algorithm on how the metric you used is evolving through time. If the value evolves in what the algorithm find abnormal it will trigger your notification. Feel free to share your thoughts and experience of this feature in the comments if you have been playing with this. It would be much appreciated !!!&lt;/p&gt;

&lt;h3&gt;
  
  
  ☎ Action
&lt;/h3&gt;

&lt;p&gt;Here you can select how you want to be notified. We usually use a SNS topic which will be sending an email to our developers email address. But it is up to you to set up as you see fit.&lt;/p&gt;

&lt;h3&gt;
  
  
  🤓 Review and confirm
&lt;/h3&gt;

&lt;p&gt;You will have to simply set the name and description of your alarm. click confirm and you are good to go !&lt;/p&gt;

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

&lt;p&gt;We only scratch the surface of what Cloudwatch can offer in this article but I think we covered here all the parts that you have to know for a basic monitoring and debugging skills.&lt;br&gt;
I hope it helped you having an overview of what AWS gives you in the monitoring world. We have been using it for 6 months at Monisnap and haven't been blocked by anything serious so far. Hope you'll be using it soon, it's a great tool box to play with !!!!&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloudwatch</category>
      <category>tutorial</category>
      <category>monitoring</category>
    </item>
    <item>
      <title>Hide your f🤬🤬king API keys and credentials from versioned code</title>
      <dc:creator>Jonathan BROSSARD</dc:creator>
      <pubDate>Fri, 24 Jan 2020 11:09:02 +0000</pubDate>
      <link>https://forem.com/monisnap/hide-your-f-king-api-keys-and-credentials-from-versioned-code-183p</link>
      <guid>https://forem.com/monisnap/hide-your-f-king-api-keys-and-credentials-from-versioned-code-183p</guid>
      <description>&lt;p&gt;As a developer, you deal every day with API keys, passwords, credentials, tokens etc... and you &lt;strong&gt;do NOT&lt;/strong&gt; want to share them. &lt;/p&gt;

&lt;p&gt;Here are the different ways to handle them : &lt;/p&gt;

&lt;p&gt;1 - A versioned settings file with secrets in it. &lt;br&gt;
&lt;strong&gt;&lt;em&gt;If you do that, please continue to read this post, internets need that.&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://i.giphy.com/media/d10dMmzqCYqQ0/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/d10dMmzqCYqQ0/giphy.gif" width="400" height="226"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;2 - A non versioned settings file. &lt;br&gt;
&lt;strong&gt;&lt;em&gt;Better ! But when you'll onboard developers, it will be funny to check how you'll send them these values.&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://i.giphy.com/media/B37cYPCruqwwg/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/B37cYPCruqwwg/giphy.gif" width="200" height="113"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;3 - Environments variables (the classic &lt;strong&gt;&lt;em&gt;.env&lt;/em&gt;&lt;/strong&gt;) ! &lt;br&gt;
&lt;strong&gt;&lt;em&gt;Yeah ! Even better. Once again, how your future team members will have their own, by copy pasting yours ?&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://i.giphy.com/media/5wWf7H89PisM6An8UAU/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/5wWf7H89PisM6An8UAU/giphy.gif" width="443" height="250"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;4 - Store your secret into a secret management service !!!&lt;br&gt;
&lt;strong&gt;&lt;em&gt;Yeah ! OK, let's see how to do so&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://i.giphy.com/media/lMVNl6XxTvXgs/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/lMVNl6XxTvXgs/giphy.gif" width="320" height="181"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are several secrets management tools, but, I'll talk about the one I know best, because this is the one we're using at &lt;a href="https://www.monisnap.com"&gt;Monisnap&lt;/a&gt; : &lt;strong&gt;AWS Secret Manager&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  What is AWS Secret Manager ?
&lt;/h3&gt;

&lt;p&gt;AWS Secrets Manager is a secrets management service which enables you to easily rotate, manage, and retrieve credentials, API keys, or other secrets. &lt;br&gt;
Using Secrets Manager, you can secure, audit, and manage secrets used to access your resources.  &lt;/p&gt;

&lt;p&gt;You'll now be able to share your code (every file, every line), without any fear. Indeed, in your code, there will only be specific strings which describes your secrets, but not the secrets values themself.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Before Secrets Manager&lt;/th&gt;
&lt;th&gt;After Secrets Manager&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;db-name.cluster-cifkjshyfli1p.eu-west-2.rds.amazonaws.com.&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DB_HOST&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;username&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DB_USER&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;password&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DB_PASSWORD&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Security
&lt;/h3&gt;

&lt;p&gt;AWS Secrets Manager automatically &lt;strong&gt;rotates&lt;/strong&gt; your secrets. Your teammates or anyone else who clone/fork your code &lt;strong&gt;can have access without any knowledge&lt;/strong&gt; on what are the secrets values. &lt;/p&gt;

&lt;p&gt;You only need to manage ACL via AWS IAM.&lt;/p&gt;

&lt;p&gt;And so, for instance, your seniors developers can have access through their IAM roles and create/edit/update/delete new secrets, and interns can't.&lt;/p&gt;
&lt;h3&gt;
  
  
  Usage
&lt;/h3&gt;

&lt;p&gt;For every AWS Cloud based infrastructure, all you need to do is to grant access to the secrets.&lt;/p&gt;

&lt;p&gt;Our MicroServices infrastructure is built on Serverless lambdas functions, so we just have to add the rights IAM roles to our lambdas.&lt;/p&gt;

&lt;p&gt;Also, you can easily split them by environments.&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;provider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;
  &lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;nodejs10&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;
  &lt;span class="nx"&gt;stage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;stage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dev&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nl"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;eu&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;west&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;

  &lt;span class="nx"&gt;iamRoleStatements&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;Role&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;using&lt;/span&gt; &lt;span class="nx"&gt;AWS&lt;/span&gt; &lt;span class="nx"&gt;Secret&lt;/span&gt; &lt;span class="nx"&gt;Manager&lt;/span&gt;
    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;Effect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Allow&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="nx"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;secretsmanager:GetSecretValue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="nx"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;:&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="nl"&gt;self&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;custom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;jarvisAdminPassword&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;self&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stage&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;

  &lt;span class="nl"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nl"&gt;JARVIS_ADMIN_PASSWORD&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;self&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;custom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;jarvisAdminPassword&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;self&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stage&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;

&lt;span class="nl"&gt;custom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="nl"&gt;stage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;${opt:stage, self:provider.stage}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

  &lt;span class="nx"&gt;jarvisAdminPassword&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;local_jarvis_admin_password_secrets_key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="nx"&gt;dev&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dev_jarvis_admin_password_secrets_key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="nx"&gt;staging&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;staging_jarvis_admin_password_secrets_key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="nx"&gt;prod&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;prod_jarvis_admin_password_secrets_key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An extra cool thing about secrets: if you need to update your database accesses, an API key or any secret value you can just update the secret value into your Secret Manager, and every services that are using it will be automatically updated :) &lt;/p&gt;

&lt;p&gt;Hope it helps !&lt;/p&gt;

</description>
      <category>aws</category>
      <category>security</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>⏱ 5 min chrono FREE admin panel with ForestAdmin</title>
      <dc:creator>Julien</dc:creator>
      <pubDate>Fri, 17 Jan 2020 11:15:17 +0000</pubDate>
      <link>https://forem.com/monisnap/5-min-chrono-admin-panel-with-forestadmin-6-months-later-583</link>
      <guid>https://forem.com/monisnap/5-min-chrono-admin-panel-with-forestadmin-6-months-later-583</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Product guy :&lt;/strong&gt; Hey, could you give me access to database ?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tech guy :&lt;/strong&gt; Hmm.. why ?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Product guy :&lt;/strong&gt; To avoid to bother you each time I need to update something&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tech guy :&lt;/strong&gt; So, you would need a BackOffice for that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Product guy :&lt;/strong&gt; Ok, could you please develop it ?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You probably already had this discussion...&lt;br&gt;
Let's imagine now that you can solve it ... in MINUTES !&lt;/p&gt;

&lt;h1&gt;
  
  
  What is &lt;a href="https://www.forestadmin.com/" rel="noopener noreferrer"&gt;ForestAdmin&lt;/a&gt; 🌲 ?
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;"Forest Admin provides an off-the-shelf admin panel based on a highly-extensible API hosted on your servers."&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In other words, it's a SaaS platform providing all common admin tasks you'll need as developer if you want to create your own Back Office.&lt;/p&gt;

&lt;p&gt;With Forest you can :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📝 View / Create / Edit / Delete entries of your database&lt;/li&gt;
&lt;li&gt;🔐 Handle access control list&lt;/li&gt;
&lt;li&gt;📈 Create awesome dashboards and views with 0 or few code lines.&lt;/li&gt;
&lt;li&gt;🌍 Create custom actions that triggers your backend API&lt;/li&gt;
&lt;li&gt;✨ And many more !&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of this only with configurations and few lines of Javascript 👨‍💻!&lt;/p&gt;

&lt;p&gt;Here is an example of what it looks like out of the box :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.forestadmin.com%2Fpublic%2Fimg%2Fillustrations-dev%2Fscreens%2Fcrud.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.forestadmin.com%2Fpublic%2Fimg%2Fillustrations-dev%2Fscreens%2Fcrud.jpg" alt="OutOfTheBox" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Create your account for FREE at &lt;a href="https://www.forestadmin.com/" rel="noopener noreferrer"&gt;https://www.forestadmin.com/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Follow the instructions provided to connect to your database*&lt;/li&gt;
&lt;li&gt;Install in local using Docker or Node.js&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;npm start&lt;/code&gt; 🚀&lt;/li&gt;
&lt;li&gt;Go to &lt;a href="http://app.forestadmin.com/projects" rel="noopener noreferrer"&gt;http://app.forestadmin.com/projects&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;*&lt;em&gt;For the time being, you can only connect to Mysql, PostgreSQL, SQL Server and MongoDB.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Deployment
&lt;/h2&gt;

&lt;p&gt;You can host your server &lt;strong&gt;anywhere you want&lt;/strong&gt; using &lt;strong&gt;Docker&lt;/strong&gt; or &lt;strong&gt;Node.js&lt;/strong&gt;. For example, Forest provide a tutorial to host your server on Heroku &lt;a href="https://docs.forestadmin.com/documentation/how-tos/deploy-to-production-on-heroku" rel="noopener noreferrer"&gt;here&lt;/a&gt; (it takes really 3 minutes !)&lt;/p&gt;

&lt;h2&gt;
  
  
  Customization
&lt;/h2&gt;

&lt;p&gt;You have 2 categories of customization :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Model&lt;/strong&gt; : You can modify your database model, relationships and custom actions (called "smart actions") using Javascript !&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UI&lt;/strong&gt; : That's the best part for developers. You can modify your UI using the GUI layout editor provided by forest admin (based on &lt;a href="https://emberjs.com/" rel="noopener noreferrer"&gt;ember.js&lt;/a&gt;) &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here are some examples :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvrw5yqtxpc81dzeewjsp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvrw5yqtxpc81dzeewjsp.png" alt="Admin panel" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can even develop your own UI with HTML/CSS/JS code to have custom views like this one :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3wa8ae0rurn0r79jys08.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3wa8ae0rurn0r79jys08.png" alt="Admin panel" width="800" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works ?
&lt;/h2&gt;

&lt;p&gt;It's quite interesting to know how the product is made because it's atypical. Indeed, the code and model is hosted on your servers but the UI layout configuration is hosted on Forest servers. Here is a schema of the architecture :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.forestadmin.com%2Fpublic%2Fimg%2Fillustrations-dev%2Fschema-4.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.forestadmin.com%2Fpublic%2Fimg%2Fillustrations-dev%2Fschema-4.svg" alt="Architecture" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This way, your data remain secure 🔐&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;It's been 6 months that we use Forest and the whole team is very happy with it. The product and marketing love the dashboard and custom views (you know, they love fancy graphs 😄). The customer support work is easier thanks to views displaying customer data to answer questions quickly.&lt;/p&gt;

&lt;p&gt;Also, it's free up to 10 users so you can test it before using it in your profesional projects !&lt;/p&gt;

&lt;p&gt;And above all, it doesn't take time to your dev team, and they can use time for "real work" ^^&lt;/p&gt;

&lt;p&gt;Hope it helps !&lt;/p&gt;

</description>
      <category>database</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Bye bye Postman ! Let's share your REST API calls in team, easily !</title>
      <dc:creator>Jonathan BROSSARD</dc:creator>
      <pubDate>Fri, 10 Jan 2020 10:56:48 +0000</pubDate>
      <link>https://forem.com/monisnap/bye-bye-postman-let-s-share-your-rest-api-calls-in-team-easily-h6l</link>
      <guid>https://forem.com/monisnap/bye-bye-postman-let-s-share-your-rest-api-calls-in-team-easily-h6l</guid>
      <description>&lt;p&gt;As developer, we are using tools to make REST API calls &lt;em&gt;(Postman, Insomnia, PostWoman...)&lt;/em&gt;, and these tools are very usefull.&lt;/p&gt;

&lt;h2&gt;
  
  
  The limits
&lt;/h2&gt;

&lt;p&gt;Make calls to test an API is fine, but if you want to edit, version, or simply share it with your team ... it's not very handy.&lt;/p&gt;

&lt;p&gt;Indeed, you can use Postman paid plans for instance, but it means that you need to pay, and it means that all your team needs to use Postman, again one more tool... &lt;/p&gt;

&lt;h2&gt;
  
  
  Do you know &lt;strong&gt;REST Client&lt;/strong&gt; ?
&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F452qob0efqpz249wrvnm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F452qob0efqpz249wrvnm.png" title="Logo REST Client VS Code Extension" alt="Logo REST Client VS Code Extension"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=humao.rest-client" rel="noopener noreferrer"&gt;REST Client&lt;/a&gt;* is a &lt;strong&gt;VS Code extension&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;It will let you to send HTTP requests and view responses &lt;strong&gt;into VS Code&lt;/strong&gt;. And only based on a text file, which can easily be &lt;strong&gt;versioned&lt;/strong&gt; among your repository. 🙏&lt;/p&gt;

&lt;h2&gt;
  
  
  Pros
&lt;/h2&gt;

&lt;p&gt;The main advantage is to be able to version and share your API calls. &lt;/p&gt;

&lt;p&gt;If you're working on an internal API, you may want to share how to test a new endpoint with your colleagues. &lt;/p&gt;

&lt;p&gt;REST Client is a good easy way to do it !&lt;/p&gt;

&lt;p&gt;Another good point is simplicity. All you need to do/have, is &lt;strong&gt;ONE file&lt;/strong&gt;. Also, if you jump between projects and do not remember how works an API/Service on which you did not work since a while, just look at this file ! &lt;/p&gt;

&lt;h2&gt;
  
  
  Cons
&lt;/h2&gt;

&lt;p&gt;You have to use VS Code... but for a lot of known reasons among internets, you are using Emacs, VI, or VS Code 😄&lt;/p&gt;

&lt;h3&gt;
  
  
  What's next ?
&lt;/h3&gt;

&lt;p&gt;Here is how to begin.&lt;/p&gt;

&lt;h3&gt;
  
  
  The file
&lt;/h3&gt;

&lt;p&gt;Only create a file with .http extension, for instance doc.http&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;And then, VS Code will show you the file as :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F3ssx618zejysxau11b20.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F3ssx618zejysxau11b20.png" alt="doc.http preview into VS Code"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And by clicking on &lt;strong&gt;"Send Request"&lt;/strong&gt;, a new tab with all request response details will be opened.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Ffqamilfzz042c6vgvl5s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Ffqamilfzz042c6vgvl5s.png" alt="doc.http response preview into VS Code"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Go further
&lt;/h3&gt;

&lt;p&gt;You may also use environment variables to easily switch between you env and avoid any api-key or token manually update in the file, or url update etc...&lt;/p&gt;

&lt;p&gt;Here is how to use environment variables, just like that : &lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Here, I created 4 environments : local, dev, staging, production.&lt;/p&gt;

&lt;p&gt;These 4 environments have their own host and token variables, with their specific values. &lt;br&gt;
But they also share a variable, named &lt;code&gt;partnerUniqueToken&lt;/code&gt; (don't ask me why ... too many partners don't have several env...).&lt;/p&gt;

&lt;p&gt;Hope it helps !&lt;/p&gt;

&lt;h6&gt;
  
  
  Big up to &lt;a href="https://dev.to/monisnapjulien"&gt;Julien&lt;/a&gt; who discovered this extension !
&lt;/h6&gt;

</description>
      <category>vscode</category>
      <category>rest</category>
      <category>postman</category>
    </item>
    <item>
      <title>Github private package with CI/CD</title>
      <dc:creator>Julien</dc:creator>
      <pubDate>Fri, 03 Jan 2020 12:33:28 +0000</pubDate>
      <link>https://forem.com/monisnap/github-private-package-with-ci-cd-4k0</link>
      <guid>https://forem.com/monisnap/github-private-package-with-ci-cd-4k0</guid>
      <description>&lt;p&gt;With the newly created &lt;a href="https://github.com/features/packages" rel="noopener noreferrer"&gt;Github Package Registry&lt;/a&gt;, you can store your private packages in the same place as your code.&lt;/p&gt;

&lt;p&gt;Deploying packages and use them inside a &lt;code&gt;package.json&lt;/code&gt; file is very simple. When it comes to use them in a CI/CD environment it's a bit tricky.&lt;/p&gt;

&lt;p&gt;First, we need to provide our ci/cd environment a secure way to retrieve packages from our private repositories. Using the classic &lt;code&gt;npm login&lt;/code&gt; is secure but not very handy. So the &lt;strong&gt;TOKEN&lt;/strong&gt; approach is what I chose. See more info about creation &lt;a href="https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line" rel="noopener noreferrer"&gt;here&lt;/a&gt;. You only need to provide &lt;strong&gt;read access to packages&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Then we need to set a reference to our private repositories for npm to resolve and find it. The easiest way is to set an &lt;strong&gt;.npmrc&lt;/strong&gt; file at the root of your project and put things this way :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;registry&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://registry.npmjs.org/
@owner:registry&lt;span class="o"&gt;=&lt;/span&gt;https://npm.pkg.github.com/
//npm.pkg.github.com/:_authToken&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The trick here is to indicate the default registry (registry.npmjs.org) for all other public packages you want to use.&lt;/p&gt;

&lt;p&gt;Be sure to replace &lt;code&gt;owner&lt;/code&gt; by your organisation name. The &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; is stored as environment variable (you have many ways to hide it on your machine and in some ci/cd tools, don't need to hardcode it).&lt;/p&gt;

&lt;p&gt;Hope it helps !&lt;/p&gt;

</description>
      <category>github</category>
      <category>npm</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
