<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Thomas.G</title>
    <description>The latest articles on DEV Community by Thomas.G (@fraxken).</description>
    <link>https://dev.to/fraxken</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F314815%2F128a0b56-a103-4bc8-92b6-ce3738e98770.jpg</url>
      <title>DEV Community: Thomas.G</title>
      <link>https://dev.to/fraxken</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/fraxken"/>
    <language>en</language>
    <item>
      <title>NodeSecure hidden capability: mama</title>
      <dc:creator>Thomas.G</dc:creator>
      <pubDate>Sat, 10 Jan 2026 07:12:29 +0000</pubDate>
      <link>https://dev.to/nodesecure/nodesecure-hidden-capability-mama-2mn2</link>
      <guid>https://dev.to/nodesecure/nodesecure-hidden-capability-mama-2mn2</guid>
      <description>&lt;p&gt;Hello 👋&lt;/p&gt;

&lt;p&gt;I'm starting a new &lt;strong&gt;short&lt;/strong&gt;-article series focused on highlighting lesser-known parts of the &lt;a href="https://github.com/NodeSecure" rel="noopener noreferrer"&gt;NodeSecure&lt;/a&gt; project. The goal is to help new contributors by giving them a clearer view of the back-end building blocks that power the project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Chapter 1: mama
&lt;/h2&gt;

&lt;p&gt;Mama stand for &lt;code&gt;ManifestManager&lt;/code&gt;. This package was designed to manage and load an npm manifest (a &lt;code&gt;package.json&lt;/code&gt; file, for simplicity).&lt;/p&gt;

&lt;p&gt;Under the hood, it uses &lt;code&gt;@nodesecure/npm-types&lt;/code&gt; to provide precise, up-to-date types (including runtime-related fields). We will dive into that package in another article.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ManifestManager&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@nodesecure/mama&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// synchronous version: ManifestManager.fromPackageJSONSync&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mama&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;ManifestManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromPackageJSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mama&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This package provides many utilities used across back-end components in the Scanner monorepo.&lt;/p&gt;

&lt;p&gt;Here are a few of them:&lt;/p&gt;

&lt;h3&gt;
  
  
  Integrity
&lt;/h3&gt;

&lt;p&gt;You can easily extract a hash by using the &lt;strong&gt;readonly&lt;/strong&gt; getter &lt;code&gt;integrity&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Scanner uses this to assert that the &lt;code&gt;package.json&lt;/code&gt; in the tarball matches the one uploaded to the registry (known as &lt;a href="https://blog.vlt.sh/blog/the-massive-hole-in-the-npm-ecosystem" rel="noopener noreferrer"&gt;manifest confusion&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Here are the properties we hash:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  name,
  version,
  dependencies,
  license: license ?? "NONE",
  scripts
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a mismatch is detected, the tool reports it as a global warning, as shown in the CLI UI:&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%2Fjhne0n0haij6y2uuu8oa.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%2Fjhne0n0haij6y2uuu8oa.png" alt="NodeSecure global warnings" width="800" height="247"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  module type
&lt;/h3&gt;

&lt;p&gt;Inspired by the recent &lt;a href="https://github.com/antfu/node-modules-inspector/" rel="noopener noreferrer"&gt;node-modules-inspector&lt;/a&gt; tool built by Antfu, we re-implemented the same module type detection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// "dts" | "faux" | "dual" | "esm" | "cjs"&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;mama&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;moduleType&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  entry files
&lt;/h3&gt;

&lt;p&gt;mama can recursively extract entry files using the Node.js &lt;code&gt;exports&lt;/code&gt; field (or legacy fields like &lt;code&gt;main&lt;/code&gt;). The API is lazy and returns an &lt;code&gt;IterableIterator&amp;lt;string&amp;gt;&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="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;mama&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getEntryFiles&lt;/span&gt;&lt;span class="p"&gt;()]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This API is used in the &lt;em&gt;tarball&lt;/em&gt; package in combination with JS-X-Ray’s EntryFilesAnalyser.&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;async&lt;/span&gt; &lt;span class="nf"&gt;scanFiles&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ScannedFilesResult&lt;/span&gt;&lt;span class="o"&gt;&amp;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;location&lt;/span&gt; &lt;span class="o"&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;manifest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&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;composition&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;spdx&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="nf"&gt;getTarballComposition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nx"&gt;conformance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extractLicenses&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SourceCodeScanner&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;manifest&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;iterate&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;manifest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;manifest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getEntryFiles&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flatMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;filterJavaScriptFiles&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
    &lt;span class="na"&gt;javascript&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;composition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flatMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;filterJavaScriptFiles&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;conformance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;spdx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;composition&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;code&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  author
&lt;/h3&gt;

&lt;p&gt;Parse the NPM &lt;code&gt;author&lt;/code&gt; field if present and then return a &lt;code&gt;Contact&lt;/code&gt; interface.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Contact&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;email&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="nl"&gt;url&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="nl"&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example, &lt;code&gt;John Doe &amp;lt;john.doe@gmail.com&amp;gt;&lt;/code&gt; produces the following object:&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;"John Doe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"john.doe@gmail.com"&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;h3&gt;
  
  
  Others
&lt;/h3&gt;

&lt;p&gt;The module also provides additional utilities around reading and managing manifests, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Parsing package specs (including scope/org, package name, and semver range)&lt;/li&gt;
&lt;li&gt;Detecting local lockfiles&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The end
&lt;/h2&gt;

&lt;p&gt;The full module documentation is available &lt;a href="https://github.com/NodeSecure/scanner/tree/master/workspaces/mama" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thanks you for reading&lt;/p&gt;

</description>
      <category>node</category>
      <category>javascript</category>
      <category>security</category>
    </item>
    <item>
      <title>Migrating from Nextcloud to Azure S3</title>
      <dc:creator>Thomas.G</dc:creator>
      <pubDate>Tue, 20 May 2025 12:14:44 +0000</pubDate>
      <link>https://dev.to/myunisoft/migrating-from-nextcloud-to-azure-s3-ek6</link>
      <guid>https://dev.to/myunisoft/migrating-from-nextcloud-to-azure-s3-ek6</guid>
      <description>&lt;p&gt;Hello 👋&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Back for a new MyUnisoft technical article&lt;/strong&gt;, this time with the help of my colleague &lt;a href="https://www.linkedin.com/in/nico-mart/" rel="noopener noreferrer"&gt;Nicolas MARTEAU&lt;/a&gt;. Today, we will share our journey to completely refactor our document management architecture and how we migrated from &lt;a href="https://nextcloud.com/fr/" rel="noopener noreferrer"&gt;Nextcloud&lt;/a&gt; to Azure S3 as our storage technology.&lt;/p&gt;

&lt;p&gt;We weren’t able to cover every detail—both for &lt;strong&gt;security&lt;/strong&gt; reasons 🛡️ and to &lt;strong&gt;protect sensitive data&lt;/strong&gt; 🔒—but I hope you will enjoy what I could share. 😊&lt;/p&gt;

&lt;h2&gt;
  
  
  👀 Why moving away from Nextcloud ?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Performance 🤖
&lt;/h3&gt;

&lt;p&gt;Until now, we have managed several &lt;strong&gt;tens of millions of documents&lt;/strong&gt; with Nextcloud. However, &lt;strong&gt;stability and performance&lt;/strong&gt; had become an issue, with regular &lt;strong&gt;downtime&lt;/strong&gt; 🕒 and &lt;strong&gt;delays&lt;/strong&gt; ⏳ of several minutes for a simple document upload at times.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💬 These upload delays sometimes led to &lt;strong&gt;misunderstandings among users&lt;/strong&gt;. For example, in certain integrations, it was not uncommon for users to delete their Accounting entries 🧾 after a few seconds because they thought the attachment was missing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The &lt;strong&gt;complexity and limited functionality&lt;/strong&gt; of the existing APIs quickly became a significant obstacle 🛑. Simply making a document available in a specific folder could require four or five separate HTTP requests. We needed a more &lt;strong&gt;robust storage solution&lt;/strong&gt; that could &lt;strong&gt;scale effectively&lt;/strong&gt; 📈 and provide consistent, fast response times. 🚀&lt;/p&gt;

&lt;h3&gt;
  
  
  Infrastructure 🏢
&lt;/h3&gt;

&lt;p&gt;Furthermore, we needed to reduce Nextcloud's impact on our infrastructure. Unlike Azure, Nextcloud &lt;strong&gt;doesn't scale well&lt;/strong&gt; and required &lt;strong&gt;too much maintenance&lt;/strong&gt; from our DevOps team.&lt;/p&gt;

&lt;h2&gt;
  
  
  😬 Architectural issues
&lt;/h2&gt;

&lt;p&gt;In the past, users accessed documents stored directly on Nextcloud, with some files displayed through the platform’s built-in viewers.&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%2Fdj3cs6unvzu5dyyoev1v.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%2Fdj3cs6unvzu5dyyoev1v.png" alt="GED Architecture 1" width="511" height="278"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This initial choice was certainly made for simplicity, but it has evolved into a significant architectural challenge as we began exposing storage directly to customers. Changing a storage server without affecting our users has become complex, and it also complicates the management of certain security and observability concerns.&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%2F68ieabrudi90yf3w2w8z.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%2F68ieabrudi90yf3w2w8z.png" alt="GED Architecture 2" width="506" height="182"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The primary issue is with PDF documents, such as ledgers, which contain hardcoded URLs pointing to specific storage servers. This requires us to maintain these URLs for years to ensure continued access.&lt;/p&gt;




&lt;p&gt;As part of our migration to S3, we are addressing these issues by routing all requests through the same service (GED).&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%2F796orjevzval17xcgiuh.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%2F796orjevzval17xcgiuh.png" alt="GED New Architecturee" width="800" height="345"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This approach enables us to resolve several issues and enhance the product’s functionality:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Requiring authentication for some sensible documents.&lt;/li&gt;
&lt;li&gt;Providing full observability over who uploads or downloads specific documents.&lt;/li&gt;
&lt;li&gt;Enabling updates to storage capabilities without impacting customers.&lt;/li&gt;
&lt;li&gt;Integrating new storage technologies seamlessly and transparently—for instance, through potential future integrations with services like Microsoft OneDrive.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  📢 The plan
&lt;/h2&gt;

&lt;p&gt;The first step was &lt;strong&gt;to draft an action plan&lt;/strong&gt; 📝 and thoroughly document the existing setup. After several weeks of work, we established the key steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Route all document&lt;/strong&gt; downloads through the GED service.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Route all document&lt;/strong&gt; uploads through the GED service.&lt;/li&gt;
&lt;li&gt;Migrate all existing documents to our new Azure storage, &lt;strong&gt;ensuring zero impact&lt;/strong&gt; 🚫 on the end user.&lt;/li&gt;
&lt;li&gt;Managing the Nextcloud links found in the &lt;strong&gt;PDFs&lt;/strong&gt; already exported by our clients before the migration. Since these links pointed directly to our &lt;strong&gt;Nextcloud servers&lt;/strong&gt;💀, we had to find a reliable solution to route these calls through the GED.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each stage comes with its own set of &lt;strong&gt;challenges&lt;/strong&gt;, which we’ll examine in detail later in the article.&lt;/p&gt;

&lt;p&gt;Our primary concern, however, was to correct previous architectural missteps 🔍.&lt;/p&gt;

&lt;h3&gt;
  
  
  1️⃣ Download
&lt;/h3&gt;

&lt;p&gt;The first step was to &lt;strong&gt;re-abstract downloads and previews&lt;/strong&gt;, routing them through our backend. This required us to manage both &lt;strong&gt;legacy documents&lt;/strong&gt; 📜 still stored on Nextcloud and &lt;strong&gt;new documents&lt;/strong&gt; that would be hosted on Azure storage.&lt;/p&gt;

&lt;p&gt;One challenge we’re facing is that the token generated by Nextcloud lacks any information about the tenant associated with the document. Without this, our &lt;strong&gt;backend cannot identify the relevant database cluster and tenant&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuflt844o911ppjqkootm.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%2Fuflt844o911ppjqkootm.png" alt="TokenXTenant" width="737" height="483"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To resolve this, we created a new opaque token that embeds the tenant ID:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:crypto&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;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;tenantId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randomBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;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;token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// =&amp;gt; 1-f82158a508b8bfbed82b601e2ed60edd&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  🔮 Previews
&lt;/h4&gt;

&lt;p&gt;Nextcloud offered automatic previews of uploaded files, a feature we relied on extensively, so we needed to re-implement an equivalent ourselves.&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%2F8eng0b0yhrrby67y29wt.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8eng0b0yhrrby67y29wt.jpg" alt="download_preview_usage" width="800" height="258"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We decided not to generate previews at upload, as this would have added significant complexity and cost, along with the challenge of handling asynchronous generation.&lt;/p&gt;

&lt;p&gt;For PDFs, we just return an optimized preview of the first page, and for images, we use the Sharp library.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getImageTransformer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;x&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="nl"&gt;y&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="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;ext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;sharp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Sharp&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;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getDimensions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&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;transformer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Azure&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isAlphaImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;sharp&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;png&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;sharp&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;failOn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;jpeg&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;mozjpeg&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;quality&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;50&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;transformer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resize&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;fit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;inside&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;withoutEnlargement&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;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;x&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;h4&gt;
  
  
  📦 Headers and encoding
&lt;/h4&gt;

&lt;p&gt;When returning documents, it’s essential to set the correct HTTP headers and apply proper encoding to values like file names.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;contentType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extname&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;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;contentLength&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getFileFromAzure&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="c1"&gt;// pipe body to reply/response&lt;/span&gt;

&lt;span class="nx"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Disposition&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;`attachment; filename="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;"`&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Length&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;contentLength&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I frequently see editors forget to re-inject the file name.&lt;/p&gt;

&lt;h4&gt;
  
  
  Monitoring
&lt;/h4&gt;

&lt;p&gt;Being able to monitor developments and misuse is critical to guaranteeing the stability of our infrastructures.&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%2Fnuglssormvjq3zgz7pq3.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnuglssormvjq3zgz7pq3.jpg" alt="GED download monitoring" width="785" height="264"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Built-in file viewer
&lt;/h4&gt;

&lt;p&gt;Since Nextcloud could display multiple documents within a viewer, we chose to re-implement a minimal yet functional viewer to retain this capability.&lt;/p&gt;

&lt;p&gt;While our front-ends offer more advanced display modules, this lightweight viewer remains useful in several scenarios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Replacing or rewriting legacy URLs in PDFs.&lt;/li&gt;
&lt;li&gt;External links shared via APIs.&lt;/li&gt;
&lt;li&gt;Providing quick access for debugging purposes.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;h3&gt;
  
  
  2️⃣ Upload
&lt;/h3&gt;

&lt;p&gt;Successfully prototyping an upload wasn’t as complex as expected… but, as always, the devil is in the details.&lt;/p&gt;

&lt;p&gt;For inter-service uploads between Node applications, another &lt;strong&gt;Fastify plugin&lt;/strong&gt; was added to our workspace package, providing methods to interact with the GED API 🔀.&lt;/p&gt;

&lt;h4&gt;
  
  
  📉 Optimize PDFs and images
&lt;/h4&gt;

&lt;p&gt;Many of the PDFs and images submitted by our users are &lt;strong&gt;quite large and can be optimized&lt;/strong&gt;. For this, we use &lt;a href="https://www.ghostscript.com/" rel="noopener noreferrer"&gt;Ghostscript&lt;/a&gt; 👻 to optimize PDFs and the &lt;a href="https://sharp.pixelplumbing.com/" rel="noopener noreferrer"&gt;Sharp&lt;/a&gt; package for images.&lt;/p&gt;

&lt;p&gt;To date, we’ve reduced the size of received PDFs and images by an average of 50%, with &lt;strong&gt;no loss in quality&lt;/strong&gt; ✨.&lt;/p&gt;

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

&lt;p&gt;Compression is performed asynchronously using &lt;strong&gt;setImmediate&lt;/strong&gt; to ensure fast server response times.&lt;br&gt;
A compression value of &lt;strong&gt;"null"&lt;/strong&gt; indicates that the compression &lt;strong&gt;ratio is below 5%&lt;/strong&gt; 🤷‍♀️, making the update to the file on Azure negligible.&lt;br&gt;
Otherwise, the file is updated in the cloud ✔.&lt;/p&gt;

&lt;p&gt;Most of these optimizations are carried out via streams, so that the file or image is &lt;strong&gt;never completely buffered&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;However, we needed to remain &lt;strong&gt;vigilant about rising CPU consumption&lt;/strong&gt; and enhance our infrastructure setup 🏗️ to handle increased workloads effectively.&lt;/p&gt;
&lt;h4&gt;
  
  
  🖼️ HEIC/HEIF
&lt;/h4&gt;

&lt;p&gt;Apple's proprietary &lt;strong&gt;HEIC&lt;/strong&gt; format 📱 presented a significant challenge, often requiring conversion to JPG or PNG for compatibility.&lt;/p&gt;

&lt;p&gt;Given that Python bindings to libheif showed much better performance, we initially opted to create our own N-API Node.js binding for libheif, using low-level libraries for rapid JPG and PNG conversion.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/MyUnisoft/heif-converter" rel="noopener noreferrer"&gt;&lt;strong&gt;HEIF-converter&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For maintenance reasons&lt;/strong&gt;, we chose to use &lt;a href="https://www.npmjs.com/package/sharp" rel="noopener noreferrer"&gt;&lt;strong&gt;Sharp&lt;/strong&gt;&lt;/a&gt; by building &lt;strong&gt;libvips&lt;/strong&gt; directly on our machines and installing the &lt;strong&gt;necessary tools&lt;/strong&gt; (libheif, mozjpeg, libpng, etc.).&lt;/p&gt;
&lt;h4&gt;
  
  
  🔒 Security
&lt;/h4&gt;

&lt;p&gt;When managing file uploads and storage, &lt;strong&gt;vigilance is essential&lt;/strong&gt; 🕵️‍♂️ in several areas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Monitor for &lt;strong&gt;spoofed HTTP headers&lt;/strong&gt; 🛡️, such as altered content-type headers.&lt;/li&gt;
&lt;li&gt;Scan files for &lt;strong&gt;viruses&lt;/strong&gt; and &lt;strong&gt;malicious content&lt;/strong&gt; 🦠.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Otherwise, an attacker could misuse your brand and storage capabilities to &lt;strong&gt;distribute malicious content and compromise users&lt;/strong&gt; 🚨.&lt;/p&gt;

&lt;p&gt;Make it a habit to consult the OWASP cheat sheets to ensure maximum protection against errors and oversights: &lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/File_Upload_Cheat_Sheet.html" rel="noopener noreferrer"&gt;OWASP File Upload Cheat Sheet&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We used &lt;a href="https://www.npmjs.com/package/clamscan" rel="noopener noreferrer"&gt;&lt;strong&gt;clamscan&lt;/strong&gt;&lt;/a&gt; (which relies on &lt;strong&gt;ClamAV&lt;/strong&gt;) to scan the files 👁️, and &lt;a href="https://www.npmjs.com/package/file-type" rel="noopener noreferrer"&gt;&lt;strong&gt;file-type&lt;/strong&gt;&lt;/a&gt; to accurately identify the file type instead of relying solely on the request headers 🧨.&lt;/p&gt;
&lt;h4&gt;
  
  
  📊 Monitoring
&lt;/h4&gt;

&lt;p&gt;As we regain control, it’s essential not to overlook usage monitoring through logs and other metrics.&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%2Fs2zktzujkrg4nosnfx89.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs2zktzujkrg4nosnfx89.jpg" alt="myunisoft_ged_upload_monitoring_1" width="393" height="528"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  3️⃣ Migrating Nextcloud documents
&lt;/h3&gt;

&lt;p&gt;To gradually phase out our Nextcloud servers, we developped a &lt;strong&gt;temporary Node.js API&lt;/strong&gt; 🦾, responsible for transferring resources from Nextcloud to Azure. This service handled upload concurrency, which we've limited to &lt;strong&gt;64 simultaneous uploads&lt;/strong&gt; to avoid overloading the server 🔥.&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%2F5g4ou6sg63af27nhfbs3.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5g4ou6sg63af27nhfbs3.jpg" alt="Albatros_tool" width="800" height="220"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Without detailing every feature of this internal tool, it was designed to support key functionalities such as pausing and resuming the migration process, as well as monitoring the status of each transfers (&lt;strong&gt;successes&lt;/strong&gt; ✅, &lt;strong&gt;errors&lt;/strong&gt; ❌, &lt;strong&gt;totals&lt;/strong&gt;, etc.).&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%2F0kn8hmvo1clrlefptp4e.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%2F0kn8hmvo1clrlefptp4e.png" alt="Nextcloud_2_totals" width="215" height="120"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Step 1&lt;/strong&gt;: Data Extraction
&lt;/h4&gt;

&lt;p&gt;We extracted from the Nextcloud database all the tokens 📄 (used to retrieve document data from the database) and the file paths on the server (to transfer the resources), saving them into &lt;code&gt;.csv&lt;/code&gt; or &lt;code&gt;.txt&lt;/code&gt; files.&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%2Fp0uwdx6fyv4qcu7tmanp.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp0uwdx6fyv4qcu7tmanp.jpg" alt="Nextcloud_export" width="800" height="56"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Step 2&lt;/strong&gt;: Environment Setup
&lt;/h4&gt;

&lt;p&gt;We then set up a &lt;strong&gt;NAS server&lt;/strong&gt; to run the Node.js tool and directly access the file system 🦄, bypassing the Nextcloud API. This approach was chosen to maximize performance and enable efficient stream-based, parallel processing of the document transfers.&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%2F2hyqnytulu9gh5rkokev.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2hyqnytulu9gh5rkokev.jpg" alt="Nextcloud_NAS" width="800" height="407"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Step 3&lt;/strong&gt;: Create the DB and go 🧨
&lt;/h4&gt;

&lt;p&gt;All that remained was to create the &lt;strong&gt;SQLite databases&lt;/strong&gt; (we chose to generate one database per firm to avoid excessively large files), using the Nextcloud exports that contained &lt;strong&gt;tens of millions of rows&lt;/strong&gt;, and then start the transfers ✅.&lt;/p&gt;

&lt;p&gt;Let’s just say we ran into a few surprises along the way 🤫, and the migration ended up taking us several days 🤭.&lt;/p&gt;
&lt;h3&gt;
  
  
  4️⃣ Legacy URLs in PDF
&lt;/h3&gt;

&lt;p&gt;Some &lt;strong&gt;URLs are permanently embedded in PDFs&lt;/strong&gt; 📄, so we need to consider strategies for rewriting them using the information available.&lt;/p&gt;

&lt;p&gt;Since Nextcloud tokens didn’t contain any tenant information, we created a &lt;strong&gt;minimal API&lt;/strong&gt; (microservice) supported by an &lt;strong&gt;SQLite&lt;/strong&gt; database to maintain the relationship between a token and its corresponding tenant ID.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;PRAGMA&lt;/span&gt; &lt;span class="n"&gt;journal_mode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;OFF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;PRAGMA&lt;/span&gt; &lt;span class="n"&gt;synchronous&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;PRAGMA&lt;/span&gt; &lt;span class="n"&gt;locking_mode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;EXCLUSIVE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="nv"&gt;"tokens"&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nv"&gt;"token"&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nv"&gt;"schema"&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;WITHOUT&lt;/span&gt; &lt;span class="n"&gt;ROWID&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can manage thousands of tokens within just a &lt;strong&gt;few milliseconds&lt;/strong&gt; ⏱️ using purely synchronous I/O. Additionally, we implemented an &lt;strong&gt;LRU cache&lt;/strong&gt; to ensure that repetitive requests are handled even more quickly.&lt;/p&gt;

&lt;p&gt;The final step is to configure HAProxy 🔀 to &lt;strong&gt;redirect nextcloud viewer requests&lt;/strong&gt; to a specific &lt;strong&gt;GED endpoint&lt;/strong&gt;, where the URL is parsed to retrieve tokens and correlate them with their respective tenants, using the project setup described above.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;link&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;canParse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Problem&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// Other URL validation here&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;(?&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;=&lt;/span&gt;&lt;span class="se"&gt;\/)([&lt;/span&gt;&lt;span class="sr"&gt;1-9&lt;/span&gt;&lt;span class="se"&gt;]{1,4}&lt;/span&gt;&lt;span class="sr"&gt;-&lt;/span&gt;&lt;span class="se"&gt;\w{15,32}&lt;/span&gt;&lt;span class="sr"&gt;|&lt;/span&gt;&lt;span class="se"&gt;\w{15})(?=\W&lt;/span&gt;&lt;span class="sr"&gt;|$&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Correlate tokens with our microservice database&lt;/span&gt;

&lt;span class="nx"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/ged/document/view?tokens=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;correlatedTokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;|&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is only a partial overview of the implementation. We use a combination of the WHATWG URL API and regular expressions to extract tokens, ensuring sufficient security to mitigate any ReDoS attack vectors.&lt;/p&gt;

&lt;p&gt;We then redirect the request with all tokens to our built-in viewer.&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%2F4oczy3memfuj2m9stg2t.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4oczy3memfuj2m9stg2t.jpg" alt="Built-in_viewer" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🔬 What we've learned
&lt;/h2&gt;

&lt;p&gt;This project taught us that errors in URLs saved within PDF documents are hard to forgive. Due to some technical debt and a lack of foresight, we ended up with an unintended &lt;code&gt;/ged/ged&lt;/code&gt; prefix. Today we're having a bit of a laugh about it, and if you see this prefix you'll know it wasn't meant to be 😆.&lt;/p&gt;

&lt;p&gt;Managing files with proper streaming while handling errors proved far more challenging than anticipated, plaguing us for weeks with ghost files, memory leaks, and other unexpected bugs. At this level of usage, it’s technical excellence or nothing.&lt;/p&gt;

&lt;h2&gt;
  
  
  ❤️ Credits
&lt;/h2&gt;

&lt;p&gt;A migration project of &lt;strong&gt;this scale doesn’t happen overnight&lt;/strong&gt;—it took us well over a year to complete all the steps outlined above. A big thank you 🙏 to everyone involved for their dedication and effort ❤️.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Nicolas 👨‍💻, for leading the project development from &lt;strong&gt;A to Z&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The infrastructure team 🏗️ (Vincent, Jean-Charles, and Cyril) for their &lt;strong&gt;consistent support&lt;/strong&gt; throughout the project.&lt;/li&gt;
&lt;li&gt;Aymeric, for managing and leading the migration of downloads and uploads for his team services.&lt;/li&gt;
&lt;li&gt;Many others 👥 for their &lt;strong&gt;reviews and support&lt;/strong&gt; 📝.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This project was incredibly rewarding 🏆, both for its &lt;strong&gt;challenges&lt;/strong&gt; and the range of &lt;strong&gt;architectural issues it addressed&lt;/strong&gt; 📐.&lt;/p&gt;




&lt;p&gt;Thank you, see you soon for another technical adventure 😉😊&lt;/p&gt;

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

</description>
      <category>node</category>
      <category>nextcloud</category>
      <category>azure</category>
      <category>s3</category>
    </item>
    <item>
      <title>Designing MyUnisoft Next-Gen Accounting APIs</title>
      <dc:creator>Thomas.G</dc:creator>
      <pubDate>Thu, 04 Apr 2024 14:20:41 +0000</pubDate>
      <link>https://dev.to/myunisoft/designing-myunisoft-next-gen-accounting-apis-1mn</link>
      <guid>https://dev.to/myunisoft/designing-myunisoft-next-gen-accounting-apis-1mn</guid>
      <description>&lt;p&gt;Hello 👋&lt;/p&gt;

&lt;p&gt;I'm excited to present my latest technical article detailing the design and development process behind the &lt;strong&gt;next&lt;/strong&gt; iteration of the MyUnisoft Set of accounting external APIs.&lt;/p&gt;

&lt;p&gt;Internally known as &lt;strong&gt;MAD&lt;/strong&gt; for MyUnisoft Accounting Data 😄, this project showcases our team's innovative approach and dedication to enhancing API and Partners experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  💡 Context about the business requirements
&lt;/h2&gt;

&lt;p&gt;You might be questioning the need for developing new APIs. We embarked on this effort to address a spectrum of issues and fulfill unmet needs, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔄 Introducing a modern and efficient format for &lt;strong&gt;exporting and importing accounting folders&lt;/strong&gt;, along with their parameters (particularly useful for migrations between tenants).&lt;/li&gt;
&lt;li&gt;📊 Incorporating accounting data that was previously absent from historical partners APIs, such as &lt;strong&gt;Analytics&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;🚀 Constructing &lt;strong&gt;high-performance&lt;/strong&gt;, &lt;strong&gt;user-friendly&lt;/strong&gt; APIs for our partners, adhering to &lt;strong&gt;modern API best practices&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When I refer to &lt;strong&gt;high performance&lt;/strong&gt;, I'm not solely emphasizing faster response times. It also includes improving the security and strength of our systems by using &lt;strong&gt;cache&lt;/strong&gt; and &lt;strong&gt;queues&lt;/strong&gt;, as well as ensuring &lt;strong&gt;better observability&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  👻 TRA
&lt;/h3&gt;

&lt;p&gt;Historically, the &lt;strong&gt;&lt;a href="https://www.cegid.com/fr/" rel="noopener noreferrer"&gt;CEGID TRA format&lt;/a&gt;&lt;/strong&gt; has been predominant in the French market, but it faces a number of issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tailored for CEGID's specific needs, many columns within the format prove &lt;strong&gt;irrelevant for MyUnisoft's purposes&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Featuring a column-oriented structure with fixed positions, it becomes &lt;strong&gt;resistant to evolution&lt;/strong&gt; and challenging to implement and maintain 😡.&lt;/li&gt;
&lt;li&gt;Parsing and generating data in this format incur significant &lt;strong&gt;costs and complexities&lt;/strong&gt; 😞.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, it can be difficult for software editors and accounting firms to adapt to unfamiliar formats quickly. That's why we've decided to be compatible with TRA.&lt;/p&gt;

&lt;p&gt;The basic idea is to enable &lt;strong&gt;faster integration and smoother migration&lt;/strong&gt; to our format. If this format has worked so far, we can learn a lot from it 😊.&lt;/p&gt;

&lt;h3&gt;
  
  
  💪 Accounting imputation for Factur-X
&lt;/h3&gt;

&lt;p&gt;A significant disappointment lies in the realization that simply exchanging &lt;strong&gt;Factur-X via APIs&lt;/strong&gt; will prove largely inadequate for creating a satisfactory experience for accounting editors and their customers (CPA), as the format does not permit embedding data for &lt;strong&gt;accounting imputation&lt;/strong&gt; 😞.&lt;/p&gt;

&lt;p&gt;To address this issue, we are planning to implement &lt;strong&gt;Factur-X natively within MAD&lt;/strong&gt;. This format will enable the fields to be omitted, provided that the attached file is a valid Factur-X. Developers will only need to fill in the required fields for imputation purposes.&lt;/p&gt;

&lt;p&gt;🚫 There will be no necessity for creating &lt;strong&gt;non-standard extensions of the XML format&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  ✒️ Technical specification
&lt;/h2&gt;

&lt;p&gt;From the beginning, my aim was to develop accounting APIs that incorporate the following features: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Versioned interface contracts&lt;/strong&gt; to establish lifecycles and facilitate iterative changes more smoothly.&lt;/li&gt;
&lt;li&gt;Utilization of &lt;strong&gt;identical interfaces&lt;/strong&gt; for both import and export functions, thereby simplifying API interactions.&lt;/li&gt;
&lt;li&gt;🧠 Maximizing intelligence and independence to minimize dependency on other APIs. For instance, this includes eliminating the necessity for IDs or internal codes during imports.&lt;/li&gt;
&lt;li&gt;Striving for &lt;strong&gt;neutrality&lt;/strong&gt; when crafting the format, ensuring compatibility and flexibility across various systems and use cases.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lastly, it's imperative to highlight the significance of neutrality in our approach. Too often, technical teams can fall into the trap of designing formats and APIs solely with internal use cases in mind, often under the pressure of immediate business requirements.&lt;/p&gt;

&lt;p&gt;However, it's essential to recognize that our target audience primarily comprises developers who experience accounting through the lens of their own unique business contexts. By prioritizing neutrality in our design principles, we aim to simplify the lives of our consumers and empower them with flexible solutions that seamlessly adapt to diverse workflows.&lt;/p&gt;

&lt;p&gt;Through MAD, we're committed to fostering an ecosystem that prioritizes &lt;strong&gt;developer-centric&lt;/strong&gt; design, ensuring that our APIs serve as versatile tools capable of meeting a wide range of needs 🌐.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔍 Gathering information on existing assets
&lt;/h3&gt;

&lt;p&gt;The initial step involved conducting an inventory of existing APIs and comprehensively understanding the data consumed by our partners. We didn't have to do a lot of work, given that we have a &lt;strong&gt;nice &lt;a href="https://partners.api.myunisoft.fr/" rel="noopener noreferrer"&gt;public documentation&lt;/a&gt; available&lt;/strong&gt; 😎!&lt;/p&gt;

&lt;p&gt;We reviewed the APIs of several of our competitors, complemented by our experience integrating with various accounting editors, which guided us in making the right design decisions.&lt;/p&gt;

&lt;p&gt;Additionally, we meticulously analyzed our &lt;strong&gt;TRA backend&lt;/strong&gt; to extract the required columns for a preliminary version. This enabled us to identify the &lt;strong&gt;essential elements for a version 1&lt;/strong&gt;, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;accounting folder&lt;/li&gt;
&lt;li&gt;payments&lt;/li&gt;
&lt;li&gt;banks&lt;/li&gt;
&lt;li&gt;journals&lt;/li&gt;
&lt;li&gt;exercices&lt;/li&gt;
&lt;li&gt;analytics (axes and their sections)&lt;/li&gt;
&lt;li&gt;entries &amp;amp; movements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🔍 Our plan is to expand this list to incorporate more elements over time.&lt;/p&gt;

&lt;h3&gt;
  
  
  📚 Building interfaces
&lt;/h3&gt;

&lt;p&gt;The second step involved crafting prototypes for our JSON object interfaces, utilizing &lt;strong&gt;TypeScript&lt;/strong&gt; and &lt;strong&gt;JSON Schema&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Here is the finalized version for &lt;strong&gt;Journal&lt;/strong&gt;, which underwent several weeks of iteration, concurrent with the creation of initial drafts of SQL.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;SimplifiedAccount&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;producerId&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="nl"&gt;number&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="nl"&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="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;TypeJournal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Achat&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Vente&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Banque&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Journal&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;producerId&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="nl"&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="nl"&gt;customerReferenceCode&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="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TypeJournal&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;counterpartAccount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SimplifiedAccount&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;additionalProducerProperties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TypeJournalInternal&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;locked&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&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;blockquote&gt;
&lt;p&gt;💡 Properties that are too specific to MyUnisoft are stored in &lt;strong&gt;additionalProducerProperties&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Simultaneously iterating on the SQL has proven essential, allowing us to eliminate keys that significantly impact performance without providing &lt;strong&gt;commensurate value&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Here is an another example with &lt;strong&gt;Bank&lt;/strong&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="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Bank&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;producerId&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="nl"&gt;IBAN&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="nl"&gt;BIC&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="nl"&gt;account&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SimplifiedAccount&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;journal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;producerId&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="nl"&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="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;BANQUE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;customerReferenceCode&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="p"&gt;};&lt;/span&gt;
  &lt;span class="nl"&gt;additionalProducerProperties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;isDefault&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&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;A significant effort has been invested in creating objects that are both clean and user-friendly.&lt;/p&gt;

&lt;h3&gt;
  
  
  🌀 (Bonus) Known your abstraction
&lt;/h3&gt;

&lt;p&gt;We frequently fall into the trap of assuming that our abstractions are universally understood across a entire ecosystem. In accounting, for instance, it's typical to categorize transactions into an abstraction known as an "Entry." Each entry comprises a set of balanced movements (or transactions).&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%2F6g7ozjlri6w20sykm1qn.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%2F6g7ozjlri6w20sykm1qn.png" alt="entry/movement abstract" width="557" height="379"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, many software solutions, including &lt;strong&gt;Dataviz&lt;/strong&gt;, often prefer consuming &lt;strong&gt;raw movements without additional abstractions&lt;/strong&gt;. This preference explains why such solutions historically lean towards formats like FEC, which are simpler compared to TRA-heavy formats.&lt;/p&gt;

&lt;p&gt;From my experience working with partners over the years, I've learned that many of them employ various abstractions for VAT, accounts, or analytics. Interestingly, the choice of abstraction can sometimes be influenced by the &lt;strong&gt;nature of their software&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The way we structure our data can play a vital role in ensuring a positive experience for specific consumers 💡.&lt;/p&gt;

&lt;h2&gt;
  
  
  💬 Technical implementation
&lt;/h2&gt;

&lt;p&gt;For this project we decided to create an &lt;strong&gt;in-house package&lt;/strong&gt; that would bring together all the necessary parts. This decision primarily aims to accommodate diverse use cases &lt;strong&gt;without the need for repetitive API&lt;/strong&gt; (or SDK) integration.&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%2F5eqn0gj6vvjdl9a89489.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%2F5eqn0gj6vvjdl9a89489.png" alt="MAD Usage" width="678" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The second step was to design a &lt;strong&gt;testable architecture&lt;/strong&gt; for the package. For some parts I drew inspiration from open source libs such as &lt;a href="https://undici.nodejs.org/" rel="noopener noreferrer"&gt;undici&lt;/a&gt; and their mocking API.&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%2F7pv06vgwg6x5agt30iye.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%2F7pv06vgwg6x5agt30iye.png" alt="MAD Package" width="800" height="351"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At one point I was thinking that it might be a problem to implement &lt;a href="https://fr.wikipedia.org/wiki/HATEOAS#:~:text=HATEOAS%2C%20abr%C3%A9viation%20d'Hypermedia%20As,autres%20architectures%20d'applications%20r%C3%A9seau." rel="noopener noreferrer"&gt;HATEOAS&lt;/a&gt;... However, I discovered that with our architecture, dynamically injecting new links to specific resources using transformers is surprisingly straightforward 😅.&lt;/p&gt;

&lt;h3&gt;
  
  
  📦 Package usage
&lt;/h3&gt;

&lt;p&gt;Here's a simple example&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;MAD&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;@myunisoft/mad&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;exporter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;MAD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Export&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1.x&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;accountingFolderId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;10&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;postgresDataSource&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="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;exercices&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;exporter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exercices&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Underneath we fetch elements from the database and transform them into the correct format, leveraging the &lt;strong&gt;SemVer version&lt;/strong&gt; injected into the constructor.&lt;/p&gt;

&lt;p&gt;Furthermore, we've integrated an observability layer to track the execution times of each component. However, this aspect likely requires further refinement 😕.&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;async&lt;/span&gt; &lt;span class="nf"&gt;exercices&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ExportResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;postgre&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Exercice&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;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;startTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&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;rawExercices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&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;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exercices&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;sqlTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;startTime&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;transformed&lt;/span&gt; &lt;span class="o"&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;transformer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exercices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rawExercices&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;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;transformed&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="na"&gt;executionTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;sqlTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;transformer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;transformed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;executionTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;total&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;startTime&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;The provider can &lt;strong&gt;simply be mocked up&lt;/strong&gt; if you need to run unit tests (by forcing a global provider or forcing it using the class constructor).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;kGlobalDispatcher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nf"&gt;before&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;MAD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setGlobalProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;kGlobalDispatcher&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nf"&gt;beforeEach&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;kGlobalDispatcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resetCalls&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nf"&gt;after&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;MAD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resetGlobalProvider&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  (&lt;strong&gt;Bonus&lt;/strong&gt;) TRA compatibility
&lt;/h3&gt;

&lt;p&gt;Effectively writing or reading a &lt;strong&gt;TRA&lt;/strong&gt; isn't inherently complex, although it can be quite tedious due to the time-consuming process of properly implementing all its sections.&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%2Fsp8x00rn2jzj7fxt0xwz.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%2Fsp8x00rn2jzj7fxt0xwz.png" alt="TRA" width="408" height="781"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The challenge lies in being as fast as possible&lt;/strong&gt; ⚡.&lt;/p&gt;

&lt;p&gt;After one or two months of work, our JavaScript implementation can now read and write a complete TRA of several million lines in just a few &lt;strong&gt;hundred milliseconds&lt;/strong&gt; 📈💻.&lt;/p&gt;

&lt;p&gt;To achieve this, we've minimized the number of operations and iterations for each section. Additionally, each section is equipped with a comprehensive definition containing all column information, such as position, alignment, and length.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TRAConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;section&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rib&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;JSONToTRA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;TRAToJSON&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;columns&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;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fixe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;mandatory&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;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;left&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;identifiant&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;mandatory&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;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;left&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// ... other fields&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;Each section is equipped with functions designed for &lt;strong&gt;efficient conversion&lt;/strong&gt; between TRA and JSON formats.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;JSONToTRA&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;lineObj&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IRib&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TRAParsingOptions&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="p"&gt;[&lt;/span&gt;
    &lt;span class="nf"&gt;convertJSONValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;lineObj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fixe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nf"&gt;convertJSONValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;lineObj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;identifiant&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nf"&gt;convertJSONValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;columns&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="nx"&gt;lineObj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;auxiliaire&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="c1"&gt;// other fields..&lt;/span&gt;
    &lt;span class="nx"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;EOL&lt;/span&gt;
  &lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="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;We utilized tools like flamegraphs to pinpoint the longest-running elements and effectively eliminate them. Additionally, we optimized our process by implementing caching mechanisms to avoid redundant operations, such as repeatedly parsing the same dates.&lt;/p&gt;

&lt;p&gt;Exploring a Rust implementation could be intriguing to further reduce execution time 🚀. Perhaps in &lt;strong&gt;the future&lt;/strong&gt;, if performance becomes a critical concern 👀.&lt;/p&gt;

&lt;h2&gt;
  
  
  🌏 API
&lt;/h2&gt;

&lt;p&gt;In parallel with the work done on the MAD package, &lt;strong&gt;we drew up a plan for creating the APIs&lt;/strong&gt; 📝.&lt;/p&gt;

&lt;p&gt;However, we encountered a challenge: some of the APIs we'll be exposing are potentially risky ⚠️, as they may permit exporting entire folders along with all associated parameters.&lt;/p&gt;

&lt;p&gt;To address this, &lt;strong&gt;we're designing asynchronous APIs&lt;/strong&gt;, incorporating status tracking and caching mechanisms integrated with our event architecture. This setup enables us to trigger webhooks as needed, enhancing security and reliability.&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%2Flut8msq5cbaaezt7ep7z.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%2Flut8msq5cbaaezt7ep7z.png" alt="MAD API" width="800" height="1188"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The sequence diagram above illustrates the interactions between the various components involved in exporting an entire accounting folder.&lt;/p&gt;

&lt;p&gt;For this export, we have &lt;strong&gt;two&lt;/strong&gt; distinct routes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;mad/all?format=json&amp;amp;version=1.0.0&lt;/code&gt; to initialize the export.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;mad/all/status?exportId=export_sch-5_acf-1&lt;/code&gt; to fetch the status.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The second route will return a DONE status when processing is complete (Real-time webhook notification can be subscribed to as an option).&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;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"export_sch-1_acf-1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DONE"&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;"&amp;lt;url&amp;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;h2&gt;
  
  
  📜 Documentation
&lt;/h2&gt;

&lt;p&gt;We've seamlessly extended our documentation, which is readily accessible on Github. Recently, we've enhanced the user experience by incorporating an online &lt;a href="https://vitepress.dev/" rel="noopener noreferrer"&gt;Vitepress&lt;/a&gt; site, hosted with Github Pages.&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%2F3fa2fin8qf8ktaimsido.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%2F3fa2fin8qf8ktaimsido.png" alt="MAD Doc" width="800" height="526"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It required several weeks of effort, and the pivotal elements include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📝 A comprehensive description outlining our decisions.&lt;/li&gt;
&lt;li&gt;📋 A detailed specification encompassing all interfaces, schemas, etc.&lt;/li&gt;
&lt;li&gt;📚 Additional insights, explanations, and guidance tailored for beginners in accounting.&lt;/li&gt;
&lt;li&gt;🗺️ Instructions on navigating MyUnisoft for developers unfamiliar with our product.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My primary objective is to ensure a &lt;strong&gt;positive developer experience&lt;/strong&gt; (DX) while maintaining thorough and accessible documentation. You're welcome to explore it yourself &lt;a href="https://partners.api.myunisoft.fr/MAD/introduction.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;, and we eagerly await your feedback 😊.&lt;/p&gt;

&lt;h2&gt;
  
  
  📆 What's next?
&lt;/h2&gt;

&lt;p&gt;We're only at the very beginning of the project. We will continue to add new content to our export format and work on new options for our various APIs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In parallel, we're working on the import&lt;/strong&gt; (I'll probably have a chance to write a new article when we're well advanced on this).&lt;/p&gt;

&lt;h2&gt;
  
  
  ❤️ Credits
&lt;/h2&gt;

&lt;p&gt;This project is primarily a &lt;strong&gt;collaborative effort&lt;/strong&gt;, made possible by the contributions of key team members:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Benoit GARIAZZO (Product Owner)&lt;/li&gt;
&lt;li&gt;Alexandre MALAJ (Specification, MAD package &amp;amp; SQL).&lt;/li&gt;
&lt;li&gt;Cédric LIONNET (API)&lt;/li&gt;
&lt;li&gt;Pierre DEMAILLY (TRA/JSON compatibility &amp;amp; review)&lt;/li&gt;
&lt;li&gt;Frédéric GUIOU (Help with naming and currently leading import)&lt;/li&gt;
&lt;li&gt;Me 😁 (Specification, Observability, Documentation)&lt;/li&gt;
&lt;li&gt;Léon and Aymeric for their invaluable assistance with existing accounting APIs &amp;amp; formats.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And all other team members who provided valuable feedback and support during the implementation process 🙌.&lt;/p&gt;

&lt;p&gt;🙏 That concludes this article. Thank you for reading!&lt;/p&gt;

</description>
      <category>accounting</category>
      <category>javascript</category>
      <category>node</category>
      <category>api</category>
    </item>
    <item>
      <title>Consuming Loki logs with Grafana API and Node.js</title>
      <dc:creator>Thomas.G</dc:creator>
      <pubDate>Wed, 25 Oct 2023 10:28:24 +0000</pubDate>
      <link>https://dev.to/myunisoft/consuming-loki-logs-with-grafana-api-and-nodejs-bgg</link>
      <guid>https://dev.to/myunisoft/consuming-loki-logs-with-grafana-api-and-nodejs-bgg</guid>
      <description>&lt;p&gt;Hello 👋&lt;/p&gt;

&lt;p&gt;In my last article we were discussing how me and my team had set up and built dashboards with Grafana and Loki 😎.&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/myunisoft" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&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%2Forganization%2Fprofile_image%2F9816%2F84062579-f1f0-4751-b8ba-171afcdbc7ad.png" alt="MyUnisoft" width="225" height="225"&gt;
      &lt;div class="ltag__link__user__pic"&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%2Fuser%2Fprofile_image%2F314815%2F128a0b56-a103-4bc8-92b6-ce3738e98770.jpg" alt="" width="400" height="400"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/myunisoft/logs-monitoring-with-loki-nodejs-and-fastifyjs-3h8k" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Logs monitoring with Loki, Node.js and Fastify.js&lt;/h2&gt;
      &lt;h3&gt;Thomas.G for MyUnisoft ・ Jun 12 '23&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#node&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#monitoring&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#grafana&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;Today we're going to discuss how you can leverage your logs for third-party tools using Grafana's API and Node.js 😍.&lt;/p&gt;

&lt;h2&gt;
  
  
  💡 The idea that starts it all
&lt;/h2&gt;

&lt;p&gt;Several months ago, I started thinking about how to use our logs to build a personalized CLI experience.&lt;/p&gt;

&lt;p&gt;After a bit of searching, I quickly found that Grafana had an &lt;a href="https://grafana.com/docs/loki/latest/reference/api/" rel="noopener noreferrer"&gt;API for queryings Loki logs&lt;/a&gt;. I immediately went crazy at the thought of what could be done with it 🔥.&lt;/p&gt;

&lt;h2&gt;
  
  
  🐢🚀 Node.js SDK
&lt;/h2&gt;

&lt;p&gt;Over the weekend, I set to create an &lt;a href="https://github.com/MyUnisoft/loki" rel="noopener noreferrer"&gt;open source Node.js SDK&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;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;GrafanaLoki&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@myunisoft/loki&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;api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GrafanaLoki&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="c1"&gt;// Note: if not provided, it will load process.env.GRAFANA_API_TOKEN&lt;/span&gt;
  &lt;span class="na"&gt;apiToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;remoteApiURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://name.loki.com&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;logs&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;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;queryRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s2"&gt;`{app="serviceName", env="production"}`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1d&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;logs&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;👀 Notice the support of duration for options like &lt;strong&gt;start&lt;/strong&gt; and &lt;strong&gt;end&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It already supports Stream and Matrix and several other APIs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GET /loki/api/v1/labels&lt;/li&gt;
&lt;li&gt;GET /loki/api/v1/label/:name/values&lt;/li&gt;
&lt;li&gt;GET /loki/api/v1/series&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We've also added datasource APIs (which may be required depending on what you'r building).&lt;/p&gt;

&lt;p&gt;It also include a LogParser inspired by Loki pattern.&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="c1"&gt;// can be provided as an option to queryRange&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;LogParser&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;endpoint&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="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;method:httpMethod&amp;gt; &amp;lt;endpoint&amp;gt; &amp;lt;statusCode:httpStatusCode&amp;gt;&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;logs&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;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;queryRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s2"&gt;`... LogQL here ...`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;parser&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;for &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;logLine&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;logs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;logLine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&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;logLine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;It's still far from perfect, don't hesitate to contribute 💪.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/OpenAlly" rel="noopener noreferrer"&gt;
        OpenAlly
      &lt;/a&gt; / &lt;a href="https://github.com/OpenAlly/loki" rel="noopener noreferrer"&gt;
        loki
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Node.js Loki SDK
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;
  Loki
&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;
  Node.js Grafana API SDK (Loki, Datasources ..)
&lt;/p&gt;

&lt;p&gt;
    &lt;a href="https://github.com/OpenAlly/loki" rel="noopener noreferrer"&gt;
      &lt;img src="https://camo.githubusercontent.com/0552eef223cb95be8e894b9d73f49e9da3b1a68f945186f4de999266941cf75c/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f7061636b6167652d6a736f6e2f762f4f70656e416c6c792f6c6f6b693f7374796c653d666f722d7468652d6261646765" alt="npm version"&gt;
    &lt;/a&gt;
    &lt;a href="https://github.com/OpenAlly/loki" rel="noopener noreferrer"&gt;
      &lt;img src="https://camo.githubusercontent.com/207eed5c41f8c5433e055e9c73164c5b9a09852eac92d9e381e1f3114eb57b0c/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f4f70656e416c6c792f6c6f6b693f7374796c653d666f722d7468652d6261646765" alt="license"&gt;
    &lt;/a&gt;
    &lt;a href="https://api.securityscorecards.dev/projects/github.com/OpenAlly/loki" rel="nofollow noopener noreferrer"&gt;
      &lt;img src="https://camo.githubusercontent.com/ef5c65274aec81c9b5fe7fce83e5a74e7bbac5352426d7808d8b31056b113821/68747470733a2f2f6170692e736563757269747973636f726563617264732e6465762f70726f6a656374732f6769746875622e636f6d2f4f70656e416c6c792f6c6f6b692f62616467653f7374796c653d666f722d7468652d6261646765" alt="ossf scorecard"&gt;
    &lt;/a&gt;
    &lt;a href="https://github.com/OpenAlly/loki/actions?query=workflow%3A%22Node.js+CI%22" rel="noopener noreferrer"&gt;
      &lt;img src="https://camo.githubusercontent.com/0eb39842628e6fbe576895e909db360abf7c48dc23983c6057ea884c513058ab/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f4f70656e416c6c792f6c6f6b692f6e6f64652e6a732e796d6c3f7374796c653d666f722d7468652d6261646765" alt="github ci workflow"&gt;
    &lt;/a&gt;
    &lt;a href="https://github.com/OpenAlly/loki" rel="noopener noreferrer"&gt;
      &lt;img src="https://camo.githubusercontent.com/26cb2e27ce29b93b62ae1bf0e2db3b8291f346ee746c1d7d8f7ad73b550ef0ee/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c616e6775616765732f636f64652d73697a652f4f70656e416c6c792f6c6f6b693f7374796c653d666f722d7468652d6261646765" alt="size"&gt;
    &lt;/a&gt;
&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;🚧 Requirements&lt;/h2&gt;
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://nodejs.org/en/" rel="nofollow noopener noreferrer"&gt;Node.js&lt;/a&gt; version 24 or higher&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;🚀 Getting Started&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;This package is available in the Node Package Repository and can be easily installed with &lt;a href="https://doc.npmjs.com/getting-started/what-is-npm" rel="nofollow noopener noreferrer"&gt;npm&lt;/a&gt; or &lt;a href="https://yarnpkg.com" rel="nofollow noopener noreferrer"&gt;yarn&lt;/a&gt;&lt;/p&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;$ npm i @openally/loki
&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; or&lt;/span&gt;
$ yarn add @openally/loki&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;📚 Usage&lt;/h2&gt;

&lt;/div&gt;

&lt;div class="highlight highlight-source-ts notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;import&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt; &lt;span class="pl-v"&gt;GrafanaApi&lt;/span&gt; &lt;span class="pl-kos"&gt;}&lt;/span&gt; &lt;span class="pl-k"&gt;from&lt;/span&gt; &lt;span class="pl-s"&gt;"@openally/loki"&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-k"&gt;import&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt; &lt;span class="pl-v"&gt;LogQL&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-v"&gt;StreamSelector&lt;/span&gt; &lt;span class="pl-kos"&gt;}&lt;/span&gt; &lt;span class="pl-k"&gt;from&lt;/span&gt; &lt;span class="pl-s"&gt;"@sigyn/logql"&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;

&lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;api&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-k"&gt;new&lt;/span&gt; &lt;span class="pl-v"&gt;GrafanaApi&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;{&lt;/span&gt;
  &lt;span class="pl-c1"&gt;authentication&lt;/span&gt;: &lt;span class="pl-kos"&gt;{&lt;/span&gt;
    &lt;span class="pl-c1"&gt;type&lt;/span&gt;: &lt;span class="pl-s"&gt;"bearer"&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
    &lt;span class="pl-c1"&gt;token&lt;/span&gt;: &lt;span class="pl-s1"&gt;process&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;env&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;GRAFANA_API_TOKEN&lt;/span&gt;&lt;span class="pl-c1"&gt;!&lt;/span&gt;
  &lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
  &lt;span class="pl-c1"&gt;remoteApiURL&lt;/span&gt;: &lt;span class="pl-s"&gt;"https://name.loki.com"&lt;/span&gt;
&lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;

&lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;ql&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-k"&gt;new&lt;/span&gt; &lt;span class="pl-v"&gt;LogQL&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;
  &lt;span class="pl-k"&gt;new&lt;/span&gt; &lt;span class="pl-v"&gt;StreamSelector&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;{&lt;/span&gt; &lt;span class="pl-c1"&gt;app&lt;/span&gt;: &lt;span class="pl-s"&gt;"serviceName"&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;env&lt;/span&gt;: &lt;span class="pl-s"&gt;"production"&lt;/span&gt; &lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;
&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;logs&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-k"&gt;await&lt;/span&gt; &lt;span class="pl-s1"&gt;api&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;Loki&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;queryRange&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;
  &lt;span class="pl-s1"&gt;ql&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c"&gt;// or string `{app="serviceName", env="production"}`&lt;/span&gt;
  &lt;span class="pl-kos"&gt;{&lt;/span&gt;
    &lt;span class="pl-c1"&gt;start&lt;/span&gt;: &lt;span class="pl-s"&gt;"1d"&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
    &lt;span class="pl-c1"&gt;limit&lt;/span&gt;: &lt;span class="pl-c1"&gt;200&lt;/span&gt;
  &lt;span class="pl-kos"&gt;}&lt;/span&gt;
&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-smi"&gt;console&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;log&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;logs&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/OpenAlly/loki" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



&lt;h2&gt;
  
  
  🔎 LogQL builder
&lt;/h2&gt;

&lt;p&gt;We've also built a utility package to construct log queries, inspired by the WHATWG URL/URLSearchParams API.&lt;/p&gt;

&lt;p&gt;The package lets you build any LogQL programmatically from scratch&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;LogQL&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@sigyn/logql&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;logql&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;LogQL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;foo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;logql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;streamSelector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;env&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;prod&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;logql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lineEq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bar&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;logql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lineNotEq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;baz&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// {env=\"prod\"} |= `foo` |= `bar` != `baz`&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;logql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But also able to parse raw string 😇&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;logql&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;LogQL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;{app=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;foo&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;, env=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;preprod&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;} |= `foo` != `bar`&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;([...&lt;/span&gt;&lt;span class="nx"&gt;logql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;streamSelector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&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;logql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lineFilters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lineContains&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;logql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lineFilters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lineDoesNotContain&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can use subclasses in your tools at any time, for example to retrieve labels from a LogQL 😮&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;StreamSelector&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@sigyn/logql&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;logql&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;labels&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;StreamSelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;logql&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;kv&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  📜 Credits
&lt;/h2&gt;

&lt;p&gt;I'm not alone and didn't do all the work 👯. Thanks to &lt;a href="https://github.com/PierreDemailly" rel="noopener noreferrer"&gt;Pierre Demailly&lt;/a&gt; and &lt;a href="https://github.com/SofianD" rel="noopener noreferrer"&gt;Sofian Doual&lt;/a&gt; for their contribution/support.&lt;/p&gt;

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

&lt;p&gt;These two tools have enabled us to initiate the development of new tools. One of them is an alerting agent for Loki named Sigyn, to whom I'll be dedicating an article in the near future.&lt;/p&gt;

&lt;p&gt;Having APIs really opens up a lot of opportunities and it'll be interesting to see what me and my team are able to produce in the future with them.&lt;/p&gt;

&lt;p&gt;Best Regards,&lt;br&gt;
Thomas&lt;/p&gt;

</description>
      <category>node</category>
      <category>javascript</category>
      <category>monitoring</category>
      <category>grafana</category>
    </item>
    <item>
      <title>Logs monitoring with Loki, Node.js and Fastify.js</title>
      <dc:creator>Thomas.G</dc:creator>
      <pubDate>Mon, 12 Jun 2023 12:45:10 +0000</pubDate>
      <link>https://dev.to/myunisoft/logs-monitoring-with-loki-nodejs-and-fastifyjs-3h8k</link>
      <guid>https://dev.to/myunisoft/logs-monitoring-with-loki-nodejs-and-fastifyjs-3h8k</guid>
      <description>&lt;p&gt;Hello 👋&lt;/p&gt;

&lt;p&gt;Over the past few months, I've been spending a lot of time creating dashboards on &lt;a href="https://grafana.com/" rel="noopener noreferrer"&gt;Grafana&lt;/a&gt; using &lt;a href="https://github.com/grafana/loki" rel="noopener noreferrer"&gt;Loki&lt;/a&gt; for &lt;a href="https://www.myunisoft.fr/" rel="noopener noreferrer"&gt;MyUnisoft&lt;/a&gt; (the company I work for).&lt;/p&gt;

&lt;p&gt;So I decided to write you an article to &lt;strong&gt;explain my adventure&lt;/strong&gt; from the point of view of a &lt;strong&gt;back-end Node.js&lt;/strong&gt; developer.&lt;/p&gt;

&lt;h2&gt;
  
  
  👀 Why grafana and loki?
&lt;/h2&gt;

&lt;p&gt;For context, the tool was implemented by a DevOps employee who left the company two years ago. So I didn't really choose the tool myself. I had no previous experience with either of these tools, so I had to experiment and discover as I went along.&lt;/p&gt;

&lt;p&gt;Grafana is pretty well-known and mature, so I wasn't worried. On the other side, I quickly liked Loki to manage and search my logs. So I thought, "&lt;strong&gt;I've got to learn to make dashboards out of this&lt;/strong&gt;".&lt;/p&gt;

&lt;h2&gt;
  
  
  📃 Writing good logs
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;💬 Several examples of logs are multiline to avoid scrolling and simplify reading.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When you write logs, you must constantly think about the information they will provide and make sure they can be analyzed without too much difficulty 😵. It always takes several iterations to get a good result.&lt;/p&gt;

&lt;p&gt;In my experience, you can find two kinds of logs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;debug&lt;/strong&gt; logs you know are useless to your dashboards. They'll come in handy in case of bugs or when you're looking for more context about a given request.&lt;/li&gt;
&lt;li&gt;All other logs that can provide exploitable data for dashboards (and for a human reader)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here an example of a debug log we print when our services start (quite useful to customize the &lt;code&gt;--max-old-space-size&lt;/code&gt; flag).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;availableHeapSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;prettyBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;v8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getHeapStatistics&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;total_available_size&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Total available heap size: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;availableHeapSize&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another common example is logging a JSON payload (you could exploit the data, of course, but that's often not the end goal).&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;once&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;payloadReady&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;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;payload before publishing:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&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="mi"&gt;2&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;blockquote&gt;
&lt;p&gt;😱 Be sure to redact the data (you may divulge confidential information without realizing it).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Most of the time, you won't get anything out of these logs, and they don't require any particular effort. In this article, I'm going to concentrate on the logs that we can exploit for our dashboards.&lt;/p&gt;

&lt;h3&gt;
  
  
  🐤 Practical example
&lt;/h3&gt;

&lt;p&gt;Here's an example of bad logs we initially set up for a document upload middleware/plugin&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Successfully uploaded '...'
//
Failed to upload '...'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are several problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No scope&lt;/strong&gt; (we can't easily search for all the logs related to our middleware).&lt;/li&gt;
&lt;li&gt;Can easily be &lt;strong&gt;mixed with other logs&lt;/strong&gt; (from other middlewares or services).&lt;/li&gt;
&lt;li&gt;It can be quite a challenge to &lt;strong&gt;parse the state&lt;/strong&gt; (failed or successful).&lt;/li&gt;
&lt;li&gt;Lack information about the &lt;strong&gt;request&lt;/strong&gt; or the &lt;strong&gt;user&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A better way of writing it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[uploader|req-x5d4] document 'name.jpg' uploaded (state: ok)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can easily extend the log with other information such as extension, size, runtime, etc. (without breaking the Loki pattern or regexp).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(state: ok|ext: .jpg|size: 52.5 kB|upload-time: 0.503 ms)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;⚠️ Always use the same unit when logging size (bytes) or time (milliseconds). Setting the right 'unit' on Grafana will do the work of cleaning the value for you.&lt;/p&gt;
&lt;/blockquote&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%2Fxz01vua03g7ry9lavzbc.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%2Fxz01vua03g7ry9lavzbc.png" alt="grafana unit" width="685" height="624"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  📜 Format clarification
&lt;/h3&gt;

&lt;p&gt;A good log has to be structured to allow good searches with LogQL. Here's a bad example where it will be really difficult to extract information.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;hello-world.jpg 52.5 kB|0.503 ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I would recommend &lt;strong&gt;adding a label&lt;/strong&gt; before each value (it's also easier for a human to read). Having start, end, and separator &lt;strong&gt;characters&lt;/strong&gt; can also be a great help.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;doc 'hello-world.jpg' [size: 52.5 kB|exec: 0.503 ms]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here the required RegExp to parse all labels with Loki&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;doc '(?P&amp;lt;doc&amp;gt;\S+)' \[size: (?P&amp;lt;size&amp;gt;\S+) kB|exec: (?P&amp;lt;exec&amp;gt;\S+) ms\]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Loki 2.0 also allows you to retrieve labels with a simplified syntax called pattern.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pattern `doc '&amp;lt;doc&amp;gt;' [size: &amp;lt;size&amp;gt; kB|exec: &amp;lt;exec&amp;gt; ms]`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🔎 Implementation in the framework/code
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ code examples are simplified/truncated&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The Fastify framework includes the &lt;a href="https://github.com/pinojs/pino" rel="noopener noreferrer"&gt;Pino&lt;/a&gt; logger by default (a really great logger with lots of cool features that doesn't compromise on performance). The framework itself allows a lot of really cool stuff, like &lt;a href="https://www.nearform.com/blog/unlock-the-power-of-runtime-log-level-control/" rel="noopener noreferrer"&gt;controlling the level of logs at runtime&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;On my team, we have chosen to customize the default request and response logs to include additional information. To achieve this, you need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;setup fastify constructor option &lt;a href="https://www.fastify.io/docs/latest/Reference/Server/#disablerequestlogging" rel="noopener noreferrer"&gt;disableRequestLogging&lt;/a&gt; to &lt;code&gt;true&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;exploit two hooks (onRequest and onResponse).
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decorateRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;standardLog&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addHook&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onRequest&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FastifyRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;`(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;) receiving request ...`&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;standardLog&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;standardLog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bind&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="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addHook&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onResponse&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FastifyRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&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="nf"&gt;standardLog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s2"&gt;`response returned "&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;method&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"
    )
  );
});
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The standardLog decorator allows us to display information about the request and the token in use (for authenticated endpoints).&lt;/p&gt;

&lt;p&gt;We have to handle several types of tokens (it all depends on who is consuming the API). A classical user or a partner through our partner API).&lt;/p&gt;

&lt;p&gt;Each of them includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The postgreSQL schema of the customer&lt;/li&gt;
&lt;li&gt;The user's or third-party's ID&lt;/li&gt;
&lt;li&gt;The accounting folder (which can be null)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;standardLog&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;FastifyRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;msg&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;if &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;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hasRequestDecorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tokenInfo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tokenInfo&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;tokenInfo&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;tokenInfoLog&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;tokenInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&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="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tokenInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;api&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nx"&gt;tokenInfoLog&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;`|s:&lt;/span&gt;&lt;span class="p"&gt;${...}&lt;/span&gt;&lt;span class="s2"&gt;|t:&lt;/span&gt;&lt;span class="p"&gt;${...}&lt;/span&gt;&lt;span class="s2"&gt;|acf:&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="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nx"&gt;tokenInfoLog&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;`|s:&lt;/span&gt;&lt;span class="p"&gt;${...}&lt;/span&gt;&lt;span class="s2"&gt;|p:&lt;/span&gt;&lt;span class="p"&gt;${...}&lt;/span&gt;&lt;span class="s2"&gt;|acf:&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="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nl"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nx"&gt;tokenInfoLog&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;break&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="s2"&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;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;|&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;tokenInfoLog&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;) &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;msg&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="k"&gt;return&lt;/span&gt; &lt;span class="s2"&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;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;|none) &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;msg&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's what it looks like in the logs&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(req-1rti) receiving request "POST /ged/base-docs/docs"
(req-1rti|user|s:538|p:1|acf:23) response returned
"POST /ged/base-docs/docs", statusCode: 201 (460.106ms) 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These informations will be critical to populating our dashboards with information about customers and the scope of actions.&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%2Fnvnm176pnjebx077yo8t.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%2Fnvnm176pnjebx077yo8t.png" alt="schema" width="767" height="379"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🌀 Logs ingestion
&lt;/h2&gt;

&lt;p&gt;My team currently uses &lt;a href="https://grafana.com/docs/loki/latest/clients/promtail/" rel="noopener noreferrer"&gt;promtail&lt;/a&gt; with a YML configuration to retrieve service logs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;http_listen_port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;9080&lt;/span&gt;
&lt;span class="na"&gt;positions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;filename&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/var/lib/promtail/positions.yml&lt;/span&gt;
&lt;span class="na"&gt;clients&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://xxx.fr/loki/api/v1/push&lt;/span&gt;

&lt;span class="na"&gt;scrape_configs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;job_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;svc_api_dev&lt;/span&gt;
    &lt;span class="na"&gt;static_configs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;labels&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;/home/xxx/logs/service-dev-*.log&lt;/span&gt;
        &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dev&lt;/span&gt;
        &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;xxx.fr&lt;/span&gt;
        &lt;span class="na"&gt;job&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;svc_api_dev&lt;/span&gt;
      &lt;span class="na"&gt;targets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;localhost&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'm not going to spend too much time on this chapter, as you can find a lot of tutorials on the Internet on how to set up and configure promtail.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 If you don't want to depend on promtail for sending the logs to Loki, you can use the fastify plugin &lt;a href="https://github.com/Julien-R44/pino-loki" rel="noopener noreferrer"&gt;pino-loki&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  📊 OCR Dashboard
&lt;/h2&gt;

&lt;p&gt;MyUnisoft is a French accounting editor, so &lt;strong&gt;O&lt;/strong&gt;ptical &lt;strong&gt;C&lt;/strong&gt;haracter &lt;strong&gt;R&lt;/strong&gt;ecognition is an important feature of our software. Monitoring is essential to adapting to the needs of our customers and responding promptly to incidents.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;👀😍 We won the &lt;a href="https://www.linkedin.com/posts/le-monde-du-chiffre_palmareslmc-palmareslmc2023-activity-7071905959412916225-pL4d?utm_source=share&amp;amp;utm_medium=member_desktop" rel="noopener noreferrer"&gt;OCR silver medal 2023&lt;/a&gt; awarded by Le Monde du Chiffre.&lt;/p&gt;
&lt;/blockquote&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%2Fk4fhao5sjfh337rulmbn.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%2Fk4fhao5sjfh337rulmbn.png" alt="myunisoft OCR" width="800" height="371"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is an example of a dashboard we have built just by using the following log:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(req-2987|user|s:24|p:5710|acf:7398) OCR xxx.jpg
[type: invoice|ext: .jpg|size: 2925.73 kB]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Group by label
&lt;/h3&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%2F4srwlq3hjpu2n33dskfv.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%2F4srwlq3hjpu2n33dskfv.png" alt="extensions" width="662" height="311"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can construct the above graph with the following logQL. Notice the &lt;strong&gt;sum&lt;/strong&gt; by &lt;strong&gt;extension&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="err"&gt;sum(&lt;/span&gt;
  &lt;span class="err"&gt;count_over_time(&lt;/span&gt;
    &lt;span class="nv"&gt;{app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"ocr"&lt;/span&gt;,env&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$env&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
      &lt;span class="err"&gt;|=&lt;/span&gt; &lt;span class="s2"&gt;"OCR"&lt;/span&gt;
      &lt;span class="nl"&gt;|= "|ext&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;"&lt;/span&gt;
      | regexp &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;ext: &lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;?P&amp;lt;extension&amp;gt;&lt;span class="se"&gt;\S&lt;/span&gt;+&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;
  &lt;span class="err"&gt;[$__range])&lt;/span&gt;
&lt;span class="err"&gt;)&lt;/span&gt; &lt;span class="err"&gt;by&lt;/span&gt; &lt;span class="err"&gt;(extension)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;RegExp are built using Golang syntax (I personnaly use &lt;a href="https://regex101.com/" rel="noopener noreferrer"&gt;regex101.com&lt;/a&gt; to test them). It is useful here to extract/detect the &lt;code&gt;extension&lt;/code&gt; label.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We can use the syntax &lt;code&gt;$env&lt;/code&gt; to inject a dashboard variable (here I can view my dashboard in production, staging or dev just by moving a value in a select).&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%2Fef2kz5cgrus5r3bvsqfy.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%2Fef2kz5cgrus5r3bvsqfy.png" alt="grafana variable" width="233" height="114"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I also use the built-in &lt;code&gt;$__range&lt;/code&gt; variable, which loads data over the currently selected range in Grafana.&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%2Fbdkn193fwnu5sl5spadn.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%2Fbdkn193fwnu5sl5spadn.png" alt="grafana range" width="553" height="42"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Unwrap
&lt;/h3&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%2F40irbud9rf6hzc5wi3ew.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%2F40irbud9rf6hzc5wi3ew.png" alt="file size" width="675" height="323"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It took me a long time to really understand unwrap. There is virtually no clear documentation or explanation on the Internet! &lt;/p&gt;

&lt;p&gt;Here it will use the &lt;code&gt;size&lt;/code&gt; label and extract all values to compute them with &lt;code&gt;min_over_time&lt;/code&gt; and &lt;code&gt;min&lt;/code&gt; functions. This is useful for &lt;strong&gt;extracting numerical values&lt;/strong&gt; (counter, execution time, etc.).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="err"&gt;min(&lt;/span&gt;
  &lt;span class="err"&gt;min_over_time(&lt;/span&gt;
    &lt;span class="nv"&gt;{app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"ocr"&lt;/span&gt;,env&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$env&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
      &lt;span class="err"&gt;|=&lt;/span&gt; &lt;span class="s2"&gt;"OCR"&lt;/span&gt;
      &lt;span class="nl"&gt;| regexp `\|size&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;(?P&amp;lt;size&amp;gt;[.0-9]+) kB&lt;/span&gt;\]&lt;span class="nf"&gt;`&lt;/span&gt;
      | unwrap size
      | __error__ &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
  &lt;span class="err"&gt;[$__range])&lt;/span&gt;
&lt;span class="err"&gt;)&lt;/span&gt; &lt;span class="err"&gt;by&lt;/span&gt; &lt;span class="err"&gt;(app)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💬 &lt;code&gt;| __error__ = ""&lt;/code&gt; avoid crashing Loki with unexpected Error (could happen if unwrap of size fail for any reasons).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then, on the right side of the stat graph, we select the corresponding unit.&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%2Fdeq12mj6xmkyqlrn0nhu.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%2Fdeq12mj6xmkyqlrn0nhu.png" alt="grafana unit" width="435" height="111"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  💡 Tips and tricks
&lt;/h2&gt;

&lt;h3&gt;
  
  
  display name
&lt;/h3&gt;

&lt;p&gt;For a long time, I had some really bad raw labels in my charts (with a format similar to JSON).&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%2Fgri579mofl2o4q38az7n.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%2Fgri579mofl2o4q38az7n.png" alt="grafana loki display name" width="478" height="81"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can customize it by editing the &lt;code&gt;display name&lt;/code&gt; option. You can use variables in this field to retrieve label values directly.&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%2Ff9rlnyc69zer7dkt87cd.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%2Ff9rlnyc69zer7dkt87cd.png" alt="grafana loki display name" width="411" height="91"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  no data
&lt;/h3&gt;

&lt;p&gt;Sometimes the graphs will display "no data" because no logs have been detected in the selected range. This can be problematic in certain graphics (such as gauges).&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%2Fiwcqoaixnt8ogh5yt08k.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%2Fiwcqoaixnt8ogh5yt08k.png" alt="grafana no data" width="541" height="323"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But maybe you'd rather have zero. No problem, just use the &lt;code&gt;No value&lt;/code&gt; option.&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%2Fkjkl7oynwzdqjzcrv2rv.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%2Fkjkl7oynwzdqjzcrv2rv.png" alt="grafana loki no data" width="409" height="91"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  filter using a regexp and variable
&lt;/h3&gt;

&lt;p&gt;On some dashboards, you will probably want to filter dynamically according to several criteria. One way of doing this is to use a regexp and a dashboard variable.&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%2F1fdxico6kvkrow78awkz.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%2F1fdxico6kvkrow78awkz.png" alt="grafana loki filter regexp" width="412" height="44"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is what I do in one of my dashboards to filter the results for one or more partners.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="err"&gt;count(&lt;/span&gt;
  &lt;span class="err"&gt;sum(&lt;/span&gt;
    &lt;span class="err"&gt;count_over_time(&lt;/span&gt;
      &lt;span class="nv"&gt;{app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"api"&lt;/span&gt;,env&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"production"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="err"&gt;|=&lt;/span&gt; &lt;span class="s2"&gt;"] CALL"&lt;/span&gt;
        &lt;span class="err"&gt;|=&lt;/span&gt; &lt;span class="s2"&gt;"$endpoint"&lt;/span&gt;
        &lt;span class="err"&gt;|~&lt;/span&gt; &lt;span class="err"&gt;`\]\[$thirdparty\]`&lt;/span&gt;
        &lt;span class="nl"&gt;| regexp `\((?P&amp;lt;schemaId&amp;gt;[0-9]+)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;(?P&amp;lt;folderId&amp;gt;[0-9]+)&lt;/span&gt;\)&lt;span class="nf"&gt;`&lt;/span&gt;
    &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$__range&lt;/span&gt;&lt;span class="o"&gt;])&lt;/span&gt;
  &lt;span class="err"&gt;)&lt;/span&gt; &lt;span class="err"&gt;by&lt;/span&gt; &lt;span class="err"&gt;(schemaId,&lt;/span&gt; &lt;span class="err"&gt;folderId)&lt;/span&gt;
&lt;span class="err"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's the line that does the job: you need to use backticks syntax to be able to inject a variable in it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;|~ `\]\[$thirdparty\]`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🚀 Data is the key to success
&lt;/h2&gt;

&lt;p&gt;As developers, we don't realize enough how monitoring and the data it generates can be powerful sources of improvement. At MyUnisoft, we work with over a &lt;strong&gt;hundred&lt;/strong&gt; partners. &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%2Fkurcj1uwjk5c3qzv4fhk.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%2Fkurcj1uwjk5c3qzv4fhk.png" alt="MyUnisoft Third-party" width="800" height="562"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Understanding how they use our APIs and the various anomalies they experience has become essential for us to continue to grow exponentially.&lt;/p&gt;

&lt;p&gt;This allows us to continually improve to provide a better experience for both our accounting customers and our developer partners who maintain the integrations.&lt;/p&gt;

&lt;p&gt;Our ability to materialize and exploit this data has become an essential part of my team's expertise. It's really exciting to see what can be achieved with simple logs.&lt;/p&gt;

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

&lt;p&gt;I'm so glad I finally managed to write this article (and I hope it brought you some value). Many thanks to my team and especially to Cédric, without whom I would not have realized all that.&lt;/p&gt;

&lt;p&gt;I still struggle with a lot of Grafana features, such as transformations or alerts, but I will continue to dig and improve.&lt;/p&gt;

&lt;p&gt;🙏 Thanks for reading me 🙏&lt;/p&gt;

</description>
      <category>node</category>
      <category>javascript</category>
      <category>monitoring</category>
      <category>grafana</category>
    </item>
    <item>
      <title>Securizing your GitHub org</title>
      <dc:creator>Thomas.G</dc:creator>
      <pubDate>Sun, 19 Feb 2023 15:48:01 +0000</pubDate>
      <link>https://dev.to/nodesecure/securize-your-github-org-4lb7</link>
      <guid>https://dev.to/nodesecure/securize-your-github-org-4lb7</guid>
      <description>&lt;p&gt;Hello 👋&lt;/p&gt;

&lt;p&gt;I started open source a bit naively (like everyone I guess 😊).&lt;/p&gt;

&lt;p&gt;But the more I progress and the more important/popular some of my projects become 😎. That's great, but at some point you have to &lt;strong&gt;deal&lt;/strong&gt; with a lot of things &lt;strong&gt;related to security&lt;/strong&gt; (like Vulnerability disclosure).&lt;/p&gt;

&lt;p&gt;You start to hear and see a lot of &lt;strong&gt;scary stories&lt;/strong&gt; around you 😱. Not to mention all the &lt;strong&gt;acronyms&lt;/strong&gt; where you don't understand anything at first 😵 (VMT, CVE, SAST, SCA, CNA ...).&lt;/p&gt;

&lt;p&gt;As I was working on an open source security project, I put pressure on myself to be ready. Also as a member of the &lt;a href="https://github.com/nodejs/security-wg" rel="noopener noreferrer"&gt;Node.js Security WG&lt;/a&gt; I thought it was an interesting topic and that I was probably not the only one who was worried about not being up to the task 😖.&lt;/p&gt;

&lt;p&gt;So I rolled up my sleeves and tackled the problem 💪. Here is my feedback/journey on how I improved the security of my &lt;a href="https://github.com/NodeSecure" rel="noopener noreferrer"&gt;NodeSecure&lt;/a&gt; GitHub organization.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;👀 We use Node.js and JavaScript (but most recommendations are valid for other ecosystems).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Security Policy and Vulnerability Disclosure
&lt;/h2&gt;

&lt;p&gt;Adding a root &lt;code&gt;SECURITY.md&lt;/code&gt; file explaining how developers and security researchers should report vulnerability is important. You don't want a security threat to &lt;strong&gt;be turned into a public issue&lt;/strong&gt; (This gives you time to analyze and possibly fix &lt;strong&gt;before disclosure&lt;/strong&gt;).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ If you are a developer, never report a security threat using a public GitHub issue. &lt;strong&gt;This is a serious mistake&lt;/strong&gt;. This could even put your business/team at &lt;strong&gt;risk&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I don't want to bullshit you, so let me share with you the &lt;strong&gt;OpenSSF guide&lt;/strong&gt; that helped me set up my first reporting strategy: &lt;a href="https://github.com/ossf/oss-vulnerability-guide/blob/main/maintainer-guide.md" rel="noopener noreferrer"&gt;Guide to implementing a coordinated vulnerability disclosure process for open source projects&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I started from scratch by reading this guide and taking inspiration from &lt;a href="https://github.com/ossf/oss-vulnerability-guide/tree/main/templates" rel="noopener noreferrer"&gt;their templates&lt;/a&gt; 🐤. As a small open source team we don't especially have DNS or mail servers (not even a defined &lt;strong&gt;V&lt;/strong&gt;ulnerability &lt;strong&gt;M&lt;/strong&gt;anagement &lt;strong&gt;T&lt;/strong&gt;eam A.K.A &lt;strong&gt;VMT&lt;/strong&gt;).&lt;/p&gt;

&lt;p&gt;I was a bit puzzled to put my personal email as I'm not alone 😟. &lt;/p&gt;

&lt;p&gt;I quickly learned that Github added a new feature &lt;a href="https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing/privately-reporting-a-security-vulnerability" rel="noopener noreferrer"&gt;to report/create private security issue&lt;/a&gt; 😍. You can enable it in the &lt;code&gt;Security&lt;/code&gt; tab (I think it's also now possible to enable it on every repositories at once).&lt;/p&gt;

&lt;p&gt;And this is what it finally looks like:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fimqmjdj2ewiiud0bpmbs.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%2Fimqmjdj2ewiiud0bpmbs.png" alt="NodeSecure SECURITY.md" width="800" height="320"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Use OpenSSF scorecard
&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%2F7kdfnqxjora14q8eil20.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%2F7kdfnqxjora14q8eil20.png" alt="scorecard" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://securityscorecards.dev/" rel="noopener noreferrer"&gt;OSSF scorecard&lt;/a&gt; initiative is really good to assess your project against security best practices. &lt;a href="https://devopsjournal.io/blog/2022/12/08/Adding-OSSF-scorecard-action-to-your-repo" rel="noopener noreferrer"&gt;I am not the first to write about this&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can easily setup the GitHub action workflow by following those &lt;a href="https://github.com/ossf/scorecard-action#installation" rel="noopener noreferrer"&gt;instructions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once configured, you will have a set of alerts available in the &lt;code&gt;Security&lt;/code&gt; tab.&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%2Fi0tib69edbeitcsoomec.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%2Fi0tib69edbeitcsoomec.png" alt="Scorecard scanning alerts" width="800" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will give you an overview of the different subjects to improve (workflows, dependencies etc). Each of these alerts contains a full description of the actions to be taken to fix the problem.&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%2F000q8acla8raunyu8kva.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%2F000q8acla8raunyu8kva.png" alt="OSSF Scorecard" width="800" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I have personally used these recommendations to dig and train myself. The next chapters will help you improve your score.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📢 By the way &lt;a href="https://github.com/NodeSecure/cli" rel="noopener noreferrer"&gt;NodeSecure CLI&lt;/a&gt; has a first-class support of the scorecard.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  🔓 Enable branch protection
&lt;/h2&gt;

&lt;p&gt;I am a bad student 😳. Almost all of my projects had no branch protection on the &lt;code&gt;main&lt;/code&gt; / &lt;code&gt;master&lt;/code&gt; branch 🙈.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To set up the protection, go to &lt;code&gt;Settings&lt;/code&gt; &amp;gt; &lt;code&gt;Branches&lt;/code&gt; and edit your main branch. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;GitHub has quite a few options on the subject 😵. If you don't know what to choose in terms of options, &lt;strong&gt;don't check anything&lt;/strong&gt; (it's &lt;strong&gt;ok&lt;/strong&gt; to begin ✔️).&lt;/p&gt;

&lt;p&gt;If you want to be more restrictive, be &lt;strong&gt;careful&lt;/strong&gt; because it could block you (some options are only viable in projects with many contributors/reviewers).&lt;/p&gt;

&lt;p&gt;As far as I am concerned I often choose:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Require a pull request before merging&lt;/li&gt;
&lt;li&gt;Require conversation resolution before merging&lt;/li&gt;
&lt;li&gt;Require status checks to pass before merging&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  🐲 Workflows Hardening
&lt;/h2&gt;

&lt;p&gt;I fell down when I saw all that it was necessary to know to secure workflows with GitHub actions 😲.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You must pay attention to the &lt;a href="https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs" rel="noopener noreferrer"&gt;permissions granted to your jobs / GITHUB_TOKEN&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://michaelheap.com/ensure-github-actions-pinned-sha/" rel="noopener noreferrer"&gt;Ensure your GitHub Actions are pinned to a SHA&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Hardening the runner (see the &lt;a href="https://blog.stepsecurity.io/announcing-general-availability-of-harden-runner-a7597a1410da" rel="noopener noreferrer"&gt;StepSecurity HardenRunner&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Probably a lot of other stuff I haven't had time to see yet 😆&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fortunately there is a &lt;a href="https://app.stepsecurity.io/" rel="noopener noreferrer"&gt;great free online tool&lt;/a&gt; that help you by doing all the hard work (it will open a pull-request and automatically fix issues).&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1617557370728767488-427" src="https://platform.twitter.com/embed/Tweet.html?id=1617557370728767488"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1617557370728767488-427');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1617557370728767488&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;The tool was created by &lt;a href="https://www.stepsecurity.io/" rel="noopener noreferrer"&gt;StepSecurity&lt;/a&gt;. I had the opportunity to talk with the CEO and they listen to the maintainers which is really cool. &lt;br&gt;
Thanks to them ❤️!&lt;/p&gt;
&lt;h2&gt;
  
  
  Configure Dependabot
&lt;/h2&gt;

&lt;p&gt;It is recommended to use &lt;a href="https://github.blog/2020-06-01-keep-all-your-packages-up-to-date-with-dependabot/" rel="noopener noreferrer"&gt;Dependabot&lt;/a&gt; for updating your dependencies and GitHub actions (yes, it also supports updating workflows in a secure way 😍).&lt;/p&gt;

&lt;p&gt;You only need to add a &lt;code&gt;.github/dependabot.yml&lt;/code&gt; config file. Personally I recommend a &lt;strong&gt;weekly&lt;/strong&gt; interval (with a lot of projects &lt;strong&gt;daily is a bit horrible&lt;/strong&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="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
&lt;span class="na"&gt;updates&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;package-ecosystem&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github-actions&lt;/span&gt;
    &lt;span class="na"&gt;directory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
    &lt;span class="na"&gt;schedule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;weekly&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;package-ecosystem&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm&lt;/span&gt;
    &lt;span class="na"&gt;directory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
    &lt;span class="na"&gt;schedule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;weekly&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The StepSecurity tool we have seen in the previous chapter is also capable of doing it 🚀. &lt;/p&gt;



&lt;p&gt;Also, think to enable &lt;a href="https://docs.github.com/en/code-security/dependabot/dependabot-alerts/about-dependabot-alerts" rel="noopener noreferrer"&gt;Dependabot alerts&lt;/a&gt; in the &lt;code&gt;Security&lt;/code&gt; tab. This will allow the bot to open pull-request to fix known vulnerabilities by looking at your dependencies (&lt;strong&gt;referenced in package.json or others&lt;/strong&gt;).&lt;/p&gt;
&lt;h2&gt;
  
  
  🔬 Adding CodeQL scanning
&lt;/h2&gt;

&lt;p&gt;To enhance security even more you can add a &lt;a href="https://snyk.io/learn/application-security/static-application-security-testing/" rel="noopener noreferrer"&gt;SAST&lt;/a&gt; tool like &lt;a href="https://codeql.github.com/" rel="noopener noreferrer"&gt;CodeQL&lt;/a&gt;. Like scorecard it will report security scanning alert but for your codebase.&lt;/p&gt;

&lt;p&gt;Here an example:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqsycrb5ew1tztvr7rj6z.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%2Fqsycrb5ew1tztvr7rj6z.png" alt="prototype-pollution" width="800" height="492"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A great way to make sure that newly added code does not contain vulnerabilities that were &lt;strong&gt;obvious to detect&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;👀 Note that once again StepSecurity can set up the workflow for you.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  📜 Enable Security advisories (and others)
&lt;/h2&gt;

&lt;p&gt;Github Security tab as a lot of cool features that help you maintain the security of your project. If you have followed all my previous chapters, most of them should be enabled now.&lt;/p&gt;

&lt;p&gt;Make sure to also enable &lt;code&gt;Secret scanning alerts&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Futrx752lf9m382tbpunn.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%2Futrx752lf9m382tbpunn.png" alt="Github Security" width="800" height="438"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For an organization many of these parameters can be forced on all repositories. Go to &lt;code&gt;Settings&lt;/code&gt; &amp;gt; &lt;code&gt;Code security and analysis&lt;/code&gt;. You will have the options to enable/disable all.&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%2Fa8f5cpwva9te0m15yb0s.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%2Fa8f5cpwva9te0m15yb0s.png" alt="Github Security" width="611" height="333"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  💡 OpenSSF Best Pratices program
&lt;/h2&gt;

&lt;p&gt;Previously known as CII-Best-Practices, this program indicates that the project uses a set of security-focused best development practices for open source software.&lt;/p&gt;

&lt;p&gt;So I registered my first project on the &lt;a href="https://bestpractices.coreinfrastructure.org/en" rel="noopener noreferrer"&gt;website&lt;/a&gt;. It was a good surprise because it allowed me to question the quality of my documentation and tests 😬.&lt;/p&gt;

&lt;p&gt;Seeing the different levels and questions really helps you think about what you're missing (and possibly learn about the concepts you don't know about yet.. Like &lt;a href="https://snyk.io/blog/building-sbom-open-source-supply-chain-security/" rel="noopener noreferrer"&gt;SBOM&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9pn6g2gcoyelkniwopy0.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%2F9pn6g2gcoyelkniwopy0.png" alt="CII-Best-Practices" width="800" height="147"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I am still working on completing the first step/badge for the CLI project which now has a score of &lt;strong&gt;8.7&lt;/strong&gt; out of &lt;strong&gt;10&lt;/strong&gt; 🎉 on the OpenSSF scorecard.&lt;/p&gt;
&lt;h2&gt;
  
  
  🎯 Conclusion
&lt;/h2&gt;

&lt;p&gt;That's it for this article. I've covered what I've done/learned in the last couple of months. Here are some really cool additional links 💃:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/ossf/wg-best-practices-os-developers/blob/main/docs/Concise-Guide-for-Evaluating-Open-Source-Software.md#readme" rel="noopener noreferrer"&gt;Concise Guide for Evaluating Open Source Software 2023-01-03&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ossf/wg-best-practices-os-developers/blob/main/docs/Concise-Guide-for-Developing-More-Secure-Software.md#readme" rel="noopener noreferrer"&gt;Concise Guide for Developing More Secure Software 2023-01-03&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you work with NPM, I invite you to read our latest article about package managers:&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/nodesecure" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&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%2Forganization%2Fprofile_image%2F6062%2F43c0ffd0-bc13-4c49-8846-ce3efbdafd52.png" alt="NodeSecure" width="200" height="200"&gt;
      &lt;div class="ltag__link__user__pic"&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%2Fuser%2Fprofile_image%2F702314%2Fecb88ea8-6968-4326-82d1-8c9a97273a30.jpeg" alt="" width="800" height="1066"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/nodesecure/everything-you-need-to-know-package-managers-286c" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;📦 Everything you need to know: package managers&lt;/h2&gt;
      &lt;h3&gt;Antoine Coulon for NodeSecure ・ Nov 18 '22&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#npm&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#node&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#opensource&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;



&lt;p&gt;Obviously, I probably &lt;strong&gt;still have a lot to learn&lt;/strong&gt;. But I hope this will help other maintainers/developers ❤️.&lt;/p&gt;

&lt;p&gt;🙏 Thanks for reading me 🙏&lt;/p&gt;

</description>
      <category>career</category>
      <category>productivity</category>
      <category>discuss</category>
    </item>
    <item>
      <title>JS-X-Ray 6.0</title>
      <dc:creator>Thomas.G</dc:creator>
      <pubDate>Mon, 16 Jan 2023 15:48:18 +0000</pubDate>
      <link>https://dev.to/nodesecure/js-x-ray-60-49ah</link>
      <guid>https://dev.to/nodesecure/js-x-ray-60-49ah</guid>
      <description>&lt;p&gt;Hello 👋&lt;/p&gt;

&lt;p&gt;It's been a while since the &lt;strong&gt;last article&lt;/strong&gt; on JS-X-Ray 😲!&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/nodesecure" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&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%2Forganization%2Fprofile_image%2F6062%2F43c0ffd0-bc13-4c49-8846-ce3efbdafd52.png" alt="NodeSecure" width="200" height="200"&gt;
      &lt;div class="ltag__link__user__pic"&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%2Fuser%2Fprofile_image%2F314815%2F128a0b56-a103-4bc8-92b6-ce3738e98770.jpg" alt="" width="400" height="400"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/nodesecure/js-x-ray-3-0-0-3ddn" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;JS-X-Ray 3.0&lt;/h2&gt;
      &lt;h3&gt;Thomas.G for NodeSecure ・ Feb 28 '21&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#node&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#security&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#opensource&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;In this article I will present you the latest major version 👀. I didn't do an article on version 4 and 5 because they didn't introduce new features (only breaking changes on the API).&lt;/p&gt;

&lt;h2&gt;
  
  
  📢 What is JS-X-Ray ?
&lt;/h2&gt;

&lt;p&gt;If you are new in town, &lt;a href="https://github.com/NodeSecure/js-x-ray" rel="noopener noreferrer"&gt;JS-X-Ray&lt;/a&gt; is an open source JavaScript SAST (Static Application Security Testing). The tool analyzes your JavaScript sources for patterns that may affect the security and quality of your project 😎.&lt;/p&gt;

&lt;p&gt;Among the notable features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Retrieving dependencies (CJS &amp;amp; ESM support) and detecting suspicious import/require.&lt;/li&gt;
&lt;li&gt;Detecting unsafe RegEx.&lt;/li&gt;
&lt;li&gt;Detecting obfuscated source (and provide hints on the tool used).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As well as a lot of &lt;a href="https://github.com/NodeSecure/js-x-ray#warnings-legends" rel="noopener noreferrer"&gt;other detections&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Major release 4 and 5
&lt;/h2&gt;

&lt;p&gt;These versions introduced changes on warnings (and we improved how we manage them in the codebase). We added new descriptors for each of them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;i18n (for translation in &lt;a href="https://github.com/NodeSecure/ci" rel="noopener noreferrer"&gt;CI&lt;/a&gt; or &lt;a href="https://github.com/NodeSecure/cli" rel="noopener noreferrer"&gt;CLI&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;experimental&lt;/li&gt;
&lt;li&gt;severity (Information, Warning, Critical)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those information are visible in the &lt;a href="https://github.com/NodeSecure/cli" rel="noopener noreferrer"&gt;NodeSecure CLI&lt;/a&gt; interface:&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%2F0xjxew9sh7xm07wxhkef.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%2F0xjxew9sh7xm07wxhkef.png" alt="NodeSecure" width="396" height="281"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Major release 6
&lt;/h2&gt;

&lt;p&gt;🐬 Ok, let's dive into this major release to discover the surprises 🎉 it has in store for us.&lt;/p&gt;

&lt;h3&gt;
  
  
  🚀 Introducing VariableTracer
&lt;/h3&gt;

&lt;p&gt;Almost a year of work on this &lt;a href="https://github.com/NodeSecure/estree-ast-utils/blob/main/src/utils/VariableTracer.js" rel="noopener noreferrer"&gt;new mechanism / class&lt;/a&gt; that brings a whole new dimension to JS-X-Ray.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tracer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;VariableTracer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enableDefaultTracing&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;crypto.createHash&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;followConsecutiveAssignment&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;moduleName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;crypto&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;tracer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;walk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This class is able to follow all declarations, assignments and patterns (and those even through very obscure patterns).&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;aA&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;call&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;bB&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aA&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;crypto&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;cr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createHash&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nf"&gt;cr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;md5&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// weak-crypto warning is throw here&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This allows us to implement Probes in a much simpler way (which makes maintenance and testing much easier).&lt;/p&gt;

&lt;p&gt;Here an example with the &lt;code&gt;isWeakCrypto&lt;/code&gt; probe:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;validateNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;tracer&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getCallExpressionIdentifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;tracer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;importedModules&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;crypto&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;return&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tracer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDataFromIdentifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&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;identifierOrMemberExpr&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;crypto.createHash&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;By default the Tracer follows all ways of &lt;code&gt;requiring&lt;/code&gt; dependencies with CJS and also usage of &lt;code&gt;eval&lt;/code&gt; or &lt;code&gt;Function&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  🚧 Removing unsafe-assign warning
&lt;/h3&gt;

&lt;p&gt;This warning was required at the beginning of the project because it was difficult for me to correctly identify some malicious patterns.&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%2Fwscxxpedjz513w9rtxds.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%2Fwscxxpedjz513w9rtxds.png" alt="NodeSecure" width="699" height="417"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, with the introduction of the new Tracer, which is very complete and precise, this warning no longer makes sense has it only generates unnecessary noise and false positives.&lt;/p&gt;
&lt;h3&gt;
  
  
  📜 Better ESM source parsing
&lt;/h3&gt;

&lt;p&gt;We previously had a lot of &lt;code&gt;parsing-error&lt;/code&gt; warnings because the NodeSecure scanner failed to detect if the file was using either CJS or ESM. &lt;/p&gt;

&lt;p&gt;That new version will automatically retry with ESM enabled if it fails with CJS.&lt;/p&gt;
&lt;h3&gt;
  
  
  📉 Reducing false positives
&lt;/h3&gt;

&lt;p&gt;To continue the momentum of the previous sections. This version drops a lot of warnings and significantly improves others.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reducing false positives for &lt;code&gt;encoded-literal&lt;/code&gt; warning by introducing new way of detecting safe values.&lt;/li&gt;
&lt;li&gt;Improve &lt;code&gt;short-identifiers&lt;/code&gt; by also storing ClassDeclaration, MethodDefinition and Function parameters.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We are also introducing a new &lt;code&gt;suspicious-file&lt;/code&gt; warning when a file contain more than 10 encoded-literal warnings to avoid having file with hundreds or thousands of warnings.&lt;/p&gt;

&lt;p&gt;Of the &lt;strong&gt;500&lt;/strong&gt; most popular NPM packages, we previously had &lt;strong&gt;24k&lt;/strong&gt; warnings with version 5. The latest version brings that number down to approximatively &lt;strong&gt;5k&lt;/strong&gt; warnings.&lt;/p&gt;
&lt;h3&gt;
  
  
  🔬 Improving coverage
&lt;/h3&gt;

&lt;p&gt;A lot of work has been done to add unit tests on all the probes of the project. We are near 100% of coverage 💪.&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%2F07uejv3fgthptaymwj5j.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%2F07uejv3fgthptaymwj5j.png" alt="NodeSecure" width="689" height="69"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks to the amazing work of our contributors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/Kawacrepe" rel="noopener noreferrer"&gt;Vincent DHENNIN&lt;/a&gt; - &lt;a class="mentioned-user" href="https://dev.to/kawacrepe"&gt;@kawacrepe&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/PierreDemailly" rel="noopener noreferrer"&gt;Pierre DEMAILLY&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/in/mathieu-kahlaoui-0887a1158/" rel="noopener noreferrer"&gt;Mathieu KA&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/M4gie" rel="noopener noreferrer"&gt;M4gie&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  👀 What's next ?
&lt;/h2&gt;

&lt;p&gt;Here what I'm working for the next major release:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adding support of TypeScript sources (probably by allowing a customization of the parser).&lt;/li&gt;
&lt;li&gt;A new API that allows to dynamically extend the SAST with new custom probes (and custom warnings).&lt;/li&gt;
&lt;li&gt;Introducing new built-in detections and warnings (unsafe URL etc).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I will continue to work to reduce the number of false positives and keep improving obfuscated codes detection.&lt;/p&gt;



&lt;p&gt;Please think to drop a star on github ❤️!&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/NodeSecure" rel="noopener noreferrer"&gt;
        NodeSecure
      &lt;/a&gt; / &lt;a href="https://github.com/NodeSecure/js-x-ray" rel="noopener noreferrer"&gt;
        js-x-ray
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      JavaScript &amp;amp; Node.js open-source SAST scanner. A static analyser for detecting most common malicious patterns 🔬.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;p&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/4438263/213887379-c873eb89-8786-4b5c-8a59-dcca49e01cb8.jpg"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F4438263%2F213887379-c873eb89-8786-4b5c-8a59-dcca49e01cb8.jpg" alt="@nodesecure/js-x-ray"&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
    &lt;a href="https://github.com/NodeSecure/js-x-ray" rel="noopener noreferrer"&gt;
      &lt;img src="https://camo.githubusercontent.com/2f50fb2c3897c7425c0ab3ed9c8e0bb5d9747c9d54c89e478f01eb8940609e7c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f64796e616d69632f6a736f6e2e7376673f7374796c653d666f722d7468652d62616467652675726c3d68747470733a2f2f7261772e67697468756275736572636f6e74656e742e636f6d2f4e6f64655365637572652f6a732d782d7261792f726566732f68656164732f6d61737465722f776f726b7370616365732f6a732d782d7261792f7061636b6167652e6a736f6e2671756572793d242e76657273696f6e266c6162656c3d56657273696f6e" alt="npm version"&gt;
    &lt;/a&gt;
    &lt;a href="https://github.com/NodeSecure/js-x-ray/blob/master/LICENSE" rel="noopener noreferrer"&gt;
      &lt;img src="https://camo.githubusercontent.com/a1cfb6cf43f37a7ae7f25341599401997596b0df3d1526d56b18c43f7c7b1604/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f4e6f64655365637572652f6a732d782d7261792e7376673f7374796c653d666f722d7468652d6261646765" alt="license"&gt;
    &lt;/a&gt;
    &lt;a href="https://api.securityscorecards.dev/projects/github.com/NodeSecure/js-x-ray" rel="nofollow noopener noreferrer"&gt;
      &lt;img src="https://camo.githubusercontent.com/6238a84a645fda299cf5ed18c005a1ed29244fe4d0ce73ef4d02f11c04a4af64/68747470733a2f2f6170692e736563757269747973636f726563617264732e6465762f70726f6a656374732f6769746875622e636f6d2f4e6f64655365637572652f6a732d782d7261792f62616467653f7374796c653d666f722d7468652d6261646765" alt="ossf scorecard"&gt;
    &lt;/a&gt;
    &lt;a href="https://github.com/NodeSecure/js-x-ray/actions?query=workflow%3A%22Node.js+CI%22" rel="noopener noreferrer"&gt;
      &lt;img src="https://camo.githubusercontent.com/1afbdbc19894f6c522e873d9345aaf3f62987fef18558408bcfdee4d5c2ed655/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f4e6f64655365637572652f6a732d782d7261792f6e6f64652e6a732e796d6c3f7374796c653d666f722d7468652d6261646765" alt="github ci workflow"&gt;
    &lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;JavaScript AST analysis. This package has been created to export the &lt;a href="https://github.com/NodeSecure/cli" rel="noopener noreferrer"&gt;NodeSecure&lt;/a&gt; AST analysis to enable better code evolution and allow better access to developers and researchers.&lt;/p&gt;
&lt;p&gt;The goal is to quickly identify dangerous code and patterns for developers and security researchers. Interpreting the results of this tool will still require you to have basic knowledge of secure coding.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Goals&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;The objective of the project is to detect potentially suspicious JavaScript code. The target is code that is added or injected for malicious purposes.&lt;/p&gt;

&lt;p&gt;Most of the time hackers will try to hide the behaviour of their code as much as possible to avoid being spotted or easily understood. The work of the library is to understand and analyze these patterns that will allow us to detect malicious code.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Feature Highlight&lt;/h2&gt;
&lt;/div&gt;


&lt;ul&gt;

&lt;li&gt;Retrieve required dependencies and files for Node.js&lt;/li&gt;

&lt;li&gt;Detect unsafe regular expressions&lt;/li&gt;

&lt;li&gt;Get warnings when the AST analysis detects a…&lt;/li&gt;

&lt;/ul&gt;
&lt;/div&gt;
&lt;br&gt;
  &lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/NodeSecure/js-x-ray" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;p&gt;That's it for today! Thanks for reading me 😉&lt;/p&gt;

</description>
      <category>gratitude</category>
    </item>
    <item>
      <title>Moving MyUnisoft Node.js back to TypeORM</title>
      <dc:creator>Thomas.G</dc:creator>
      <pubDate>Tue, 02 Aug 2022 18:53:46 +0000</pubDate>
      <link>https://dev.to/myunisoft/moving-myunisoft-nodejs-back-to-typeorm-3fok</link>
      <guid>https://dev.to/myunisoft/moving-myunisoft-nodejs-back-to-typeorm-3fok</guid>
      <description>&lt;p&gt;Hello 👋,&lt;/p&gt;

&lt;p&gt;Recently I took the time to reflect on my last two years at &lt;a href="https://www.myunisoft.fr/" rel="noopener noreferrer"&gt;MyUnisoft&lt;/a&gt;. I finally told myself that I wasn't writing enough about the difficulties we faced with my team 😊.&lt;/p&gt;

&lt;p&gt;Today I decided to write an article about our transition to &lt;a href="https://typeorm.io/" rel="noopener noreferrer"&gt;TypeORM&lt;/a&gt;. A choice we made over a year ago with my colleague &lt;a href="https://www.linkedin.com/in/alexandre-malaj-6062b0a6/" rel="noopener noreferrer"&gt;Alexandre MALAJ&lt;/a&gt; who joined a few months after me.&lt;/p&gt;

&lt;p&gt;We'll see why and how this choice allowed us to enhance the overall DX for my team 🚀. And that in the end it was a lot of trade-offs, and obviously, far from a perfect solution too.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔍 The problem
&lt;/h2&gt;

&lt;p&gt;At MyUnisoft we work with a &lt;a href="https://www.postgresql.org/" rel="noopener noreferrer"&gt;PostgreSQL&lt;/a&gt; database with static and dynamic &lt;a href="https://www.postgresql.org/docs/current/ddl-schemas.html" rel="noopener noreferrer"&gt;schema&lt;/a&gt; (each client is isolated in one schema). And uniquely without counting the duplication of the schemas we have about 500 tables.&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%2F0alcskihv2wz9z2p09ms.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%2F0alcskihv2wz9z2p09ms.png" alt="pgschemas" width="740" height="377"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;The Node.js stack was split into several services &lt;strong&gt;coupled to the database&lt;/strong&gt; (or to &lt;strong&gt;third-party&lt;/strong&gt; services for some of them). Developers before us were writing raw queries and there were no &lt;strong&gt;unit&lt;/strong&gt; or &lt;strong&gt;functional&lt;/strong&gt; tests 😬. When I took over as lead it was &lt;strong&gt;hell&lt;/strong&gt; to succeed in testing each service properly. Among the painful things 😱:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;strong coupling.&lt;/li&gt;
&lt;li&gt;heavy docker configuration&lt;/li&gt;
&lt;li&gt;complexity to generate business data for our tests.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We had to &lt;strong&gt;find a solution&lt;/strong&gt; to improve and secure our developments while &lt;strong&gt;iterating on production releases&lt;/strong&gt; 😵.&lt;/p&gt;

&lt;p&gt;Decentralizing with events wasn’t a possibility since of existing codes and dependencies (and we had no DevOps at the time).&lt;/p&gt;

&lt;h2&gt;
  
  
  💡 The solution
&lt;/h2&gt;

&lt;p&gt;We started thinking about &lt;strong&gt;creating an internal package&lt;/strong&gt; that would serve as an abstraction to interact with the database. We don't want to go for microservices 😉, so having a package that centralizes all this seems like a good compromise for us.&lt;/p&gt;

&lt;p&gt;Among our main objectives:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generate a compliant database &lt;strong&gt;locally&lt;/strong&gt; or on &lt;strong&gt;Docker&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Easily generate fake data.&lt;/li&gt;
&lt;li&gt;Built to allow us to carry out our &lt;strong&gt;functional and business tests&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Centralized code review (which also allows us to track changes more easily)&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;We also had ideas like building a schema in a running database (which could be used for partner API testing and sandboxing).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The question remained whether we should &lt;strong&gt;continue writing raw queries&lt;/strong&gt; or not 😨. I'm not necessarily a big fan of ORMs, but we had a diversity of tables and requirements that made the writing of raw queries complicated at time.&lt;/p&gt;

&lt;p&gt;We looked at the different solutions in the ecosystem by checking our constraints with the schemas. After must research, we concluded that &lt;strong&gt;TypeORM was viable&lt;/strong&gt; (other ORM had critical issues).&lt;/p&gt;

&lt;p&gt;Far from perfect, but we had to &lt;strong&gt;give it a try&lt;/strong&gt; 💃!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: with hindsight, we are now also very interested in &lt;a href="https://massivejs.org/" rel="noopener noreferrer"&gt;Massive.js&lt;/a&gt;. This could have been one of our choices.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  🐥 Let the story begin
&lt;/h2&gt;

&lt;h3&gt;
  
  
  👶 Baby steps
&lt;/h3&gt;

&lt;p&gt;My colleague Alexandre spent several months migrating the database on TypeORM 😮. I helped him by reviewing each table and relations.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We did not opt for a migration script at the time (for us the choice was still too vague).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We have made a gource to illustrate our work:&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/5rF6ogvUDiA"&gt;
&lt;/iframe&gt;
&lt;br&gt;
One of the problems we quickly encountered was that it was not possible to use the ActiveRecord pattern with dynamic schemas 😭. However this is ok for static schema because you can define them with the &lt;code&gt;@Entity&lt;/code&gt; decorator.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Entity&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sch_interglobal&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;JefactureWebhook&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;BaseEntity&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The management of datasources (connection) by schema/client was a bit infernal. We created our &lt;strong&gt;abstraction on top of TypeORM&lt;/strong&gt; to handle all this properly and regarding our schema initialization requirements.&lt;/p&gt;

&lt;p&gt;One of our encounters being quite complicated has been to &lt;strong&gt;clone a schema when we add a new client on the fly&lt;/strong&gt; 🐝(that's something we do in our tests, in the authentication service for example).&lt;/p&gt;

&lt;p&gt;We were able to achieve this by using the &lt;code&gt;@EventSubscriber&lt;/code&gt; decorator on a static table we use to register new customers’ information.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;EventSubscriber&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Sub_GroupeMembre&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;listenTo&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;Entities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;schInterglobal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GroupeMembre&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;afterInsert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UpdateEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;idGroupeMembre&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;entity&lt;/span&gt;&lt;span class="o"&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;queryManager&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;datasources&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="s2"&gt;default&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;queryManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s2"&gt;`SELECT clone_schema('sch1', 'sch&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;idGroupeMembre&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await &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;DataSource&lt;/span&gt;&lt;span class="p"&gt;({})).&lt;/span&gt;&lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;datasources&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`sch&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;idGroupeMembre&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;connection&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;The tricky part was &lt;strong&gt;to build an SQL script to properly clone a schema&lt;/strong&gt; with all tables, relations, foreign keys etc.. But after many difficulties we still managed to get out of it 😅.&lt;/p&gt;

&lt;h3&gt;
  
  
  📜 Blueprints
&lt;/h3&gt;

&lt;p&gt;When I started this project I was inspired by &lt;a href="https://github.com/adonisjs/lucid" rel="noopener noreferrer"&gt;Lucid&lt;/a&gt; which is the ORM of the &lt;a href="https://adonisjs.com/" rel="noopener noreferrer"&gt;Adonis.js framework&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;By the way, Lucid was &lt;strong&gt;one of our choices&lt;/strong&gt;, but like many of &lt;a href="https://twitter.com/amanvirk1" rel="noopener noreferrer"&gt;Harminder&lt;/a&gt;'s packages, it is sometimes difficult to &lt;strong&gt;use them outside of Adonis&lt;/strong&gt; (which is not a criticism, it is sometimes understandable when the goal is to build a great DX for a framework).&lt;/p&gt;

&lt;p&gt;But I was quite a fan of Lucid's &lt;strong&gt;factory API&lt;/strong&gt; so we built &lt;strong&gt;an equivalent&lt;/strong&gt; with TypeORM that we called "Blueprint".&lt;/p&gt;

&lt;p&gt;Here is an example of a blueprint:&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;new&lt;/span&gt; &lt;span class="nx"&gt;Blueprint&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IConnectorLogs&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;ConnectorLogsEntity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;faker&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;severity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;faker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;helpers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arrayElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;connectorLogSeverities&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;faker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lorem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sentence&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;public&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;faker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;datatype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;requestId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;faker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;datatype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;readedAt&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;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;faker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;past&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;thirdPartyId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;faker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;datatype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;min&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;max&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; 
    &lt;span class="p"&gt;})),&lt;/span&gt;
    &lt;span class="na"&gt;idSociete&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The callback includes the faker lib as well as internal custom functions to generate accounting data. You can use this blueprint to generate data like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&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;Blueprints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ConnectorLogs&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;readedAt&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;Date&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;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The API is similar but it appears our objectives and TypeORM forced us to make different choices.&lt;/p&gt;

&lt;h4&gt;
  
  
  ES6 Proxy usage
&lt;/h4&gt;

&lt;p&gt;You may have noticed but something is weird with this API. Every time you hit &lt;code&gt;Blueprints.sch&lt;/code&gt; it triggers an &lt;strong&gt;ES6 proxy trap&lt;/strong&gt; that will return a new instance of a given Blueprint.&lt;/p&gt;

&lt;p&gt;It was quite satisfying for me to manage to use a Proxy for a real need and at the same time manage to return the right type with TypeScript.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;schBlueprints&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;./sch/index&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Blueprint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;EntityBlueprint&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../blueprint&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// CONSTANTS&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;kProxyHandler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;prop&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;EmulateBlueprint&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Blueprint&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;E&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;S&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;EntityBlueprint&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;E&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;S&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;never&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;DeepEmulateBlueprint&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Blueprints&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;Blueprints&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;EmulateBlueprint&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Blueprints&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="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Proxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;schBlueprints&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;kProxyHandler&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;DeepEmulateBlueprint&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;schBlueprints&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  📟 Seeder
&lt;/h3&gt;

&lt;p&gt;We worked from the beginning of the project to build a relatively simple seeding API. The idea was mainly to be able to generate the static data required for our services to work properly.&lt;/p&gt;

&lt;p&gt;Here's an example of one simple seed script that generates static data with a blueprint:&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="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SeederRunOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;seeder&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;seeder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sch_global.profil&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PersPhysique&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;doubleAuthRecoveryCodes&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createMany&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;seeder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;loadedTable&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tableName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When we generate a new database locally or in Docker we can see the execution of all the seeds:&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%2Folg9hgfmyu82ovwv0tly.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%2Folg9hgfmyu82ovwv0tly.png" alt="seeder" width="800" height="458"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  🌀 Docker and testcontainers
&lt;/h3&gt;

&lt;p&gt;When &lt;a href="https://www.linkedin.com/in/tonygorez/" rel="noopener noreferrer"&gt;Tony Gorez&lt;/a&gt; was still working with us at MyUnisoft he was one of the first to work around how we can set up our tests inside a Docker and run them in our GitLab CI.&lt;/p&gt;

&lt;p&gt;The execution of our tests was relatively long (time to build the Docker etc). That's when he told us about something a friend had recommended to him: &lt;a href="https://github.com/testcontainers/testcontainers-node" rel="noopener noreferrer"&gt;testcontainers&lt;/a&gt; for Node.js.&lt;/p&gt;

&lt;p&gt;Once set up but what a magical feeling... The execution of our tests was faster by a ratio of 4x. Tony has been a great help and his work has &lt;strong&gt;allowed us to build the foundation&lt;/strong&gt; of the tests for our services.&lt;/p&gt;

&lt;p&gt;On my side I worked on an internal abstraction allowing everyone not to lose time on setup:&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="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dotenv&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;config&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;testcontainers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@myunisoft/testcontainers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kr"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;globalSetup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;testcontainers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;containers&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;Set&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;postgres&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;redis&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
    &lt;span class="na"&gt;pgInitOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;seedsOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;tables&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sch_interglobal/groupeMembre&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;sch_global/thirdPartyApiCategory&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  📦 Difficulties with a package 😱
&lt;/h3&gt;

&lt;p&gt;Not everything in the process goes smoothly 😕. In the beginning, it was really difficult to manage the versioning. We used to use npm link a lot to work with our local projects but it was far from perfect (it was more like hell 😈).&lt;/p&gt;

&lt;p&gt;And by the way, you have to be very careful with everything related to NPM &lt;strong&gt;peerDependencies&lt;/strong&gt; (especially with TypeScript). If you use a version of typeorm in the package, you necessarily must use the same one in the service otherwise you will have problems with types that do not match.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"peerDependencies"&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;"@myunisoft/postgre-installer"&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.12.1"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We had the same issue with our internal Fastify plugin. It cost us a few days sometimes the time to understand that we had screwed up well on the subject 🙈.&lt;/p&gt;

&lt;p&gt;In the end, after some stabilizations, we could release new versions very quickly.&lt;/p&gt;

&lt;p&gt;I'm not necessarily completely satisfied with the DX on this subject at the moment and I'm thinking of improving it with automatic releases using our commits.&lt;/p&gt;

&lt;h3&gt;
  
  
  Others APIs
&lt;/h3&gt;

&lt;p&gt;I couldn't even cover everything because this project is so large. For example, we have a snapshot API that allows us to save and delete data during our tests...&lt;/p&gt;

&lt;p&gt;Speaking of tests, it is always difficult to give you examples without being boring. But there too the work was colossal.&lt;/p&gt;

&lt;p&gt;I would like to underline the work of &lt;a href="https://www.linkedin.com/in/cedric-lionnet-578845121/" rel="noopener noreferrer"&gt;Cédric Lionnet&lt;/a&gt; who has always been at the forefront when it came to solidifying our tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  💸 Hard work pays off
&lt;/h2&gt;

&lt;p&gt;After one year of hard work the project is starting to be actively used by the whole team across all HTTP services 😍. Everyone starts to actively contribute (and a dozen developers on a project is a pretty interesting strike force ⚡).&lt;/p&gt;

&lt;p&gt;Sure we had a lot of &lt;strong&gt;issues&lt;/strong&gt; but we managed to solve them one by one 💪 (I'm not even talking about the migration to TypeORM 3.x 😭).&lt;/p&gt;

&lt;p&gt;But thanks to our effort, we are finally able to significantly improve the testing within our Node.js services. We can also start to work in localhost whereas before, developers used remote environments.&lt;/p&gt;

&lt;p&gt;In two years we have managed to recreate a healthy development environment with good practices and unit and functional testing on almost all our projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  📢 My take on TypeORM
&lt;/h2&gt;

&lt;p&gt;If I were in the same situation tomorrow I would probably try another way/solution (like &lt;a href="https://massivejs.org/" rel="noopener noreferrer"&gt;Massive.js&lt;/a&gt;). For example, TypeORM poor performance will probably be a topic in the future for my team.&lt;/p&gt;

&lt;p&gt;As I said at the beginning, I'm not a fan of ORMs and in the context of personal projects, I do without them almost all the time.&lt;/p&gt;

&lt;p&gt;However, I must admit that we succeeded with TypeORM and that the result is not too bad either. There is probably no silver bullet 🤷.&lt;/p&gt;

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

&lt;p&gt;Many engineers would have given up at the beginning thinking that it would not be worth the energy to fight 😰.&lt;/p&gt;

&lt;p&gt;It's a bit simple to always want to start from scratch 😝. For me it was a challenge, to face reality which is sometimes hard to accept and forces us to make different choices 😉.&lt;/p&gt;

&lt;p&gt;It was also a great team effort with a lot of trusts 👯. We had invested a lot and as a lead I was afraid I had made the wrong choice. But with &lt;strong&gt;Alexandre&lt;/strong&gt; it is always a pleasure to see that today all this pays off.&lt;/p&gt;




&lt;p&gt;I'm not quoting everyone but thanks to those who actively helped and worked on the project especially in the early stage.&lt;/p&gt;

&lt;p&gt;Thanks for reading and as usual see you soon for a new article 😘&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>typescript</category>
      <category>postgres</category>
    </item>
    <item>
      <title>NodeSecure Vuln-era</title>
      <dc:creator>Thomas.G</dc:creator>
      <pubDate>Thu, 21 Jul 2022 08:15:29 +0000</pubDate>
      <link>https://dev.to/nodesecure/announcing-nodesecure-vulnera-22a6</link>
      <guid>https://dev.to/nodesecure/announcing-nodesecure-vulnera-22a6</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;😍 Logo and cover by our beloved &lt;a href="https://www.linkedin.com/in/mehdi-bouchard/" rel="noopener noreferrer"&gt;medhi bouchard&lt;/a&gt; ❤️&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Hello 👋,&lt;/p&gt;

&lt;p&gt;Back for a little article about the rebranding of one of the NodeSecure tools: &lt;a href="https://github.com/NodeSecure/vuln" rel="noopener noreferrer"&gt;Vulnera&lt;/a&gt; (previously &lt;em&gt;vuln&lt;/em&gt;, the &lt;strong&gt;vuln-era&lt;/strong&gt; has begun!).&lt;/p&gt;

&lt;p&gt;An opportunity for me to also write about this wonderful project that was born with the redesign of the back-end less than a year ago ⌚. If you don't remember I wrote an article:&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/nodesecure" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&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%2Forganization%2Fprofile_image%2F6062%2F43c0ffd0-bc13-4c49-8846-ce3efbdafd52.png" alt="NodeSecure" width="200" height="200"&gt;
      &lt;div class="ltag__link__user__pic"&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%2Fuser%2Fprofile_image%2F314815%2F128a0b56-a103-4bc8-92b6-ce3738e98770.jpg" alt="" width="400" height="400"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/nodesecure/announcing-new-node-secure-back-end-1dp9" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Announcing new NodeSecure back-end&lt;/h2&gt;
      &lt;h3&gt;Thomas.G for NodeSecure ・ Sep 11 '21&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#node&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#security&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;Don't wait and dive in 🌊 with me to discover this tool 💃.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Vulnera ? 👀
&lt;/h2&gt;

&lt;p&gt;Vulnera is a package that allows you to &lt;strong&gt;programmatically&lt;/strong&gt; fetch your Node.js project vulnerabilities from &lt;strong&gt;multiple sources or strategies&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;NPM Audit (&lt;a href="https://github.com/advisories" rel="noopener noreferrer"&gt;Github Advisory Database&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ossindex.sonatype.org/" rel="noopener noreferrer"&gt;Sonatype OSS Index&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;deprecated&lt;/code&gt; &lt;a href="https://github.com/nodejs/security-wg/tree/main/vuln" rel="noopener noreferrer"&gt;Node.js Security WG Database&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Snyk&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;📢 Feel free to push new sources (we have &lt;a href="https://github.com/NodeSecure/vuln/blob/main/docs/adding_new_strategy.md" rel="noopener noreferrer"&gt;a guide&lt;/a&gt; on how to add/contribute one).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The code was originally designed for vulnerability management within the &lt;a href="https://github.com/NodeSecure/scanner" rel="noopener noreferrer"&gt;Scanner&lt;/a&gt;. Yet, its API is &lt;strong&gt;evolving&lt;/strong&gt; with the objective of making it a &lt;strong&gt;full-fledged project&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;vulnera&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;@nodesecure/vulnera&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;def&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;vulnera&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setStrategy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;vulnera&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;strategies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NPM_AUDIT&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;vulnerabilities&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;def&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getVulnerabilities&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;useStandardFormat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;vulnerabilities&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Standard vulnerability format 👯
&lt;/h3&gt;

&lt;p&gt;We have created a standard format to reconcile the different sources.&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="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;StandardVulnerability&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/** Unique identifier for the vulnerability **/&lt;/span&gt;
  &lt;span class="nl"&gt;id&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="cm"&gt;/** Vulnerability origin, either Snyk, NPM or NodeSWG **/&lt;/span&gt;
  &lt;span class="nl"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Origin&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="cm"&gt;/** Package associated with the vulnerability **/&lt;/span&gt;
  &lt;span class="nl"&gt;package&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="cm"&gt;/** Vulnerability title **/&lt;/span&gt;
  &lt;span class="nl"&gt;title&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="cm"&gt;/** Vulnerability description **/&lt;/span&gt;
  &lt;span class="nl"&gt;description&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="cm"&gt;/** Vulnerability link references on origin's website **/&lt;/span&gt;
  &lt;span class="nl"&gt;url&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="cm"&gt;/** Vulnerability severity levels given the strategy **/&lt;/span&gt;
  &lt;span class="nl"&gt;severity&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;Severity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="cm"&gt;/** Common Vulnerabilities and Exposures dictionary */&lt;/span&gt;
  &lt;span class="nl"&gt;cves&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="cm"&gt;/** Common Vulnerability Scoring System (CVSS) **/&lt;/span&gt;
  &lt;span class="nl"&gt;cvssVector&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="cm"&gt;/** CVSS Score **/&lt;/span&gt;
  &lt;span class="nl"&gt;cvssScore&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="cm"&gt;/** The range of vulnerable versions */&lt;/span&gt;
  &lt;span class="nl"&gt;vulnerableRanges&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="cm"&gt;/** The set of versions that are vulnerable **/&lt;/span&gt;
  &lt;span class="nl"&gt;vulnerableVersions&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="cm"&gt;/** The set of versions that are patched **/&lt;/span&gt;
  &lt;span class="nl"&gt;patchedVersions&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="cm"&gt;/** Overview of available patches **/&lt;/span&gt;
  &lt;span class="nl"&gt;patches&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;Patch&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;You can always use the original formats of each source of course 😊. We have implemented and exposed &lt;strong&gt;TypeScript interfaces&lt;/strong&gt; for each of them.&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%2F0eqb67gqp772iawr10gp.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%2F0eqb67gqp772iawr10gp.png" alt="NodeSecure types" width="311" height="183"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Usage in Scanner 🔬
&lt;/h2&gt;

&lt;p&gt;On the scanner we have all the necessary information because we go through the dependency tree 🎄. At &lt;a href="https://github.com/NodeSecure/scanner/blob/master/src/depWalker.js#L297" rel="noopener noreferrer"&gt;the end of the process&lt;/a&gt;, we recover all vulnerabilities by iterating &lt;strong&gt;spec&lt;/strong&gt; by &lt;strong&gt;spec&lt;/strong&gt; within the &lt;strong&gt;hydratePayloadDependencies&lt;/strong&gt; strategy method.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;hydratePayloadDependencies&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;strategy&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;vulnera&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setStrategy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;userStrategyName&lt;/span&gt; &lt;span class="c1"&gt;// SNYK for example&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;hydratePayloadDependencies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dependencies&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;useStandardFormat&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;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;location&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vulnerabilityStrategy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;strategy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The following diagram explains the overall behavior and interactions between the Scanner and Vulnera.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6x76ry38w7qcitdulayh.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%2F6x76ry38w7qcitdulayh.png" alt="NodeSecure" width="800" height="590"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you want to learn more about the Payload you can check the TypeScript interface &lt;a href="https://github.com/NodeSecure/scanner/blob/master/types/scanner.d.ts#L132" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  What's next ? 🚀
&lt;/h2&gt;

&lt;p&gt;Some sources are more difficult to exploit than others (for NPM we use &lt;a href="https://www.npmjs.com/package/@npmcli/arborist" rel="noopener noreferrer"&gt;Arborist&lt;/a&gt; which simplifies our lives).&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;vulnerabilities&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;arborist&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;audit&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;toJSON&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;However, we have to think and create mechanics to exploit sources like Sonatype 😨. This is required for API like &lt;code&gt;getVulnerabilities()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Among the major subjects and &lt;strong&gt;ideas&lt;/strong&gt; we are working on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a &lt;strong&gt;private&lt;/strong&gt; database to benchmark the sources between them (see &lt;a href="https://github.com/NodeSecure/vulnera/issues/29" rel="noopener noreferrer"&gt;#29&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Merging multiple sources in one (see &lt;a href="https://github.com/NodeSecure/vulnera/issues/25" rel="noopener noreferrer"&gt;#25&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Fetch vulnerabilities of a given remote package (with support for private registry like &lt;a href="https://verdaccio.org/" rel="noopener noreferrer"&gt;verdaccio&lt;/a&gt;). At the moment we only support the analysis of a local manifest or a payload of the scanner.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Credits 🙇
&lt;/h2&gt;

&lt;p&gt;This project owes much to our core collaborator &lt;a href="https://www.linkedin.com/in/antoine-coulon-b29934153/" rel="noopener noreferrer"&gt;Antoine COULON&lt;/a&gt; who invested a lot of energy to improve it 💪.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Fun fact:&lt;/strong&gt; &lt;a href="https://github.com/NodeSecure/cli/commit/236c7333720b14878b5f620f3a814c045a375a45" rel="noopener noreferrer"&gt;its first contribution&lt;/a&gt; 🐤 on NodeSecure was also on the old version of the code Scanner that managed vulnerabilities.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But I don't forget individual contributions 👏&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.linkedin.com/in/mathieu-kahlaoui-0887a1158/" rel="noopener noreferrer"&gt;Mathieu Kahlaoui&lt;/a&gt; for adding &lt;a href="https://github.com/NodeSecure/vuln/pull/33" rel="noopener noreferrer"&gt;the getVulnerabilities() API&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.linkedin.com/in/oleh-sych-41245116a/" rel="noopener noreferrer"&gt;Oleh Sych&lt;/a&gt; for adding &lt;a href="https://github.com/NodeSecure/vuln/pull/11" rel="noopener noreferrer"&gt;Snyk strategy&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Medhi for his work on the logo&lt;/li&gt;
&lt;/ul&gt;




&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/NodeSecure" rel="noopener noreferrer"&gt;
        NodeSecure
      &lt;/a&gt; / &lt;a href="https://github.com/NodeSecure/vulnera" rel="noopener noreferrer"&gt;
        vulnera
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Programmatically fetch security vulnerabilities with one or many strategies (NPM Audit, Sonatype, Snyk, Node.js DB).
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;p&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/43391199/180091156-9cf883b3-05bc-4c69-9943-3d1168818fab.png"&gt;&lt;img alt="vulnera" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F43391199%2F180091156-9cf883b3-05bc-4c69-9943-3d1168818fab.png" width="650"&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
    &lt;a href="https://github.com/NodeSecure/vulnera" rel="noopener noreferrer"&gt;
      &lt;img src="https://camo.githubusercontent.com/2e96c2010c3fa1ccaa8f1d3bf68e99b3a8b36cb5056fe9f60dd4a638fc2a89b2/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f7061636b6167652d6a736f6e2f762f4e6f64655365637572652f76756c6e6572613f7374796c653d666f722d7468652d6261646765" alt="npm version"&gt;
    &lt;/a&gt;
    &lt;a href="https://github.com/NodeSecure/vulnera" rel="noopener noreferrer"&gt;
      &lt;img src="https://camo.githubusercontent.com/01eb01b5f1cd10012b69a2fc5ed293d7cc4d983de582c7635c73e68bfdf23679/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f4e6f64655365637572652f76756c6e6572613f7374796c653d666f722d7468652d6261646765" alt="license"&gt;
    &lt;/a&gt;
    &lt;a href="https://api.securityscorecards.dev/projects/github.com/NodeSecure/vulnera" rel="nofollow noopener noreferrer"&gt;
      &lt;img src="https://camo.githubusercontent.com/0ac3dc0152a1eeeaced5c88b503c080a77dc1e559f088d8f1b4be98983600536/68747470733a2f2f6170692e736563757269747973636f726563617264732e6465762f70726f6a656374732f6769746875622e636f6d2f4e6f64655365637572652f76756c6e6572612f62616467653f7374796c653d666f722d7468652d6261646765" alt="ossf scorecard"&gt;
    &lt;/a&gt;
    &lt;a href="https://github.com/NodeSecure/vulnera/actions?query=workflow%3A%22Node.js+CI%22" rel="noopener noreferrer"&gt;
      &lt;img src="https://camo.githubusercontent.com/e3655b307d0b6e23682d00de3ec3e8175f13e4e04048229f0b0671661869744b/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f4e6f64655365637572652f76756c6e6572612f6d61696e2e796d6c3f7374796c653d666f722d7468652d6261646765" alt="github ci workflow"&gt;
    &lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;vuln-&lt;em&gt;era&lt;/em&gt;&lt;/strong&gt; has begun! Programmatically fetch security vulnerabilities with one or many strategies. Originally designed to run and analyze &lt;a href="https://github.com/NodeSecure/scanner" rel="noopener noreferrer"&gt;Scanner&lt;/a&gt; dependencies it now also runs independently from an npm Manifest.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Requirements&lt;/h2&gt;
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://nodejs.org/en/" rel="nofollow noopener noreferrer"&gt;Node.js&lt;/a&gt; v22 or higher&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Getting Started&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;This package is available in the Node Package Repository and can be easily installed with &lt;a href="https://docs.npmjs.com/getting-started/what-is-npm" rel="nofollow noopener noreferrer"&gt;npm&lt;/a&gt; or &lt;a href="https://yarnpkg.com" rel="nofollow noopener noreferrer"&gt;yarn&lt;/a&gt;.&lt;/p&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;$ npm i @nodesecure/vulnera
&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; or&lt;/span&gt;
$ yarn add @nodesecure/vulnera&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Usage example&lt;/h2&gt;
&lt;/div&gt;

&lt;div class="highlight highlight-source-js notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;import&lt;/span&gt; &lt;span class="pl-c1"&gt;*&lt;/span&gt; &lt;span class="pl-k"&gt;as&lt;/span&gt; &lt;span class="pl-s1"&gt;vulnera&lt;/span&gt; &lt;span class="pl-k"&gt;from&lt;/span&gt; &lt;span class="pl-s"&gt;"@nodesecure/vulnera"&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;

&lt;span class="pl-k"&gt;await&lt;/span&gt; &lt;span class="pl-s1"&gt;vulnera&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;setStrategy&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;
  &lt;span class="pl-s1"&gt;vulnera&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;strategies&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;GITHUB_ADVISORY&lt;/span&gt;
&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;

&lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;definition&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-k"&gt;await&lt;/span&gt; &lt;span class="pl-s1"&gt;vulnera&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;getStrategy&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-smi"&gt;console&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;log&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;definition&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;strategy&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;

&lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;vulnerabilities&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-k"&gt;await&lt;/span&gt; &lt;span class="pl-s1"&gt;definition&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;getVulnerabilities&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;process&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;cwd&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt;
  &lt;span class="pl-c1"&gt;useFormat&lt;/span&gt;: &lt;span class="pl-s"&gt;"Standard"&lt;/span&gt;
&lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-smi"&gt;console&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;log&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;vulnerabilities&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Available strategy&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;The default strategy is &lt;strong&gt;NONE&lt;/strong&gt; which mean no strategy at all (we execute…&lt;/p&gt;
&lt;/div&gt;


&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/NodeSecure/vulnera" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;p&gt;Thanks 🙏 for reading me and see you soon for another article!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>security</category>
      <category>opensource</category>
    </item>
    <item>
      <title>NodeSecure CLI v2.0.0</title>
      <dc:creator>Thomas.G</dc:creator>
      <pubDate>Wed, 29 Jun 2022 09:48:03 +0000</pubDate>
      <link>https://dev.to/nodesecure/nodesecure-cli-v200-2ai7</link>
      <guid>https://dev.to/nodesecure/nodesecure-cli-v200-2ai7</guid>
      <description>&lt;p&gt;Hello 👋,&lt;/p&gt;

&lt;p&gt;I am writing this article with excitement and after several months of work. With the &lt;a href="https://github.com/NodeSecure/Governance#team" rel="noopener noreferrer"&gt;core team&lt;/a&gt; we are thrilled to announce that we are publishing a &lt;strong&gt;new version&lt;/strong&gt; of the UI.🚀.&lt;/p&gt;

&lt;p&gt;As you are reading these lines I am probably under the sun ☀️ of Tel Aviv for the &lt;a href="https://www.nodetlv.com/" rel="noopener noreferrer"&gt;NodeTLV&lt;/a&gt; conference where I will give a talk about &lt;strong&gt;NodeSecure&lt;/strong&gt; and some other tools.&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%2F3wspz0krx3g44jicz6v6.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3wspz0krx3g44jicz6v6.JPG" alt="NodeSecure" width="800" height="450"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;What an incredible journey 😍. Four years ago I was working on my tool alone 😥... But now more than a dozen developers are contributing to the project and I can only thank all of you for your precious support 🙏.&lt;/p&gt;

&lt;p&gt;If you are new, then let me introduce you to the project&lt;/p&gt;

&lt;h2&gt;
  
  
  🐤 Getting started with NodeSecure
&lt;/h2&gt;

&lt;p&gt;NodeSecure is an organization gathering a lot of individual projects that will allow you to improve the &lt;strong&gt;security&lt;/strong&gt; and &lt;strong&gt;quality&lt;/strong&gt; of your projects 💪. With our tools you can &lt;strong&gt;visually&lt;/strong&gt; discover the dependencies you use on a daily basis and &lt;strong&gt;learn&lt;/strong&gt; more about them 📚.&lt;/p&gt;

&lt;p&gt;Our most notable project is:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/NodeSecure" rel="noopener noreferrer"&gt;
        NodeSecure
      &lt;/a&gt; / &lt;a href="https://github.com/NodeSecure/cli" rel="noopener noreferrer"&gt;
        cli
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      JavaScript security CLI that allow you to deeply analyze the dependency tree of a given package or local Node.js project.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;
  🐢 Node-Secure CLI 🚀
&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;
  a Node.js CLI to deeply analyze the dependency tree of a given NPM package or Node.js local app
&lt;/p&gt;

&lt;p&gt;
    &lt;a href="https://www.npmjs.com/package/@nodesecure/cli" rel="nofollow noopener noreferrer"&gt;
      &lt;img src="https://camo.githubusercontent.com/a2426a4c17f17920d8d729dc7e98151a94f25c34f3536685646337c36f225f8b/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f7061636b6167652d6a736f6e2f762f4e6f64655365637572652f636c693f7374796c653d666f722d7468652d6261646765" alt="npm version"&gt;
    &lt;/a&gt;
    &lt;a href="https://github.com/NodeSecure/cli/blob/master/LICENSE" rel="noopener noreferrer"&gt;
      &lt;img src="https://camo.githubusercontent.com/5da5c30577f938d3dce222a1cd9d4d257cf23be56461f4e22e943d5c7d1b4989/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f4e6f64655365637572652f636c693f7374796c653d666f722d7468652d6261646765" alt="license"&gt;
    &lt;/a&gt;
    &lt;a href="https://api.securityscorecards.dev/projects/github.com/NodeSecure/cli" rel="nofollow noopener noreferrer"&gt;
      &lt;img src="https://camo.githubusercontent.com/3e6ab8f0f8cc6b7d582fcfce4ed96a022e8b55d384a839376f82848d2dbde16d/68747470733a2f2f6170692e736563757269747973636f726563617264732e6465762f70726f6a656374732f6769746875622e636f6d2f4e6f64655365637572652f636c692f62616467653f7374796c653d666f722d7468652d6261646765" alt="ossf scorecard"&gt;
    &lt;/a&gt;
    &lt;a href="https://slsa.dev/spec/v1.0/levels#build-l3" rel="nofollow noopener noreferrer"&gt;
      &lt;img src="https://camo.githubusercontent.com/6e438d6195129c64136dc231d2226d729f1a537223b8bc475907a28a17b31784/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f534c53412d6c6576656c253230332d677265656e3f7374796c653d666f722d7468652d6261646765266c6f676f3d646174613a696d6167652f706e673b6261736536342c6956424f5277304b47676f414141414e5355684555674141414134414141414f43414d414141416f6c74336a414141414247644254554541414c4750432f7868425141414143426a53464a4e414142364a6741416749514141506f41414143413641414164544141414f7067414141366d41414146334363756c4538414141424d6c424d564558764d5144764d4144774d5144774d4144774d4144764d4144764d4144774d4144774d5144764d5144764d5144774d4144774d4144764d4144774d4144774d4144774d5144764d5144764d5144774d5144764d5144774d5144774d4144774d4144774d5144774d4144774d4144764d4144764d5144764d5144774d4144774d5144774d4144764d5144774d4144774d5144774d4144774d4144774d4144774d4144774d4144774d4144764d5144764d5144774d4144774d5144774d4144764d5144764d5144774d4144764d5144764d5144774d4144774d5144774d5144774d5144764d5144774d4144764d4144774d4144774d5144764d5144774d4144774d5144774d5144774d5144774d5144764d5144764d5144764d4144774d4144764d4144764d4144764d4144774d5144774d5144764d4144764d5144764d5144764d4144764d4144764d5144774d5144764d5144764d4144764d4144764d4144764d5144774d5144764d5144764d5144764d4144764d4144774d4144764d5144764d5144764d5144764d4144774d4144774d5144774d4141414141412f486f53774141414159335253546c4d7073766e65516c5172552f4c5153577a764d35447a6d7a65463950692b4e367676726b39487550336173546150676b56466d4f337255724d6a71764c3664304c4c54566a492f50754d514e53474f57612f365955387a4e75444c69684a306536614d477a6c38733249543762366c49466b526a316d74765130654a5739357247302b5369643539782f4141414141574a4c5230526c746432496e77414141416c7753466c7a4141414f7777414144734d4278322b6f5a4141414141643053553146422b594847673074474c725461443441414143715355524256416a58593242675a45714741475957566a59476467346f6a354f4c6d346552675a63764263546846784155456b3457595241564530394f6c7043556b706152545536575930695756314255556c5a5256514d715564646753453757314e4c5331674670304e585442334b544451794e6a4532536b30334e7a4331413347523153797472473173376534646b426f67746a6b374f4c713575795443757534656e6c336379684f766a3636667648784149456d59494367344a4451755069415172456d4749696f364a6a5a4f464f6a536567534842424d704f546f78504167434a66445a432f6d324b4867414141435630525668305a4746305a54706a636d5668644755414d6a41794d6930774e7930794e6c51784d7a6f304e546f794e4373774d446f774d43384179776f414141416c6445565964475268644755366257396b61575a35414449774d6a49744d4463744d6a5a554d544d364e4455364d6a51724d4441364d44426558584f3241414141475852465748525462325a30643246795a514233643363756157357263324e686347557562334a6e6d2b3438476741414141424a52553545726b4a6767673d3d" alt="slsa level3"&gt;
    &lt;/a&gt;
    &lt;a href="https://github.com/NodeSecure/cli/actions?query=workflow%3A%22Node.js+CI%22" rel="noopener noreferrer"&gt;
      &lt;img src="https://camo.githubusercontent.com/39d2523d233101413839edd6d704102daf3580b0031f122569b56533ef1a7f18/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f4e6f64655365637572652f636c692f6e6f64656a732e796d6c3f7374796c653d666f722d7468652d6261646765" alt="github ci workflow"&gt;
    &lt;/a&gt;
    &lt;a href="https://codecov.io/github/NodeSecure/cli" rel="nofollow noopener noreferrer"&gt;
      &lt;img src="https://camo.githubusercontent.com/a79cb943ea84e1a608d0609b6fb87de203ff3f148b45126cd3f7aa64f79984aa/68747470733a2f2f696d672e736869656c64732e696f2f636f6465636f762f632f6769746875622f4e6f64655365637572652f636c693f7374796c653d666f722d7468652d6261646765" alt="codecov"&gt;
    &lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
&lt;a rel="noopener noreferrer" href="https://github.com/NodeSecure/cli/./docs/ui-preview.PNG"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FNodeSecure%2Fcli%2F.%2Fdocs%2Fui-preview.PNG"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;📜 Features&lt;/h2&gt;
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Run a static scan on every JavaScript files and sort out warnings (unsafe-regex, unsafe-import etc) and the complete list of required expr and statements (files, node.js module, etc.).&lt;/li&gt;
&lt;li&gt;Return complete composition for each packages (extensions, files, tarball size, etc).&lt;/li&gt;
&lt;li&gt;Packages metadata from the npm registry API (number of releases, last publish date, maintainers etc).&lt;/li&gt;
&lt;li&gt;Search for licenses files in the tarball and return the &lt;a href="https://spdx.org/licenses/" rel="nofollow noopener noreferrer"&gt;SPDX&lt;/a&gt; expression conformance of each detected licenses.&lt;/li&gt;
&lt;li&gt;Link vulnerabilities from the multiple sources like GitHub Advisory, Sonatype or Snyk using &lt;a href="https://github.com/NodeSecure/vulnera" rel="noopener noreferrer"&gt;Vulnera&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Add flags (emojis) to each packages versions to identify well known patterns and potential security threats easily.&lt;/li&gt;
&lt;li&gt;First-class support of open source security initiatives like &lt;a href="https://github.com/ossf/scorecard" rel="noopener noreferrer"&gt;OpenSSF Scorecard&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Generate security report (PDF).&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;🚧 Requirements&lt;/h2&gt;
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://nodejs.org/en/" rel="nofollow noopener noreferrer"&gt;Node.js&lt;/a&gt; v22 or higher&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;💃 Getting&lt;/h2&gt;…&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/NodeSecure/cli" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;How can you use it? It's easy, you just have to install globally the CLI with npm:&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;$ &lt;/span&gt;npm i @nodesecure/cli &lt;span class="nt"&gt;-g&lt;/span&gt;

&lt;span class="c"&gt;# Analyze a remote package on the NPM Registry.&lt;/span&gt;
&lt;span class="c"&gt;# Note: also work with a private registry like gitlab or verdaccio&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;nsecure auto fastify

&lt;span class="c"&gt;# Analyze a local manifest (or local project).&lt;/span&gt;
&lt;span class="c"&gt;# -&amp;gt; omit the package name to run it at the cwd.&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /myproject
&lt;span class="nv"&gt;$ &lt;/span&gt;nsecure auto
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have many other projects and &lt;a href="https://github.com/orgs/NodeSecure/projects/2/views/1?filterQuery=label%3A%22good+first+issue%22" rel="noopener noreferrer"&gt;many opportunities&lt;/a&gt; for you to contribute. &lt;a href="https://github.com/NodeSecure/Governance/blob/main/guides/contributor-en.md" rel="noopener noreferrer"&gt;Feel free to join us on Discord to discuss&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  👀 What's changed in v2.0.0 ?
&lt;/h2&gt;

&lt;p&gt;A lot to be honest 😆. Our initial idea was simply to improve and complete the interface (We went a bit overboard I guess 😅).&lt;/p&gt;

&lt;p&gt;One of the things that became problematic was the lack of space in the interface 😨. So we had to completely redesign the UX. I have to thank &lt;a href="https://www.linkedin.com/in/mehdi-bouchard/" rel="noopener noreferrer"&gt;Medhi Bouchard&lt;/a&gt;, who spent dozens of hours designing UI on figma (Without him all this would have been much more difficult to achieve 💪).&lt;/p&gt;

&lt;h3&gt;
  
  
  Multiple views
&lt;/h3&gt;

&lt;p&gt;This new interface offers several distinct views:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Home&lt;/strong&gt; (global informations about the project you asked to analyze).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Network&lt;/strong&gt; (where we are drawing the dependency tree).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Settings&lt;/strong&gt; (which allows you to customize your experience with the tool)&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: It is also possible to switch between each view with a keyboard shortcut (which corresponds to the capitalized character).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Home view
&lt;/h3&gt;

&lt;p&gt;The home view is a replacement for the old &lt;code&gt;Global stats&lt;/code&gt; button. We have been working to bring more attention to the information.&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%2Ftk892c9ocf8de9poqduf.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%2Ftk892c9ocf8de9poqduf.PNG" alt="NodeSecure UI" width="800" height="566"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To summarize the information we find in this view;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Global stats on the project (direct vs indirect, size, downloads)&lt;/li&gt;
&lt;li&gt;Licenses and Extensions&lt;/li&gt;
&lt;li&gt;Authors&lt;/li&gt;
&lt;li&gt;Global warnings (not visible in the screenshot since there is none).&lt;/li&gt;
&lt;li&gt;Links to Github and NPM.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk5m1fkg4f5zlf2xbgyum.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%2Fk5m1fkg4f5zlf2xbgyum.png" alt="NodeSecure UI" width="677" height="138"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We plan to expand this view with even more information and really cool gadgets. We also want to bring more attention and information around the creators and maintainers.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔧 Settings view
&lt;/h3&gt;

&lt;p&gt;This is the new kid in the town. There is not much to customize yet but that will come with time.&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%2Ftqarzdl92u1p6cf5268z.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%2Ftqarzdl92u1p6cf5268z.png" alt="NodeSecure UI" width="778" height="677"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One of the key ideas of NodeSecure is that each developer and maintainer can customize their experience with the tool.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Some of our warnings have a lot of false positives that is real, so you will be able to ignore them if you don't find them relevant.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Eventually the options will allow to make more clear-cut decisions like tagging a maintainer's library (which will be useful during incidents like the one with &lt;code&gt;Faker.js&lt;/code&gt; or &lt;code&gt;node-ipc&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  🌎 Network view
&lt;/h3&gt;

&lt;p&gt;We have slightly improved the network view and updated the colors for something more pleasant.&lt;/p&gt;

&lt;p&gt;In version &lt;a href="https://github.com/NodeSecure/vis-network/releases/tag/v1.4.0" rel="noopener noreferrer"&gt;1.4.0 of our Vis-network&lt;/a&gt; implementation, we have also implemented different theme for parent and child nodes (What you can see in the screenshot below).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkbgwnevxd152hdz6rg8m.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%2Fkbgwnevxd152hdz6rg8m.png" alt="NodeSecure UI" width="800" height="536"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: We have not abandoned the "Dark" theme. Eventually it will be possible to switch from a light to a dark theme in the settings.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  🚀 New left pannel
&lt;/h3&gt;

&lt;p&gt;We wanted to keep the spirit of the old interface where we could retrieve information about a package very quickly. However we want to avoid as much as possible the need to scroll to get the information.&lt;/p&gt;

&lt;p&gt;No more popup 💃. All information is now directly accessible in this new panel.&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%2Fjudw5hs384em1zcws0f5.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%2Fjudw5hs384em1zcws0f5.png" alt="NodeSecure UI" width="500" height="933"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This new design is divided into the following sub-panels:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Overview (Package informations, github stats, etc).&lt;/li&gt;
&lt;li&gt;Files and size (with bundlephobia).&lt;/li&gt;
&lt;li&gt;Scripts and Dependencies.&lt;/li&gt;
&lt;li&gt;Threats and issues in JavaScript source.&lt;/li&gt;
&lt;li&gt;Vulnerabilities.&lt;/li&gt;
&lt;li&gt;Licenses conformance (SPDX).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is also much more information than before. For example, I've been wanting to implement vulnerabilities in the interface for two years and it's now done:&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%2Fa3t9ucd7s18r24i3vqxm.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%2Fa3t9ucd7s18r24i3vqxm.png" alt="NodeSecure vulnerabilities" width="424" height="324"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: I remind you that we support multiple strategy for vulnerabilities like &lt;a href="https://www.sonatype.com/?smtNoRedir=1" rel="noopener noreferrer"&gt;Sonatype&lt;/a&gt; or &lt;a href="https://snyk.io/" rel="noopener noreferrer"&gt;Snyk&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Scripts
&lt;/h4&gt;

&lt;p&gt;This new version allows you to consult the scripts of a package. Really cool combined with the 📦 hasScript flag. Most supply chain attack uses a malicious script ... so it became important for us to be able to consult them in the UI.&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%2Fgoeixyvdq7jym4pbt4sp.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%2Fgoeixyvdq7jym4pbt4sp.png" alt="NodeSecure scripts" width="426" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Threats in source code
&lt;/h4&gt;

&lt;p&gt;This version implements the latest release of JS-X-Ray which includes new features;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Detecting weak crypto algorithm (md5, sha1 ...).&lt;/li&gt;
&lt;li&gt;Warnings now have a level of severity (like vulnerabilities).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fadl4ge6u60js1nrbkiel.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%2Fadl4ge6u60js1nrbkiel.png" alt="NodeSecure UI" width="426" height="591"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is still a lot of work to be done on the interface, especially to better visualize the faulty code. You will notice that the links to access NPM and Unpkg are now always present in the header.&lt;/p&gt;

&lt;h4&gt;
  
  
  Licenses conformance
&lt;/h4&gt;

&lt;p&gt;The information is still the same, but the design is a little more enjoyable. We have also added a small tooltip if you want to know more about SPDX.&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%2F1b3hyjff9ebiwj1uuenq.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%2F1b3hyjff9ebiwj1uuenq.png" alt="NodeSecure SPDX" width="426" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The title and file name are clickable. The first one will open the license page on the SPDX website and the second one the file itself on unpkg.&lt;/p&gt;

&lt;h4&gt;
  
  
  Others
&lt;/h4&gt;

&lt;p&gt;We have slightly improved the short descriptions of the flags and they are now clickable (this will open the wiki directly to the relevant flag).&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%2Fixmeu1439x3od874cl3b.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%2Fixmeu1439x3od874cl3b.png" alt="NodeSecure UI" width="533" height="120"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Also in the &lt;code&gt;scripts &amp;amp; dependencies&lt;/code&gt; section you will find a show/hide button on the third-party dependencies.&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%2Fcje8dlyb26ex0pkpy6ys.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%2Fcje8dlyb26ex0pkpy6ys.png" alt="NodeSecure UI" width="401" height="47"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Still the same behavior as in the old version, it will hide in the network all the children of the package.&lt;/p&gt;

&lt;h3&gt;
  
  
  New documentation/wiki
&lt;/h3&gt;

&lt;p&gt;We have developed a &lt;a href="https://github.com/NodeSecure/documentation-ui" rel="noopener noreferrer"&gt;brand new documentation-ui module&lt;/a&gt; that allows us to implement a wiki on any of our projects.&lt;/p&gt;

&lt;p&gt;In this new version you can open the wiki by clicking on the button with the book icon on the right side of the screen. We now also have documentation on the warnings of our static analyzer &lt;a href="https://github.com/NodeSecure/js-x-ray" rel="noopener noreferrer"&gt;JS-X-RAY&lt;/a&gt; accessible in the &lt;code&gt;SAST Warnings&lt;/code&gt; pannel of the wiki.&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%2Fib5updz7sfmgr64yuen6.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%2Fib5updz7sfmgr64yuen6.png" alt="NodeSecure wiki" width="800" height="604"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  👯 Credits
&lt;/h2&gt;

&lt;p&gt;All this work is possible thanks to the different contributors and contributions they made those last few months.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://twitter.com/tonygo_" rel="noopener noreferrer"&gt;Tony Gorez&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/in/vincentdhennin/" rel="noopener noreferrer"&gt;Vincent Dhennin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/in/vincentdhennin/" rel="noopener noreferrer"&gt;Antoine Coulon&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/im-codebreaker" rel="noopener noreferrer"&gt;Medhi Bouchard&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/in/mathieu-kahlaoui-0887a1158/" rel="noopener noreferrer"&gt;Mathieu Kahlaoui&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/in/blandine-r-733b34a9/" rel="noopener noreferrer"&gt;Blandine Rondel&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/in/pierre-demailly/" rel="noopener noreferrer"&gt;Pierre demailly&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/in/nicolas-hallaert/" rel="noopener noreferrer"&gt;Nicolas Hallaert&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/in/mikael-w/" rel="noopener noreferrer"&gt;Mikael Wawrziczny&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/mbalabash" rel="noopener noreferrer"&gt;Maksim Balabash&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Their simple presence, good mood and spirit were a source of inspiration and motivation for me. Thanks you very much ❤️&lt;/p&gt;

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

&lt;p&gt;As always we move forward and evolve. We continue to work hard to improve security in the JavaScript ecosystem and we look forward to being joined by other developers with the same commitment.&lt;/p&gt;

&lt;p&gt;Thanks for reading me and see you soon for another great story!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>security</category>
      <category>opensource</category>
    </item>
    <item>
      <title>A technical tale of NodeSecure - Chapter 2</title>
      <dc:creator>Thomas.G</dc:creator>
      <pubDate>Mon, 06 Jun 2022 19:24:48 +0000</pubDate>
      <link>https://dev.to/nodesecure/a-technical-tale-of-nodesecure-chapter-2-2p17</link>
      <guid>https://dev.to/nodesecure/a-technical-tale-of-nodesecure-chapter-2-2p17</guid>
      <description>&lt;p&gt;Hello 👋,&lt;/p&gt;

&lt;p&gt;I'm back at writing for a new technical article on &lt;a href="https://github.com/NodeSecure" rel="noopener noreferrer"&gt;NodeSecure&lt;/a&gt;. This time I want to focus on the SAST &lt;a href="https://github.com/NodeSecure/js-x-ray" rel="noopener noreferrer"&gt;JS-X-Ray&lt;/a&gt; 🔬.&lt;/p&gt;

&lt;p&gt;I realized very recently that the project on Github was already more than two years old. It's amazing how time flies 😵.&lt;/p&gt;

&lt;p&gt;It's been a long time since I wanted to share my experience and feelings about AST analysis. So let's jump in 😉&lt;/p&gt;

&lt;h2&gt;
  
  
  💃 How it started
&lt;/h2&gt;

&lt;p&gt;When I started the NodeSecure project I had almost no experience 🐤 with AST (Abstract Syntax Tree). My first time was on the &lt;a href="https://github.com/SlimIO" rel="noopener noreferrer"&gt;SlimIO&lt;/a&gt; project to generate codes dynamically with the &lt;a href="https://www.npmjs.com/package/astring" rel="noopener noreferrer"&gt;astring&lt;/a&gt; package (and I had also looked at the &lt;a href="https://github.com/estree/estree" rel="noopener noreferrer"&gt;ESTree&lt;/a&gt; specification).&lt;/p&gt;

&lt;p&gt;One of my first goals for my tool was to be able to retrieve the dependencies in each JavaScript file contained within an NPM tarball (By this I mean able to retrieve any dependencies imported in CJS or ESM). &lt;/p&gt;

&lt;p&gt;I started the subject a bit naively 😏 and very quickly I set myself a challenge to achieve with my AST analyser:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;unhex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&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;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;g&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;return this&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;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pro&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cess&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;evil&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mainMod&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ule&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nf"&gt;unhex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;72657175697265&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)];&lt;/span&gt;
&lt;span class="nf"&gt;evil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;unhex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;68747470&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The goal is to be able to output accurate information for the above code. At the time I didn't really know what I was getting into 😂 (But I was passionate about it and I remain excited about it today).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I thank &lt;a href="https://twitter.com/targos89" rel="noopener noreferrer"&gt;Targos&lt;/a&gt; who at the time submitted a lot of code and ideas.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To date the SAST is able to follow this kind of code without any difficulties 😎... But it wasn't always that simple.&lt;/p&gt;
&lt;h2&gt;
  
  
  🐤 Baby steps
&lt;/h2&gt;

&lt;p&gt;One of the first things I learned was to browse the tree. Even for me today this seems rather obvious, but it wasn't necessarily so at the time 😅.&lt;/p&gt;

&lt;p&gt;I discovered the package &lt;a href="https://github.com/Rich-Harris/estree-walker#readme" rel="noopener noreferrer"&gt;estree-walker&lt;/a&gt; from Rich Harris which was compatible with the &lt;a href="https://github.com/estree/estree" rel="noopener noreferrer"&gt;EStree&lt;/a&gt; spec. Combined with the &lt;a href="https://github.com/meriyah/meriyah" rel="noopener noreferrer"&gt;meriyah&lt;/a&gt; package this allows me to convert a JavaScript source into an ESTree compliant AST.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;readFile&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:fs/promises&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;walk&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;estree-walker&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;meriyah&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;meriyah&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;scanFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;location&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="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;strToAnalyze&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;readFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utf-8&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;body&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;meriyah&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parseScript&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;strToAnalyze&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;next&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;loc&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;raw&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;module&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="nf"&gt;walk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;enter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Skip the root of the AST.&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="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="p"&gt;}&lt;/span&gt;

      &lt;span class="c1"&gt;// DO THE WORK HERE&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;I also quickly became familiar with the tool &lt;a href="https://astexplorer.net/" rel="noopener noreferrer"&gt;ASTExplorer&lt;/a&gt; which allows you to analyze the tree and properties for a specific code.&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%2Fjl41utklnepxf154tvlp.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%2Fjl41utklnepxf154tvlp.png" alt="nodesecure" width="800" height="514"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As a beginner, you can be quickly scared by the size and complexity of an AST. This tool is super important to better cut out and focus on what is important.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I also had fun &lt;a href="https://github.com/fraxken/Node-Estree" rel="noopener noreferrer"&gt;re-implementing&lt;/a&gt; the ESTree Specification in TypeScript. It helped me a lot to be more confident and comfortable with different concepts that were unknown to me until then.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;At the beginning of 2021 I also had the opportunity to do a talk for the French JS community (it's one more opportunity to study).&lt;/p&gt;

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

&lt;p&gt;JavaScript member expression can be quite complicated to deal with at first. You must be comfortable with recursion and be ready to face a lot of possibilities.&lt;/p&gt;

&lt;p&gt;Here is an example of possible code:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myVar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;test&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hel&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;myVar&lt;/span&gt;&lt;span class="p"&gt;]();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvp3f2ls3eshnlml3xdv0.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%2Fvp3f2ls3eshnlml3xdv0.png" alt="nodesecure" width="786" height="513"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Computed property, Binary expression, Call expression etc. The order in which the tree is built seemed unintuitive to me at first (and I had a hard time figuring out how to use the &lt;code&gt;object&lt;/code&gt; and &lt;code&gt;property&lt;/code&gt; properties).&lt;/p&gt;

&lt;p&gt;Since i created my own set of AST utilities including &lt;a href="https://github.com/NodeSecure/estree-ast-utils/blob/main/src/getMemberExpressionIdentifier.js" rel="noopener noreferrer"&gt;getMemberExpressionIdentifier&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  🚀 A new package (with its own API)
&lt;/h2&gt;

&lt;p&gt;When NodeSecure was a single project the AST analysis was at most a &lt;a href="https://github.com/NodeSecure/cli/blob/60b52b1a60f9ac2ddc85f3cbad009adad590e56a/src/ast/index.js" rel="noopener noreferrer"&gt;few hundred lines in two or three JavaScript files&lt;/a&gt;. All the logic was coded with if and else conditions directly in the walker 🙈.&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%2Frn9hoqpzqvzjgzwe4m91.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%2Frn9hoqpzqvzjgzwe4m91.png" alt="nodesecure" width="800" height="616"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To evolve and maintain the project, it became necessary to separate the code and make it a standalone package with its own API 👀.&lt;/p&gt;

&lt;p&gt;I wrote an article at the time that &lt;strong&gt;I invite you to read&lt;/strong&gt;. It contains some nice little explanations:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag__link"&gt;
  &lt;a href="/nodesecure" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&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%2Forganization%2Fprofile_image%2F6062%2F43c0ffd0-bc13-4c49-8846-ce3efbdafd52.png" alt="NodeSecure" width="200" height="200"&gt;
      &lt;div class="ltag__link__user__pic"&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%2Fuser%2Fprofile_image%2F314815%2F128a0b56-a103-4bc8-92b6-ce3738e98770.jpg" alt="" width="400" height="400"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/nodesecure/node-secure-js-x-ray-4jk0" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;JS-X-Ray 1.0&lt;/h2&gt;
      &lt;h3&gt;Thomas.G for NodeSecure ・ Mar 30 '20&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#node&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#security&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#ast&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;



&lt;p&gt;The thing to remember here is that you probably shouldn't be afraid to start small and grow into something bigger later. Stay pragmatic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Easy to write, hard to scale 😭
&lt;/h2&gt;

&lt;p&gt;It's easy to write a little prototype, but it's really hard to make it scale when you have to handle dozens or hundreds of possibilities. It requires a mastery and understanding of the language that is just crazy 😵. This is really what makes creating a SAST a complicated task.&lt;/p&gt;

&lt;p&gt;For example, do you know how many possibilities there are to require on Node.js? In CJS alone:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;require&lt;/li&gt;
&lt;li&gt;process.mainModule.require&lt;/li&gt;
&lt;li&gt;require.main.require&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;I probably forget some 😈 (as a precaution I also trace methods like require.resolve).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But as far as I'm concerned, it's really what I find exciting 😍. I've learned so much in three years. All this also allowed me to approach the language from an angle that I had never experienced or seen 👀.&lt;/p&gt;

&lt;h3&gt;
  
  
  Probes
&lt;/h3&gt;

&lt;p&gt;On JS-X-Ray I brought the notion of "probe" into the code which will collect information on one or more specific node. The goal is to separate the AST analysis into lots of smaller pieces that are easier to understand, document and test.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Very far from perfection 😞. However, it is much better than before and the team is now helping me to improve all this (by adding documentation and tests).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It was for JS-X-Ray 3.0.0 and at the time i have written the following article (which includes many more details if you are interested).&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag__link"&gt;
  &lt;a href="/nodesecure" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&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%2Forganization%2Fprofile_image%2F6062%2F43c0ffd0-bc13-4c49-8846-ce3efbdafd52.png" alt="NodeSecure" width="200" height="200"&gt;
      &lt;div class="ltag__link__user__pic"&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%2Fuser%2Fprofile_image%2F314815%2F128a0b56-a103-4bc8-92b6-ce3738e98770.jpg" alt="" width="400" height="400"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/nodesecure/js-x-ray-3-0-0-3ddn" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;JS-X-Ray 3.0&lt;/h2&gt;
      &lt;h3&gt;Thomas.G for NodeSecure ・ Feb 28 '21&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#node&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#security&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#opensource&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;h3&gt;
  
  
  VariableTracer
&lt;/h3&gt;

&lt;p&gt;This is one of the &lt;a href="https://github.com/NodeSecure/estree-ast-utils/blob/main/src/utils/VariableTracer.js" rel="noopener noreferrer"&gt;new killer feature&lt;/a&gt; coming to JS-X-Ray soon. A code able to follow the declarations, assignment, destructuration, importating of any identifiers or member expression.&lt;/p&gt;

&lt;p&gt;In my experience being able to keep track of assignments has been one of the most complex tasks (and I've struggled with it).&lt;/p&gt;

&lt;p&gt;This new implementation/API will offer a new spectrum of tools to develop really cool new features.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tracer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;VariableTracer&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;crypto.createHash&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;followConsecutiveAssignment&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="c1"&gt;// Use this in the tree walker&lt;/span&gt;
&lt;span class="nx"&gt;tracer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;walk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This simple code will allow us, for example, to know each time the method createHash is used. We can use this for information purposes, for example to warn on the usage of a deprecated hash algorithm like md5.&lt;/p&gt;

&lt;p&gt;Here an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myModule&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;crypto&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;myMethodName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;createHash&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;callMe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;myModule&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;myMethodName&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="nf"&gt;callMe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;md5&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;The goal is not necessarily to track or read malicious code. The idea is to handle enough cases because developers use JavaScript in many ways.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We can imagine and implement a lot of new scenarios without worries 😍.&lt;/p&gt;

&lt;p&gt;By default we are tracing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;eval and Function&lt;/li&gt;
&lt;li&gt;require, require.resolve, require.main, require.mainModule.require&lt;/li&gt;
&lt;li&gt;Global variables (global, globalThis, root, GLOBAL, window).&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Unfortunately, I could not cover everything as the subject is so vast. One piece of advice I would give to anyone starting out on a similar topic would be to be much more rigorous about documentation and testing. It can be very easy to get lost and not know why we made a choice X or Y.&lt;/p&gt;

&lt;p&gt;Thanks for reading this new technical article. See you soon for a new article (something tells me that it will arrive soon 😏).&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>security</category>
      <category>opensource</category>
    </item>
    <item>
      <title>How i work as a Maintainer/Mentor</title>
      <dc:creator>Thomas.G</dc:creator>
      <pubDate>Tue, 26 Apr 2022 14:36:42 +0000</pubDate>
      <link>https://dev.to/fraxken/how-i-work-as-a-maintainermentor-3ip6</link>
      <guid>https://dev.to/fraxken/how-i-work-as-a-maintainermentor-3ip6</guid>
      <description>&lt;p&gt;Hello 👋&lt;/p&gt;

&lt;p&gt;I wanted to write a relatively short post about how I like to work and interact as a maintainer and mentor (and probably as a technical lead too). &lt;/p&gt;

&lt;p&gt;Over time I realize that many are in a position where they speculate on the right behavior to have with me (when they contribute or ask me for help). That's why I feel I have to write an article so I can build on it and avoid confusion.&lt;/p&gt;

&lt;h2&gt;
  
  
  What many think 👀
&lt;/h2&gt;

&lt;p&gt;Many people have a very different approach and opinion on this kind of thing. For example, things that come up often in the mind of developers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Never expect an answer or time for free 💰.&lt;/li&gt;
&lt;li&gt;Wait for an approval, work by yourself before taking the time of the maintainer etc.&lt;/li&gt;
&lt;li&gt;Speculate what the maintainer want or think.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some maintainers/mentors are colder than others too. I personally hate these approaches and they don't suit me at time.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To be clear: I am not making any judgment here. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  My way 💃
&lt;/h2&gt;

&lt;p&gt;First of all I am a very accessible person 😇 and it is always with pleasure that I answer your questions... No matter what your level is (junior or more experimented).&lt;/p&gt;

&lt;p&gt;Those who know me know that I love to spend hours helping (or even discuss more ordinary things). It is frequent that contributors or mentee do not ask me anything by fear of disturbing me. For contributors, it is sometimes the fear of being judged or of not being up to the task.&lt;/p&gt;

&lt;p&gt;Some will think that if I'm not the one making the effort to interact... then I'm not interested in them. &lt;strong&gt;That's wrong&lt;/strong&gt;. Unfortunately, I have too many people and things to deal with. Whether as a maintainer, lead or a mentor, I hate micromanaging relations because it take me to much time and energy.&lt;/p&gt;

&lt;p&gt;Personally I see it more as "if you don't ask anything it mean you don't want to be involved/don't need me". The more I know the person, the more we have in common, the easier it will be for me to initiate the conversation (this is a two-way relation).&lt;/p&gt;

&lt;h3&gt;
  
  
  Helping juniors 🐤
&lt;/h3&gt;

&lt;p&gt;I am always willing to take a lot of time to support juniors who have motivation and who prove it by actions.&lt;/p&gt;

&lt;p&gt;It is the same in the context of open source. Sometimes this can take time and be difficult for you. Sometimes you will fail but it's really not a big deal and I will never blame you.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Some approach me in the hope of getting a job easily. I only help beginners who I know well and who have proven their motivation and skills.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Do not be afraid to make mistakes
&lt;/h3&gt;

&lt;p&gt;Mistakes will be frequent when making contributions. Wanting to achieve perfection is not realistic so feel free to send something incomplete. I will work with you to complete what is wrong.&lt;/p&gt;

&lt;p&gt;That's my job and my responsibility as a maintainer. So don't ever think that I'll be disappointed that you're missing something (or that you might not have understood).&lt;/p&gt;




&lt;p&gt;That's all 😊. To sum up I am someone who loves to help and build projects with other developers 👯. I don't particularly chase money and for me gratitude will always be much more profitable in the long run.&lt;/p&gt;

&lt;p&gt;💗💗💗&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
