<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Manuel Salinardi blog]]></title><description><![CDATA[Manuel Salinardi blog]]></description><link>https://blog.manuelsalinardi.it</link><generator>RSS for Node</generator><lastBuildDate>Sat, 12 Sep 2026 22:48:41 GMT</lastBuildDate><atom:link href="https://blog.manuelsalinardi.it/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Fastify & Typescript: con il nuovo flag --experimental-strip-types di Node.js]]></title><description><![CDATA[Read the article in english language

Dalla la versione di Node.js v22.6.0 è stato introdotto il nuovo flag sperimentale:
--experimental-strip-types
Questo flag ci permette di eseguire con Node.js un file .ts direttamente e senza bisogno di librerie ...]]></description><link>https://blog.manuelsalinardi.it/fastify-typescript-con-il-nuovo-flag-experimental-strip-types-di-nodejs</link><guid isPermaLink="true">https://blog.manuelsalinardi.it/fastify-typescript-con-il-nuovo-flag-experimental-strip-types-di-nodejs</guid><category><![CDATA[fastify]]></category><category><![CDATA[server]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[TypeScript]]></category><category><![CDATA[backend]]></category><dc:creator><![CDATA[Manuel Salinardi]]></dc:creator><pubDate>Tue, 01 Oct 2024 14:29:19 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1727792675602/ad238191-c46b-40fc-9631-5b6008b4fc5b.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p><a target="_blank" href="https://blog.manuelsalinardi.dev/fastify-typescript-with-the-new-nodejs-flag-experimental-strip-types">Read the article in english language</a></p>
</blockquote>
<p>Dalla la versione di Node.js v22.6.0 è stato introdotto il nuovo flag sperimentale:</p>
<p><a target="_blank" href="https://nodejs.org/docs/latest/api/cli.html#--experimental-strip-types"><strong>--experimental-strip-types</strong></a></p>
<p>Questo flag ci permette di eseguire con Node.js un file .ts direttamente e senza bisogno di librerie esterne ne di compilare in Javascript prima di eseguire il file, usando la tecnica del <a target="_blank" href="https://nodejs.org/docs/latest/api/typescript.html#type-stripping">"type stripping"</a></p>
<p>Per usare questo nuovo flag bisogna usare una versione di Node.js uguale o maggiore della v22.6.0</p>
<blockquote>
<p>In questo articolo userò Node.js 22.8.0</p>
</blockquote>
<h2 id="heading-experimental-strip-typeshttpsnodejsorgdocslatestapiclihtml-experimental-strip-types-in-nodejs"><a target="_blank" href="https://nodejs.org/docs/latest/api/cli.html#--experimental-strip-types"><strong>--experimental-strip-types</strong></a> <strong>in Node.js</strong></h2>
<p>Creiamo un file, ad esempio node-typescript.ts</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">interface</span> Person {
    name: <span class="hljs-built_in">string</span>
    surname: <span class="hljs-built_in">string</span>
}

<span class="hljs-keyword">const</span> manuel: Person = {
    name: <span class="hljs-string">'Manuel'</span>,
    surname: <span class="hljs-string">'Salinardi'</span>
} 

<span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Hello <span class="hljs-subst">${manuel.name}</span> <span class="hljs-subst">${manuel.surname}</span>`</span>)
</code></pre>
<p>Proviamo ad eseguirlo con node:</p>
<pre><code class="lang-bash">node node-typescript.ts
</code></pre>
<p>E boom!</p>
<pre><code class="lang-bash">interface Person {
          ^^^^^^

SyntaxError: Unexpected identifier <span class="hljs-string">'Person'</span>
    at wrapSafe (node:internal/modules/cjs/loader:1469:18)
    at Module._compile (node:internal/modules/cjs/loader:1491:20)
    at Module._extensions..js (node:internal/modules/cjs/loader:1691:10)
    at Module.load (node:internal/modules/cjs/loader:1317:32)
    at Module._load (node:internal/modules/cjs/loader:1127:12)
    at TracingChannel.traceSync (node:diagnostics_channel:315:14)
    at wrapModuleLoad (node:internal/modules/cjs/loader:217:24)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:166:5)
    at node:internal/main/run_main_module:30:49
</code></pre>
<p>Wow che bell'errore, ed è giusto che sia così perché "interface" non esiste in Javascript ma è una feature di Typescript.</p>
<p>Ed è qui che entra in gioco il nuovo flag sperimentale che ci permette di eseguire file Typescript direttamente con Node.js</p>
<pre><code class="lang-bash">node --experimental-strip-types node-typescript.ts
</code></pre>
<p>Ed ora come output vedremo:</p>
<pre><code class="lang-bash">Hello Manuel Salinardi
(node:98118) ExperimentalWarning: Type Stripping is an experimental feature and might change at any time
(Use `node --trace-warnings ...` to show <span class="hljs-built_in">where</span> the warning was created)
</code></pre>
<p>Wow funziona!</p>
<p>Vediamo anche che c'è un warning che ci avvisa che stiamo usando un flag sperimentale.</p>
<h2 id="heading-experimental-strip-typeshttpsnodejsorgdocslatestapiclihtml-experimental-strip-types-in-fastify"><a target="_blank" href="https://nodejs.org/docs/latest/api/cli.html#--experimental-strip-types"><strong>--experimental-strip-types</strong></a> <strong>in Fastify</strong></h2>
<h3 id="heading-creiamo-il-progetto-fastify">Creiamo il progetto Fastify</h3>
<p>Usiamo <a target="_blank" href="https://github.com/fastify/fastify-cli">fastify-cli</a> per creare il progetto Fastify</p>
<pre><code class="lang-bash">npx fastify-cli generate fastify-type-stripping --lang=ts --esm
</code></pre>
<p>Entriamo nella cartella "fastify-type-stripping" appena creata e installiamo le dipendenze</p>
<pre><code class="lang-bash">npm install
</code></pre>
<p>Ora lanciamo il server appena creato</p>
<pre><code class="lang-bash">npm start
</code></pre>
<p>Se tutto è andato a buon fine dovremmo vedere un output come questo:</p>
<pre><code class="lang-bash">{<span class="hljs-string">"level"</span>:30,<span class="hljs-string">"time"</span>:1725951422786,<span class="hljs-string">"pid"</span>:8786,<span class="hljs-string">"hostname"</span>:<span class="hljs-string">"Manuels-MacBook-Pro.local"</span>,<span class="hljs-string">"msg"</span>:<span class="hljs-string">"Server listening at http://127.0.0.1:3000"</span>}
{<span class="hljs-string">"level"</span>:30,<span class="hljs-string">"time"</span>:1725951422787,<span class="hljs-string">"pid"</span>:8786,<span class="hljs-string">"hostname"</span>:<span class="hljs-string">"Manuels-MacBook-Pro.local"</span>,<span class="hljs-string">"msg"</span>:<span class="hljs-string">"Server listening at http://[::1]:3000"</span>}
</code></pre>
<h3 id="heading-usiamo-experimental-strip-typeshttpsnodejsorgdocslatestapiclihtml-experimental-strip-types">Usiamo <a target="_blank" href="https://nodejs.org/docs/latest/api/cli.html#--experimental-strip-types"><strong>--experimental-strip-types</strong></a></h3>
<p>fastify-cli ha generato questo package.json</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"type"</span>: <span class="hljs-string">"module"</span>,
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"fastify-type-stripping"</span>,
  <span class="hljs-attr">"version"</span>: <span class="hljs-string">"1.0.0"</span>,
  <span class="hljs-attr">"description"</span>: <span class="hljs-string">"This project was bootstrapped with Fastify-CLI."</span>,
  <span class="hljs-attr">"main"</span>: <span class="hljs-string">"app.ts"</span>,
  <span class="hljs-attr">"directories"</span>: {
    <span class="hljs-attr">"test"</span>: <span class="hljs-string">"test"</span>
  },
  <span class="hljs-attr">"scripts"</span>: {
    <span class="hljs-attr">"test"</span>: <span class="hljs-string">"npm run build:ts &amp;&amp; tsc -p test/tsconfig.json &amp;&amp; FASTIFY_AUTOLOAD_TYPESCRIPT=1 node --test --experimental-test-coverage --loader ts-node/esm test/**/*.ts"</span>,
    <span class="hljs-attr">"start"</span>: <span class="hljs-string">"npm run build:ts &amp;&amp; fastify start -l info dist/app.js"</span>,
    <span class="hljs-attr">"build:ts"</span>: <span class="hljs-string">"tsc"</span>,
    <span class="hljs-attr">"watch:ts"</span>: <span class="hljs-string">"tsc -w"</span>,
    <span class="hljs-attr">"dev"</span>: <span class="hljs-string">"npm run build:ts &amp;&amp; concurrently -k -p \"[{name}]\" -n \"TypeScript,App\" -c \"yellow.bold,cyan.bold\" \"npm:watch:ts\" \"npm:dev:start\""</span>,
    <span class="hljs-attr">"dev:start"</span>: <span class="hljs-string">"fastify start --ignore-watch=.ts$ -w -l info -P dist/app.js"</span>
  },
  <span class="hljs-attr">"keywords"</span>: [],
  <span class="hljs-attr">"author"</span>: <span class="hljs-string">""</span>,
  <span class="hljs-attr">"license"</span>: <span class="hljs-string">"ISC"</span>,
  <span class="hljs-attr">"dependencies"</span>: {
    <span class="hljs-attr">"fastify"</span>: <span class="hljs-string">"^5.0.0"</span>,
    <span class="hljs-attr">"fastify-plugin"</span>: <span class="hljs-string">"^5.0.0"</span>,
    <span class="hljs-attr">"@fastify/autoload"</span>: <span class="hljs-string">"^6.0.0"</span>,
    <span class="hljs-attr">"@fastify/sensible"</span>: <span class="hljs-string">"^6.0.0"</span>,
    <span class="hljs-attr">"fastify-cli"</span>: <span class="hljs-string">"^7.0.1"</span>
  },
  <span class="hljs-attr">"devDependencies"</span>: {
    <span class="hljs-attr">"@types/node"</span>: <span class="hljs-string">"^22.1.0"</span>,
    <span class="hljs-attr">"c8"</span>: <span class="hljs-string">"^10.1.2"</span>,
    <span class="hljs-attr">"ts-node"</span>: <span class="hljs-string">"^10.4.0"</span>,
    <span class="hljs-attr">"concurrently"</span>: <span class="hljs-string">"^9.0.0"</span>,
    <span class="hljs-attr">"fastify-tsconfig"</span>: <span class="hljs-string">"^2.0.0"</span>,
    <span class="hljs-attr">"typescript"</span>: <span class="hljs-string">"^5.2.2"</span>
  }
}
</code></pre>
<p>Analizziamo lo script di start del package.json:</p>
<pre><code class="lang-bash"><span class="hljs-string">"start"</span>: <span class="hljs-string">"npm run build:ts &amp;&amp; fastify start -l info dist/app.js"</span>
</code></pre>
<p>Lo script “<strong>start</strong>“ è composto da due comandi:</p>
<ul>
<li><p><strong>npm run build:ts</strong></p>
</li>
<li><p><strong>fastify start -l info dist/app.js</strong></p>
</li>
</ul>
<p>Quindi vediamo che per prima cosa lancia lo script "<strong>build:ts</strong>":</p>
<pre><code class="lang-bash"><span class="hljs-string">"build:ts"</span>: <span class="hljs-string">"tsc"</span>
</code></pre>
<p>"<strong>build:ts</strong>" lancia il comando “<strong>tsc</strong>”, Che converte il codice Typescript in JavaScript a seconda delle impostazioni che trova nel file tsconfig.json, che nel nostro caso è:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"extends"</span>: <span class="hljs-string">"fastify-tsconfig"</span>,
  <span class="hljs-attr">"compilerOptions"</span>: {
    <span class="hljs-attr">"outDir"</span>: <span class="hljs-string">"dist"</span>,
    <span class="hljs-attr">"sourceMap"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"moduleResolution"</span>: <span class="hljs-string">"NodeNext"</span>,
    <span class="hljs-attr">"module"</span>: <span class="hljs-string">"NodeNext"</span>,
    <span class="hljs-attr">"target"</span>: <span class="hljs-string">"ES2022"</span>,
    <span class="hljs-attr">"esModuleInterop"</span>: <span class="hljs-literal">true</span>
  },
  <span class="hljs-attr">"include"</span>: [<span class="hljs-string">"src/**/*.ts"</span>]
}
</code></pre>
<p>Come possiamo vedere c’è la proprietà "outDir": "dist". Che dice al compilatore di mettere l’output della compilazione, ovvero i file JavaScript generati nella cartella “dist“</p>
<p>Poi viene lanciato il comando: <strong>fastify start -l info dist/app.js</strong>, che va a lanciare il nostro server dal file <strong>dist/app.js</strong> autogenerato dal compilatore TypeScript.</p>
<p>Quindi vediamo che ogni volta che lanciamo il server c’è sempre questo passo intermedio che è la conversione dei file TypeScript in JavaScript e poi il server viene effettivamente lanciato dai file JavaScript.</p>
<h3 id="heading-lanciamo-il-server-senza-la-compilazione-typescript">Lanciamo il server senza la compilazione Typescript</h3>
<p>Proviamo ad usare la nuova feature di Node.js <a target="_blank" href="https://nodejs.org/docs/latest/api/cli.html#--experimental-strip-types"><strong>--experimental-strip-types</strong></a> per lanciare direttamente il server con il codice sorgente TypeScriprt senza dover prima convertire i file in JavaScript.</p>
<p>Procediamo a tentativi e risolviamo tutti gli errori che incontriamo.</p>
<h3 id="heading-tentativo-1-stato-iniziale">Tentativo 1: Stato iniziale</h3>
<p>Iniziamo solo con provare a rimuovere la compilazione ed avviare il server direttamente con il file TypeScript e godiamoci l’esplosione:</p>
<pre><code class="lang-json"><span class="hljs-string">"start"</span>: <span class="hljs-string">"fastify start -l info src/app.ts"</span>
</code></pre>
<pre><code class="lang-javascript">&gt; fastify-type-stripping@<span class="hljs-number">1.0</span><span class="hljs-number">.0</span> start
&gt; fastify start -l info src/app.ts

<span class="hljs-built_in">TypeError</span> [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension <span class="hljs-string">".ts"</span> <span class="hljs-keyword">for</span> /Users/manuelsalinardi/coding/blogs/fastify/fastify-type-stripping/src/app.ts
    at <span class="hljs-built_in">Object</span>.getFileProtocolModuleFormat [<span class="hljs-keyword">as</span> file:] (node:internal/modules/esm/get_format:<span class="hljs-number">217</span>:<span class="hljs-number">9</span>)
    at defaultGetFormat (node:internal/modules/esm/get_format:<span class="hljs-number">243</span>:<span class="hljs-number">36</span>)
    at defaultLoad (node:internal/modules/esm/load:<span class="hljs-number">123</span>:<span class="hljs-number">22</span>)
    at <span class="hljs-keyword">async</span> ModuleLoader.load (node:internal/modules/esm/loader:<span class="hljs-number">567</span>:<span class="hljs-number">7</span>)
    at <span class="hljs-keyword">async</span> ModuleLoader.moduleProvider (node:internal/modules/esm/loader:<span class="hljs-number">442</span>:<span class="hljs-number">45</span>)
    at <span class="hljs-keyword">async</span> ModuleJob._link (node:internal/modules/esm/module_job:<span class="hljs-number">106</span>:<span class="hljs-number">19</span>) {
  <span class="hljs-attr">code</span>: <span class="hljs-string">'ERR_UNKNOWN_FILE_EXTENSION'</span>
}
</code></pre>
<p>Boom!!!</p>
<p>Abbiamo un errore che ci dice il file .ts non è supportato, ma abbiamo visto che per questo possiamo aggiungere il nuovo flag di Node.js <a target="_blank" href="https://nodejs.org/docs/latest/api/cli.html#--experimental-strip-types"><strong>--experimental-strip-types</strong></a>.</p>
<p>Ma dove?</p>
<h3 id="heading-tentativo-2-passare-il-flag-experimental-strip-types">Tentativo 2: Passare il flag <strong>--experimental-strip-types</strong></h3>
<p>Il flag <a target="_blank" href="https://nodejs.org/docs/latest/api/cli.html#--experimental-strip-types"><strong>--experimental-strip-types</strong></a> va passato all’eseguibile <strong>node</strong> ma noi stiamo lanciando il server con:<br /><strong>fastify start -l info src/app.ts.</strong> In questo caso possiamo usare la variabile d’ambiente:<br /><a target="_blank" href="https://nodejs.org/docs/v20.17.0/api/cli.html#node_optionsoptions"><strong>NODE_OPTIONS</strong></a> che ci permette di passare le <a target="_blank" href="https://nodejs.org/docs/latest-v22.x/api/cli.html">opzioni</a> di Node.js in questo modo:</p>
<pre><code class="lang-json"><span class="hljs-string">"start"</span>: <span class="hljs-string">"NODE_OPTIONS='--experimental-strip-types' fastify start -l info src/app.ts"</span>
</code></pre>
<pre><code class="lang-javascript"> *  Executing task: source ~/.zshrc &amp;&amp; npm run start 


&gt; fastify-type-stripping@<span class="hljs-number">1.0</span><span class="hljs-number">.0</span> start
&gt; NODE_OPTIONS=<span class="hljs-string">'--experimental-strip-types'</span> fastify start -l info src/app.ts

(node:<span class="hljs-number">57588</span>) ExperimentalWarning: Type Stripping is an experimental feature and might change at any time
(Use <span class="hljs-string">`node --trace-warnings ...`</span> to show where the warning was created)
<span class="hljs-attr">file</span>:<span class="hljs-comment">///Users/manuelsalinardi/coding/blogs/fastify/fastify-type-stripping/src/app.ts:2</span>
<span class="hljs-keyword">import</span> AutoLoad, {AutoloadPluginOptions} <span class="hljs-keyword">from</span> <span class="hljs-string">'@fastify/autoload'</span>;
                  ^^^^^^^^^^^^^^^^^^^^^
<span class="hljs-built_in">SyntaxError</span>: Named <span class="hljs-keyword">export</span> <span class="hljs-string">'AutoloadPluginOptions'</span> not found. The requested <span class="hljs-built_in">module</span> <span class="hljs-string">'@fastify/autoload'</span> is a CommonJS <span class="hljs-built_in">module</span>, which m
ay not support all <span class="hljs-built_in">module</span>.exports <span class="hljs-keyword">as</span> named <span class="hljs-built_in">exports</span>.                                                                                CommonJS modules can always be imported via the <span class="hljs-keyword">default</span> <span class="hljs-keyword">export</span>, <span class="hljs-keyword">for</span> example using:

<span class="hljs-keyword">import</span> pkg <span class="hljs-keyword">from</span> <span class="hljs-string">'@fastify/autoload'</span>;
<span class="hljs-keyword">const</span> {AutoloadPluginOptions} = pkg;

    at ModuleJob._instantiate (node:internal/modules/esm/module_job:<span class="hljs-number">171</span>:<span class="hljs-number">21</span>)
    at <span class="hljs-keyword">async</span> ModuleJob.run (node:internal/modules/esm/module_job:<span class="hljs-number">254</span>:<span class="hljs-number">5</span>)
    at <span class="hljs-keyword">async</span> onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:<span class="hljs-number">482</span>:<span class="hljs-number">26</span>)
    at <span class="hljs-keyword">async</span> requireServerPluginFromPath (<span class="hljs-regexp">/Users/m</span>anuelsalinardi/coding/blogs/fastify/fastify-type-stripping/node_modules/fastify-c
li/util.js:<span class="hljs-number">83</span>:<span class="hljs-number">22</span>)                                                                                                                      at <span class="hljs-keyword">async</span> runFastify (<span class="hljs-regexp">/Users/m</span>anuelsalinardi/coding/blogs/fastify/fastify-type-stripping/node_modules/fastify-cli/start.js:<span class="hljs-number">115</span>:<span class="hljs-number">1</span>
<span class="hljs-number">2</span>)                                                                                                                                 
 *  The terminal process <span class="hljs-string">"/bin/zsh '-l', '-c', 'source ~/.zshrc &amp;&amp; npm run start'"</span> terminated <span class="hljs-keyword">with</span> exit code: <span class="hljs-number">1.</span>
</code></pre>
<p>Wow è cambiato l’errore!</p>
<p>Ora vediamo che il flag <a target="_blank" href="https://nodejs.org/docs/latest/api/cli.html#--experimental-strip-types"><strong>--experimental-strip-types</strong></a> é stato preso correttamente perchè abbiamo questo warning:</p>
<pre><code class="lang-javascript">(node:<span class="hljs-number">57588</span>) ExperimentalWarning: Type Stripping is an experimental feature and might change at any time
(Use <span class="hljs-string">`node --trace-warnings ...`</span> to show where the warning was created)
</code></pre>
<p>Ma c’è un errore con il plugin <a target="_blank" href="https://www.npmjs.com/package/@fastify/autoload">@fastify/autoload</a>:</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">SyntaxError</span>: Named <span class="hljs-keyword">export</span> <span class="hljs-string">'AutoloadPluginOptions'</span> not found. The requested <span class="hljs-built_in">module</span> <span class="hljs-string">'@fastify/autoload'</span> is a CommonJS <span class="hljs-built_in">module</span>, which m
ay not support all <span class="hljs-built_in">module</span>.exports <span class="hljs-keyword">as</span> named <span class="hljs-built_in">exports</span>.
</code></pre>
<p>C’è qualcosa che non va con un import nel nostro file src/app.ts:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> AutoLoad, {AutoloadPluginOptions} <span class="hljs-keyword">from</span> <span class="hljs-string">'@fastify/autoload'</span>;
</code></pre>
<p>Questo è dovuto dal comportamento del type stripping del flag <a target="_blank" href="https://nodejs.org/docs/latest/api/cli.html#--experimental-strip-types"><strong>--experimental-strip-types</strong></a> che se importiamo un typo TypeScript senza esplicitamente usare la keyword “<a target="_blank" href="https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-and-export"><strong>type</strong></a>“ Node.js lo tratterà come un normale valore generando un errore a runtime, come spiegato più in dettaglio qui: <a target="_blank" href="https://nodejs.org/docs/latest-v22.x/api/typescript.html#type-stripping">https://nodejs.org/docs/latest-v22.x/api/typescript.html#type-stripping</a>.</p>
<h3 id="heading-tentativo-3-usare-import-type-per-i-tipi-typescript">Tentativo 3: Usare “import type” per i tipi TypeScript</h3>
<p>Per assicurarci di importare correttamente con “import type“ i tipi TypeScript in modo che il type stripping riesca correttamente ad eliminarli aggiungiamo una impostazione del compilatore TypeScript: <a target="_blank" href="https://www.typescriptlang.org/tsconfig/#verbatimModuleSyntax"><strong>verbatimModuleSyntax</strong></a> al nostro file:</p>
<p>tsconfig.json</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"extends"</span>: <span class="hljs-string">"fastify-tsconfig"</span>,
  <span class="hljs-attr">"compilerOptions"</span>: {
    <span class="hljs-attr">"outDir"</span>: <span class="hljs-string">"dist"</span>,
    <span class="hljs-attr">"sourceMap"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"moduleResolution"</span>: <span class="hljs-string">"NodeNext"</span>,
    <span class="hljs-attr">"module"</span>: <span class="hljs-string">"NodeNext"</span>,
    <span class="hljs-attr">"target"</span>: <span class="hljs-string">"ES2022"</span>,
    <span class="hljs-attr">"esModuleInterop"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"verbatimModuleSyntax"</span>: <span class="hljs-literal">true</span>
  },
  <span class="hljs-attr">"include"</span>: [<span class="hljs-string">"src/**/*.ts"</span>]
}
</code></pre>
<p>Ora vedremo gli errori degli import direttamente dal nostro editor:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1727708383225/d91dcb87-0285-4b0e-a4b1-79d3e386f905.png" alt class="image--center mx-auto" /></p>
<p>Quindi andiamo a sistemare tutti gli errori:</p>
<p>src/app.ts</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> AutoLoad, { <span class="hljs-keyword">type</span> AutoloadPluginOptions } <span class="hljs-keyword">from</span> <span class="hljs-string">'@fastify/autoload'</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-keyword">type</span> FastifyPluginAsync } <span class="hljs-keyword">from</span> <span class="hljs-string">'fastify'</span>;
</code></pre>
<p>src/routes/root.ts</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { <span class="hljs-keyword">type</span> FastifyPluginAsync } <span class="hljs-keyword">from</span> <span class="hljs-string">'fastify'</span>
</code></pre>
<p>src/routes/example/index.ts</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { <span class="hljs-keyword">type</span> FastifyPluginAsync } <span class="hljs-keyword">from</span> <span class="hljs-string">'fastify'</span>
</code></pre>
<p>src/plugins/sensible.ts</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> sensible, { <span class="hljs-keyword">type</span> FastifySensibleOptions } <span class="hljs-keyword">from</span> <span class="hljs-string">'@fastify/sensible'</span>
</code></pre>
<p>Ora che abbiamo risolto tutti i problemi di import proviamo a lanciare il server ed incrociamo le dita.</p>
<pre><code class="lang-typescript"> *  Executing task: source ~/.zshrc &amp;&amp; npm run start 


&gt; fastify-<span class="hljs-keyword">type</span>-stripping@<span class="hljs-number">1.0</span><span class="hljs-number">.0</span> start
&gt; NODE_OPTIONS=<span class="hljs-string">'--experimental-strip-types'</span> fastify start -l info src/app.ts

(node:<span class="hljs-number">24423</span>) ExperimentalWarning: Type Stripping is an experimental feature and might change at <span class="hljs-built_in">any</span> time
(Use <span class="hljs-string">`node --trace-warnings ...`</span> to show where the warning was created)
/Users/manuelsalinardi/coding/blogs/fastify/fastify-<span class="hljs-keyword">type</span>-stripping/node_modules/<span class="hljs-meta">@fastify</span>/autoload/lib/find-plugins.js:<span class="hljs-number">148</span>
    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">`@fastify/autoload cannot import <span class="hljs-subst">${isHook ? <span class="hljs-string">'hooks '</span> : <span class="hljs-string">''</span>}</span>plugin at '<span class="hljs-subst">${file}</span>'. To fix this error compile TypeScript to JavaScrip
t or use 'ts-node' to run your app.`</span>)                                                                                                                         ^

<span class="hljs-built_in">Error</span>: <span class="hljs-meta">@fastify</span>/autoload cannot <span class="hljs-keyword">import</span> plugin at <span class="hljs-string">'/Users/manuelsalinardi/coding/blogs/fastify/fastify-type-stripping/src/plugins/sensible.ts'</span>. To fi
x <span class="hljs-built_in">this</span> error compile TypeScript to JavaScript or use <span class="hljs-string">'ts-node'</span> to run your app.                                                                         at handleTypeScriptSupport (<span class="hljs-regexp">/Users/m</span>anuelsalinardi/coding/blogs/fastify/fastify-<span class="hljs-keyword">type</span>-stripping/node_modules/<span class="hljs-meta">@fastify</span>/autoload/lib/find-plugins.j
s:<span class="hljs-number">148</span>:<span class="hljs-number">11</span>)                                                                                                                                               at processFile (<span class="hljs-regexp">/Users/m</span>anuelsalinardi/coding/blogs/fastify/fastify-<span class="hljs-keyword">type</span>-stripping/node_modules/<span class="hljs-meta">@fastify</span>/autoload/lib/find-plugins.js:<span class="hljs-number">124</span>:<span class="hljs-number">3</span>)
    at processDirContents (<span class="hljs-regexp">/Users/m</span>anuelsalinardi/coding/blogs/fastify/fastify-<span class="hljs-keyword">type</span>-stripping/node_modules/<span class="hljs-meta">@fastify</span>/autoload/lib/find-plugins.js:<span class="hljs-number">101</span>
:<span class="hljs-number">7</span>)                                                                                                                                                     at buildTree (<span class="hljs-regexp">/Users/m</span>anuelsalinardi/coding/blogs/fastify/fastify-<span class="hljs-keyword">type</span>-stripping/node_modules/<span class="hljs-meta">@fastify</span>/autoload/lib/find-plugins.js:<span class="hljs-number">41</span>:<span class="hljs-number">9</span>)
    at <span class="hljs-keyword">async</span> findPlugins (<span class="hljs-regexp">/Users/m</span>anuelsalinardi/coding/blogs/fastify/fastify-<span class="hljs-keyword">type</span>-stripping/node_modules/<span class="hljs-meta">@fastify</span>/autoload/lib/find-plugins.js:<span class="hljs-number">14</span>:<span class="hljs-number">3</span>
)                                                                                                                                                       at <span class="hljs-keyword">async</span> autoload (<span class="hljs-regexp">/Users/m</span>anuelsalinardi/coding/blogs/fastify/fastify-<span class="hljs-keyword">type</span>-stripping/node_modules/<span class="hljs-meta">@fastify</span>/autoload/index.js:<span class="hljs-number">20</span>:<span class="hljs-number">22</span>)

Node.js v22<span class="hljs-number">.8</span><span class="hljs-number">.0</span>

 *  The terminal process <span class="hljs-string">"/bin/zsh '-l', '-c', 'source ~/.zshrc &amp;&amp; npm run start'"</span> terminated <span class="hljs-keyword">with</span> exit code: <span class="hljs-number">1.</span>
</code></pre>
<p>Boom! Oh no un altro errore!</p>
<p>L’errore arriva dal modulo @fastify/autoload e ci dice:<br />To fix this error compile TypeScript to JavaScript or use 'ts-node' to run your app.</p>
<p>È un controllo che viene fatto da @fastify/autoload, per disabilitarlo bisogna lanciare il server con la variabile d’ambiente: <strong>FASTIFY_AUTOLOAD_TYPESCRIPT</strong></p>
<h3 id="heading-tentativo-4-fastifyautoloadtypescript">Tentativo 4: <strong>FASTIFY_AUTOLOAD_TYPESCRIPT</strong></h3>
<p>Aggiungiamo anche la variabile d’ambiente: <strong>FASTIFY_AUTOLOAD_TYPESCRIPT</strong> allo script di “<strong>start</strong>”</p>
<pre><code class="lang-json"><span class="hljs-string">"start"</span>: <span class="hljs-string">"NODE_OPTIONS='--experimental-strip-types' FASTIFY_AUTOLOAD_TYPESCRIPT=1 fastify start -l info src/app.ts"</span>
</code></pre>
<pre><code class="lang-json"> *  Executing task: source ~/.zshrc &amp;&amp; npm run start 


&gt; fastify-type-stripping@<span class="hljs-number">1.0</span><span class="hljs-number">.0</span> start
&gt; NODE_OPTIONS='--experimental-strip-types' FASTIFY_AUTOLOAD_TYPESCRIPT=<span class="hljs-number">1</span> fastify start -l info src/app.ts

(node:<span class="hljs-number">36950</span>) ExperimentalWarning: Type Stripping is an experimental feature and might change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
{<span class="hljs-attr">"level"</span>:<span class="hljs-number">30</span>,<span class="hljs-attr">"time"</span>:<span class="hljs-number">1727709779182</span>,<span class="hljs-attr">"pid"</span>:<span class="hljs-number">36950</span>,<span class="hljs-attr">"hostname"</span>:<span class="hljs-string">"Manuels-MacBook-Pro.local"</span>,<span class="hljs-attr">"msg"</span>:<span class="hljs-string">"Server listening at http://127.0.0.1:3000"</span>}
{<span class="hljs-attr">"level"</span>:<span class="hljs-number">30</span>,<span class="hljs-attr">"time"</span>:<span class="hljs-number">1727709779183</span>,<span class="hljs-attr">"pid"</span>:<span class="hljs-number">36950</span>,<span class="hljs-attr">"hostname"</span>:<span class="hljs-string">"Manuels-MacBook-Pro.local"</span>,<span class="hljs-attr">"msg"</span>:<span class="hljs-string">"Server listening at http://[::1]:3000"</span>}
</code></pre>
<p>Wow finalmente funziona!</p>
<p>Ora siamo in grado di lanciare il nostro server direttamente dal codice sorgente TypeScript senza più il bisogno di compilazione.</p>
<p>Possiamo fare lo stesso anche per lo script di “<strong>test</strong>“ del package.json ed eliminare dalle “<strong>devDependencies</strong>“ la libreria “<strong>ts-node</strong>“ perchè lo stesso lo possiamo fare nativamente in Node.js grazie al nuovo flag <a target="_blank" href="https://nodejs.org/docs/latest/api/cli.html#--experimental-strip-types"><strong>--experimental-strip-types</strong></a></p>
]]></content:encoded></item><item><title><![CDATA[Come Personalizzare il Logger in Fastify]]></title><description><![CDATA[Read the article in english language

Intro
Fastify usa pino come logger, uno dei più performanti logger per Node.js.
Quindi generalmente per personalizzare il logger di Fastify, si applicano gli stessi concetti per personalizzare pino, ma con qualch...]]></description><link>https://blog.manuelsalinardi.it/come-personalizzare-il-logger-in-fastify</link><guid isPermaLink="true">https://blog.manuelsalinardi.it/come-personalizzare-il-logger-in-fastify</guid><category><![CDATA[fastify]]></category><category><![CDATA[pino]]></category><category><![CDATA[logging]]></category><category><![CDATA[server]]></category><dc:creator><![CDATA[Manuel Salinardi]]></dc:creator><pubDate>Fri, 02 Aug 2024 07:48:45 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1722584847625/c4eb2f12-4d9d-4dd7-ba40-edc4116e11eb.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p><a target="_blank" href="https://blog.manuelsalinardi.dev/how-to-customize-logger-in-fastify">Read the article in english language</a></p>
</blockquote>
<h2 id="heading-intro">Intro</h2>
<p>Fastify usa <a target="_blank" href="https://github.com/pinojs/pino">pino</a> come logger, uno dei più performanti logger per Node.js.</p>
<p>Quindi generalmente per personalizzare il logger di Fastify, si applicano gli stessi concetti per personalizzare pino, ma con qualche eccezione che vedremo in questo articolo.</p>
<blockquote>
<p>Per questo articolo è stato usato Node.js v20.13.1</p>
</blockquote>
<h2 id="heading-abilitare-il-logger">Abilitare il logger</h2>
<p>Di default il logger di Fastify è disabilitato, per abilitarlo basta impostarlo a true quando si inizializza Fastify.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> fastify = Fastify({
  <span class="hljs-attr">logger</span>: <span class="hljs-literal">true</span>
})
</code></pre>
<h2 id="heading-scriviamo-il-primo-log">Scriviamo il primo log</h2>
<p>Per vedere il primo esempio di log creiamo un semplice progetto Fastify, che poi useremo per tutti gli altri esempi che vedremo in questo articolo.</p>
<h3 id="heading-creiamo-il-progetto-fastify">Creiamo il progetto Fastify</h3>
<p>Creiamo ed entriamo nella cartella del progetto</p>
<pre><code class="lang-bash">mkdir my-logger
<span class="hljs-built_in">cd</span> my-logger
</code></pre>
<p>Installiamo Fastify nel progetto</p>
<pre><code class="lang-bash">npm install fastify
</code></pre>
<p>Questo il contenuto del file package.json generato:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"dependencies"</span>: {
    <span class="hljs-attr">"fastify"</span>: <span class="hljs-string">"^4.28.1"</span>
  }
}
</code></pre>
<p>Creiamo il file index.js</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// importa Fastify</span>
<span class="hljs-keyword">const</span> Fastify = <span class="hljs-built_in">require</span>(<span class="hljs-string">'fastify'</span>)

<span class="hljs-comment">// inizializza Fastify</span>
<span class="hljs-keyword">const</span> fastify = Fastify({
  <span class="hljs-attr">logger</span>: <span class="hljs-literal">true</span> <span class="hljs-comment">// abilita il logger (disabilitato di default)</span>
})

<span class="hljs-comment">// inizia a loggare</span>
fastify.log.info(<span class="hljs-string">'Hello Log!'</span>)
</code></pre>
<blockquote>
<p><em>Non abbiamo avviato alcun server, abbiamo solo inizializzato Fastify, e questo è sufficiente per iniziare a loggare.</em></p>
</blockquote>
<p>Eseguiamo il file con il comando:</p>
<pre><code class="lang-bash">node server
</code></pre>
<p>Come output otterremo un oggetto simile a questo:</p>
<pre><code class="lang-json">{<span class="hljs-attr">"level"</span>:<span class="hljs-number">30</span>,<span class="hljs-attr">"time"</span>:<span class="hljs-number">1721369918925</span>,<span class="hljs-attr">"pid"</span>:<span class="hljs-number">85758</span>,<span class="hljs-attr">"hostname"</span>:<span class="hljs-string">"Manuels-MacBook-Pro.local"</span>,<span class="hljs-attr">"msg"</span>:<span class="hljs-string">"Hello World!"</span>}
</code></pre>
<p>Formattiamolo meglio per analizzarlo</p>
<pre><code class="lang-json">{
   <span class="hljs-attr">"level"</span>:<span class="hljs-number">30</span>, <span class="hljs-comment">// 30 rappresenta il livello info dovuto da: fastify.log.info</span>
   <span class="hljs-attr">"time"</span>:<span class="hljs-number">1721369918925</span>, <span class="hljs-comment">// Unix time in millisecondi ( epoch time )</span>
   <span class="hljs-attr">"pid"</span>:<span class="hljs-number">85758</span>, <span class="hljs-comment">// id del processo ( equivlente a process.pid )</span>
   <span class="hljs-attr">"hostname"</span>:<span class="hljs-string">"Manuels-MacBook-Pro.local"</span>, <span class="hljs-comment">// il nome host del sistema operativo. ( equivlente a require('os').hostname() ) </span>
   <span class="hljs-attr">"msg"</span>:<span class="hljs-string">"Hello World!"</span> <span class="hljs-comment">// la stringa che abbiamo loggato</span>
}
</code></pre>
<p>Vediamo che dalla nostra semplice chiamata di log:</p>
<pre><code class="lang-javascript">fastify.log.info(<span class="hljs-string">'Hello Log!'</span>)
</code></pre>
<p>Solo "msg" è effettivamente quello che abbiamo scritto noi, tutti gli altri campi sono stati aggiunti da Pino, e verranno aggiunti per ogni chiamata al logger.</p>
<h2 id="heading-personalizziamo-il-logger">Personalizziamo il Logger</h2>
<p>Abbiamo visto che di default quando facciamo una chiamata al logger viene loggato un oggetto JSON in cui il nostro messaggio viene messo come valore del campo "msg".</p>
<p>Ma vengono anche aggiunti da Pino i campi:</p>
<ul>
<li><p>"level"</p>
</li>
<li><p>"time"</p>
</li>
<li><p>"pid"</p>
</li>
<li><p>"hostname"</p>
</li>
</ul>
<p>In questa sezione vedremo come personalizzarli uno ad uno.</p>
<h3 id="heading-level">level</h3>
<p><strong>Prima di personalizzare il campo "level" vediamo un pò come funziona.</strong></p>
<p>In Pino esistono diversi livelli di log:</p>
<p>"fatal" | "error" | "warn" | "info" | "debug" | "trace".</p>
<p>Per ogni livello esiste il suo corrispondente metodo:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> Fastify = <span class="hljs-built_in">require</span>(<span class="hljs-string">'fastify'</span>)

<span class="hljs-keyword">const</span> fastify = Fastify({
  <span class="hljs-attr">logger</span>: <span class="hljs-literal">true</span>
})

fastify.log.fatal(<span class="hljs-string">'Hello fatal!'</span>)
fastify.log.error(<span class="hljs-string">'Hello error!'</span>)
fastify.log.warn(<span class="hljs-string">'Hello warn!'</span>)
fastify.log.info(<span class="hljs-string">'Hello info!'</span>)
fastify.log.debug(<span class="hljs-string">'Hello debug!'</span>)
fastify.log.trace(<span class="hljs-string">'Hello trace!'</span>)
</code></pre>
<p>Il codice di sopra genererà il seguente output:</p>
<pre><code class="lang-json">{<span class="hljs-attr">"level"</span>:<span class="hljs-number">60</span>,<span class="hljs-attr">"time"</span>:<span class="hljs-number">1722405654790</span>,<span class="hljs-attr">"pid"</span>:<span class="hljs-number">57113</span>,<span class="hljs-attr">"hostname"</span>:<span class="hljs-string">"Manuels-MacBook-Pro.local"</span>,<span class="hljs-attr">"msg"</span>:<span class="hljs-string">"Hello fatal!"</span>}
{<span class="hljs-attr">"level"</span>:<span class="hljs-number">50</span>,<span class="hljs-attr">"time"</span>:<span class="hljs-number">1722405654790</span>,<span class="hljs-attr">"pid"</span>:<span class="hljs-number">57113</span>,<span class="hljs-attr">"hostname"</span>:<span class="hljs-string">"Manuels-MacBook-Pro.local"</span>,<span class="hljs-attr">"msg"</span>:<span class="hljs-string">"Hello error!"</span>}
{<span class="hljs-attr">"level"</span>:<span class="hljs-number">40</span>,<span class="hljs-attr">"time"</span>:<span class="hljs-number">1722405654790</span>,<span class="hljs-attr">"pid"</span>:<span class="hljs-number">57113</span>,<span class="hljs-attr">"hostname"</span>:<span class="hljs-string">"Manuels-MacBook-Pro.local"</span>,<span class="hljs-attr">"msg"</span>:<span class="hljs-string">"Hello warn!"</span>}
{<span class="hljs-attr">"level"</span>:<span class="hljs-number">30</span>,<span class="hljs-attr">"time"</span>:<span class="hljs-number">1722405654790</span>,<span class="hljs-attr">"pid"</span>:<span class="hljs-number">57113</span>,<span class="hljs-attr">"hostname"</span>:<span class="hljs-string">"Manuels-MacBook-Pro.local"</span>,<span class="hljs-attr">"msg"</span>:<span class="hljs-string">"Hello info!"</span>}
</code></pre>
<p>Ma c'è qualcosa di strano, perché dal codice abbiamo chiamato il logger 6 volte, ma ne sono stati generati solo 4?:</p>
<ul>
<li><p>"Hello fatal!"</p>
</li>
<li><p>"Hello error!"</p>
</li>
<li><p>"Hello warn!"</p>
</li>
<li><p>"Hello info!"</p>
</li>
</ul>
<p>Mancano all'appello:</p>
<ul>
<li><p>"Hello debug!"</p>
</li>
<li><p>"Hello trace!"</p>
</li>
</ul>
<p>Dove sono finiti?</p>
<p>Pino associa ad ogni livello un valore numerico:</p>
<ul>
<li><p>"fatal" = 60</p>
</li>
<li><p>"error" = 50</p>
</li>
<li><p>"warn" = 40</p>
</li>
<li><p>"info" = 30</p>
</li>
<li><p>"debug" = 20</p>
</li>
<li><p>"trace" = 10</p>
</li>
</ul>
<p>Che possiamo vedere nel campo "level" dei log generati.</p>
<p>Di default vengono scartati i log sotto il livello 30, ovvero "info".</p>
<p>Per cambiare questo default e vedere i log di tutti i livelli, bisogna impostare il campo level del logger in questo modo:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> fastify = Fastify({
  <span class="hljs-attr">logger</span>: {
    <span class="hljs-attr">level</span>: <span class="hljs-string">'trace'</span>
  }
})
</code></pre>
<p>Ora vediamo che ci sono tutti e 6 i log:</p>
<pre><code class="lang-json">{<span class="hljs-attr">"level"</span>:<span class="hljs-number">60</span>,<span class="hljs-attr">"time"</span>:<span class="hljs-number">1722406770355</span>,<span class="hljs-attr">"pid"</span>:<span class="hljs-number">57832</span>,<span class="hljs-attr">"hostname"</span>:<span class="hljs-string">"Manuels-MacBook-Pro.local"</span>,<span class="hljs-attr">"msg"</span>:<span class="hljs-string">"Hello fatal!"</span>}
{<span class="hljs-attr">"level"</span>:<span class="hljs-number">50</span>,<span class="hljs-attr">"time"</span>:<span class="hljs-number">1722406770355</span>,<span class="hljs-attr">"pid"</span>:<span class="hljs-number">57832</span>,<span class="hljs-attr">"hostname"</span>:<span class="hljs-string">"Manuels-MacBook-Pro.local"</span>,<span class="hljs-attr">"msg"</span>:<span class="hljs-string">"Hello error!"</span>}
{<span class="hljs-attr">"level"</span>:<span class="hljs-number">40</span>,<span class="hljs-attr">"time"</span>:<span class="hljs-number">1722406770355</span>,<span class="hljs-attr">"pid"</span>:<span class="hljs-number">57832</span>,<span class="hljs-attr">"hostname"</span>:<span class="hljs-string">"Manuels-MacBook-Pro.local"</span>,<span class="hljs-attr">"msg"</span>:<span class="hljs-string">"Hello warn!"</span>}
{<span class="hljs-attr">"level"</span>:<span class="hljs-number">30</span>,<span class="hljs-attr">"time"</span>:<span class="hljs-number">1722406770355</span>,<span class="hljs-attr">"pid"</span>:<span class="hljs-number">57832</span>,<span class="hljs-attr">"hostname"</span>:<span class="hljs-string">"Manuels-MacBook-Pro.local"</span>,<span class="hljs-attr">"msg"</span>:<span class="hljs-string">"Hello info!"</span>}
{<span class="hljs-attr">"level"</span>:<span class="hljs-number">20</span>,<span class="hljs-attr">"time"</span>:<span class="hljs-number">1722406770355</span>,<span class="hljs-attr">"pid"</span>:<span class="hljs-number">57832</span>,<span class="hljs-attr">"hostname"</span>:<span class="hljs-string">"Manuels-MacBook-Pro.local"</span>,<span class="hljs-attr">"msg"</span>:<span class="hljs-string">"Hello debug!"</span>}
{<span class="hljs-attr">"level"</span>:<span class="hljs-number">10</span>,<span class="hljs-attr">"time"</span>:<span class="hljs-number">1722406770355</span>,<span class="hljs-attr">"pid"</span>:<span class="hljs-number">57832</span>,<span class="hljs-attr">"hostname"</span>:<span class="hljs-string">"Manuels-MacBook-Pro.local"</span>,<span class="hljs-attr">"msg"</span>:<span class="hljs-string">"Hello trace!"</span>}
</code></pre>
<p><strong>Ora che abbiamo capito come funziona il campo "level", vediamo come personalizzarlo:</strong></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> fastify = Fastify({
  <span class="hljs-attr">logger</span>: {
    <span class="hljs-attr">formatters</span>: {
      level (label, number) {
        <span class="hljs-comment">// questi campi verranno sostituiti al campo "level" di default</span>
        <span class="hljs-keyword">return</span> {
          <span class="hljs-attr">myLevel</span>: <span class="hljs-string">`<span class="hljs-subst">${label}</span>-<span class="hljs-subst">${number}</span>`</span>,
          <span class="hljs-attr">levelName</span>: label,
          <span class="hljs-attr">levelNumber</span>: number 
        }
      }
    }
  }
})
</code></pre>
<p>Ora i log generati saranno in questo formato:</p>
<pre><code class="lang-json">{<span class="hljs-attr">"myLevel"</span>:<span class="hljs-string">"fatal-60"</span>,<span class="hljs-attr">"levelName"</span>:<span class="hljs-string">"fatal"</span>,<span class="hljs-attr">"levelNumber"</span>:<span class="hljs-number">60</span>,<span class="hljs-attr">"time"</span>:<span class="hljs-number">1722576879835</span>,<span class="hljs-attr">"pid"</span>:<span class="hljs-number">90770</span>,<span class="hljs-attr">"hostname"</span>:<span class="hljs-string">"Manuels-MacBook-Pro.local"</span>,<span class="hljs-attr">"msg"</span>:<span class="hljs-string">"Hello fatal!"</span>}
{<span class="hljs-attr">"myLevel"</span>:<span class="hljs-string">"error-50"</span>,<span class="hljs-attr">"levelName"</span>:<span class="hljs-string">"error"</span>,<span class="hljs-attr">"levelNumber"</span>:<span class="hljs-number">50</span>,<span class="hljs-attr">"time"</span>:<span class="hljs-number">1722576879835</span>,<span class="hljs-attr">"pid"</span>:<span class="hljs-number">90770</span>,<span class="hljs-attr">"hostname"</span>:<span class="hljs-string">"Manuels-MacBook-Pro.local"</span>,<span class="hljs-attr">"msg"</span>:<span class="hljs-string">"Hello error!"</span>}
{<span class="hljs-attr">"myLevel"</span>:<span class="hljs-string">"warn-40"</span>,<span class="hljs-attr">"levelName"</span>:<span class="hljs-string">"warn"</span>,<span class="hljs-attr">"levelNumber"</span>:<span class="hljs-number">40</span>,<span class="hljs-attr">"time"</span>:<span class="hljs-number">1722576879835</span>,<span class="hljs-attr">"pid"</span>:<span class="hljs-number">90770</span>,<span class="hljs-attr">"hostname"</span>:<span class="hljs-string">"Manuels-MacBook-Pro.local"</span>,<span class="hljs-attr">"msg"</span>:<span class="hljs-string">"Hello warn!"</span>}
{<span class="hljs-attr">"myLevel"</span>:<span class="hljs-string">"info-30"</span>,<span class="hljs-attr">"levelName"</span>:<span class="hljs-string">"info"</span>,<span class="hljs-attr">"levelNumber"</span>:<span class="hljs-number">30</span>,<span class="hljs-attr">"time"</span>:<span class="hljs-number">1722576879835</span>,<span class="hljs-attr">"pid"</span>:<span class="hljs-number">90770</span>,<span class="hljs-attr">"hostname"</span>:<span class="hljs-string">"Manuels-MacBook-Pro.local"</span>,<span class="hljs-attr">"msg"</span>:<span class="hljs-string">"Hello info!"</span>}
</code></pre>
<p>Come possiamo vedere dai log, ora al posto del campo "level" ne troviamo altri 3:</p>
<ul>
<li><p>"myLevel"</p>
</li>
<li><p>"levelName"</p>
</li>
<li><p>"levelNumber"</p>
</li>
</ul>
<h3 id="heading-time">time</h3>
<p>Si può personalizzare il campo time impostando il campo "timestamp" nel seguente modo:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> Fastify = <span class="hljs-built_in">require</span>(<span class="hljs-string">'fastify'</span>)
<span class="hljs-keyword">const</span> pino = <span class="hljs-built_in">require</span>(<span class="hljs-string">'pino'</span>)

<span class="hljs-keyword">const</span> fastify = Fastify({
  <span class="hljs-attr">logger</span>: {
    <span class="hljs-attr">timestamp</span>: pino.stdTimeFunctions.isoTime, <span class="hljs-comment">// converte da epoch time a ISO time</span>
  }
})

fastify.log.info(<span class="hljs-string">'Hello info!'</span>)
</code></pre>
<pre><code class="lang-json">{
   <span class="hljs-attr">"level"</span>:<span class="hljs-number">30</span>,
   <span class="hljs-attr">"time"</span>:<span class="hljs-string">"2024-07-18T06:48:52.040Z"</span>, <span class="hljs-comment">// ISO Time</span>
   <span class="hljs-attr">"pid"</span>:<span class="hljs-number">19929</span>,
   <span class="hljs-attr">"hostname"</span>:<span class="hljs-string">"Manuels-MacBook-Pro.local"</span>,
   <span class="hljs-attr">"msg"</span>:<span class="hljs-string">"Hello info"</span>
}
</code></pre>
<p>Pino ci mette a disposizione le seguenti funzioni per la formattazione del "timestamp":</p>
<ul>
<li><p><code>pino.stdTimeFunctions.epochTime</code>: Milliseconds since Unix epoch (Default)</p>
</li>
<li><p><code>pino.stdTimeFunctions.unixTime</code>: Seconds since Unix epoch</p>
</li>
<li><p><code>pino.stdTimeFunctions.nullTime</code>: Clears timestamp property (Used when <code>timestamp: false</code>)</p>
</li>
<li><p><code>pino.stdTimeFunctions.isoTime</code>: ISO 8601-formatted time in UTC</p>
</li>
</ul>
<p>Dalla documentazione ufficiale: <a target="_blank" href="https://github.com/pinojs/pino/blob/main/docs/api.md#pino-stdtimefunctions">Pino stdTimeFunctions</a></p>
<h3 id="heading-pid-e-hostname">pid e hostname</h3>
<p>Per personalizzare i campi "pid" e "hostname" bisogna personalizzare i bindings.</p>
<p>I bindings sono dati aggiuntivi che possono essere associati ad un logger per essere inclusi in tutti i log generati da quel logger. Questi dati vengono utilizzati per arricchire le informazioni dei log senza dover specificare manualmente queste informazioni ogni volta che si effettua una chiamata di log.</p>
<p>Di default Pino logger aggiunge nei bindings: "pid" e "hostname", quindi per rimuoverli ed aggiungere campi a nostro piacimento dobbiamo modificare i bindings in questo modo:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> Fastify = <span class="hljs-built_in">require</span>(<span class="hljs-string">'fastify'</span>)
<span class="hljs-keyword">const</span> os = <span class="hljs-built_in">require</span>(<span class="hljs-string">'os'</span>)

<span class="hljs-keyword">const</span> fastify = Fastify({
  <span class="hljs-attr">logger</span>: {
    <span class="hljs-attr">formatters</span>: {
      <span class="hljs-attr">bindings</span>: <span class="hljs-function">(<span class="hljs-params">bindings</span>) =&gt;</span> {
        <span class="hljs-keyword">return</span> { 
          <span class="hljs-attr">osMachine</span>: os.machine(),
          <span class="hljs-attr">osType</span>: os.type()
        };
      },
    },
  }
})

fastify.log.info(<span class="hljs-string">'Hello info!'</span>)
</code></pre>
<pre><code class="lang-json">{
   <span class="hljs-attr">"level"</span>:<span class="hljs-number">30</span>,
   <span class="hljs-attr">"time"</span>:<span class="hljs-number">1721283756521</span>,
   <span class="hljs-attr">"osMachine"</span>:<span class="hljs-string">"arm64"</span>,
   <span class="hljs-attr">"osType"</span>:<span class="hljs-string">"Darwin"</span>,
   <span class="hljs-attr">"msg"</span>:<span class="hljs-string">"Hello info!"</span>
}
</code></pre>
<p>Vediamo che "pid" e "hostname" non ci sono più, ma al loro posto ci sono "osMachine" e "osType".</p>
<p>Invece di sostituire "pid" e "hostname" possiamo anche tenerli ed aggiungere campi in questo modo:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> fastify = Fastify({
  <span class="hljs-attr">logger</span>: {
    <span class="hljs-attr">formatters</span>: {
      <span class="hljs-attr">bindings</span>: <span class="hljs-function">(<span class="hljs-params">bindings</span>) =&gt;</span> {
        <span class="hljs-keyword">return</span> { 
          <span class="hljs-attr">pid</span>: bindings.pid,
          <span class="hljs-attr">hostname</span>: bindings.hostname,
          <span class="hljs-attr">osMachine</span>: os.machine(),
          <span class="hljs-attr">osType</span>: os.type()
        };
      },
    },
  }
})

fastify.log.info(<span class="hljs-string">'Hello info!'</span>)
</code></pre>
<pre><code class="lang-json">{
   <span class="hljs-attr">"level"</span>:<span class="hljs-number">30</span>,
   <span class="hljs-attr">"time"</span>:<span class="hljs-number">1722577623975</span>,
   <span class="hljs-attr">"pid"</span>:<span class="hljs-number">91371</span>,
   <span class="hljs-attr">"hostname"</span>:<span class="hljs-string">"Manuels-MacBook-Pro.local"</span>,
   <span class="hljs-attr">"osMachine"</span>:<span class="hljs-string">"arm64"</span>,
   <span class="hljs-attr">"osType"</span>:<span class="hljs-string">"Darwin"</span>,
   <span class="hljs-attr">"msg"</span>:<span class="hljs-string">"Hello info!"</span>
}
</code></pre>
<h2 id="heading-cose-il-request-logger">Cos'è il Request Logger?</h2>
<p>Fino ad ora abbiamo usato il logger di Fastify ma senza mai avviare un server.<br />Fastify ci mette a disposizione una feature utilissima che si chiama appunto "Request Logger".<br />Ora con qualche esempio pratico vedremo come funziona e come personalizzarlo.</p>
<h3 id="heading-creiamo-il-server">Creiamo il server</h3>
<pre><code class="lang-javascript"> <span class="hljs-keyword">const</span> Fastify = <span class="hljs-built_in">require</span>(<span class="hljs-string">'fastify'</span>)

<span class="hljs-keyword">const</span> fastify = Fastify({
  <span class="hljs-attr">logger</span>: <span class="hljs-literal">true</span>
})

<span class="hljs-comment">// Dichiara la rotta GET /</span>
fastify.get(<span class="hljs-string">'/'</span>, <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handler</span> (<span class="hljs-params">request, reply</span>) </span>{
  fastify.log.info(<span class="hljs-string">'Fastify logger!'</span>)
  <span class="hljs-comment">// notare che viene usato request.log al posto di fastify.log</span>
  <span class="hljs-comment">// al contrario degli esempi precedenti</span>
  request.log.info(<span class="hljs-string">'Request logger!'</span>)
  <span class="hljs-keyword">return</span> { <span class="hljs-attr">hello</span>: <span class="hljs-string">'world'</span> }
})

<span class="hljs-comment">// Avvia il server sulla porta 3000</span>
fastify.listen({ <span class="hljs-attr">port</span>: <span class="hljs-number">3000</span> }, <span class="hljs-function">(<span class="hljs-params">err</span>) =&gt;</span> {
  <span class="hljs-keyword">if</span> (err) {
    fastify.log.error(err)
    process.exit(<span class="hljs-number">1</span>)
  }
})
</code></pre>
<p>In questo server abbiamo definito solo una rotta GET "/" che:</p>
<ul>
<li><p>logga la stringa "Fastify logger!" (con fastify.log)</p>
</li>
<li><p>logga la stringa "Request logger!" (con request.log)</p>
</li>
<li><p>ritorna come risposta l'oggetto: { hello: 'world' }</p>
</li>
</ul>
<p>Da notare che per il primo log viene usato "fastify.log" per il secondo "request.log". Poi vedremo con un esempio qual'è la differenza tra i due.</p>
<p>Avviamo il server con:</p>
<pre><code class="lang-bash">node server
</code></pre>
<p>All'avvio del server vedremo dei log auto generati da Fastify del genere:</p>
<pre><code class="lang-json">{<span class="hljs-attr">"level"</span>:<span class="hljs-number">30</span>,<span class="hljs-attr">"time"</span>:<span class="hljs-number">1720591420640</span>,<span class="hljs-attr">"pid"</span>:<span class="hljs-number">69286</span>,<span class="hljs-attr">"hostname"</span>:<span class="hljs-string">"Manuels-MacBook-Pro.local"</span>,<span class="hljs-attr">"msg"</span>:<span class="hljs-string">"Server listening at http://127.0.0.1:3000"</span>}
{<span class="hljs-attr">"level"</span>:<span class="hljs-number">30</span>,<span class="hljs-attr">"time"</span>:<span class="hljs-number">1720591420643</span>,<span class="hljs-attr">"pid"</span>:<span class="hljs-number">69286</span>,<span class="hljs-attr">"hostname"</span>:<span class="hljs-string">"Manuels-MacBook-Pro.local"</span>,<span class="hljs-attr">"msg"</span>:<span class="hljs-string">"Server listening at http://[::1]:3000"</span>}
</code></pre>
<h3 id="heading-prima-chiamata-http">Prima chiamata HTTP</h3>
<p>Ora proviamo a fare una chiamata HTTP, ad esempio con Postman alla nostra unica rotta appena creata.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1720592120698/1d083e16-2e12-4631-a4e7-5e5646f05294.png" alt class="image--center mx-auto" /></p>
<p>Questa chiamata ha generato 4 log:</p>
<pre><code class="lang-json"><span class="hljs-comment">// auto generato da Fastify (quando riceve una richiesta HTTP)</span>
{
   <span class="hljs-attr">"level"</span>:<span class="hljs-number">30</span>,
   <span class="hljs-attr">"time"</span>:<span class="hljs-number">1720678823876</span>,
   <span class="hljs-attr">"pid"</span>:<span class="hljs-number">2292</span>,
   <span class="hljs-attr">"hostname"</span>:<span class="hljs-string">"Manuels-MacBook-Pro.local"</span>,
   <span class="hljs-attr">"reqId"</span>:<span class="hljs-string">"req-1"</span>,
   <span class="hljs-attr">"req"</span>:{
      <span class="hljs-attr">"method"</span>:<span class="hljs-string">"GET"</span>,
      <span class="hljs-attr">"url"</span>:<span class="hljs-string">"/"</span>,
      <span class="hljs-attr">"hostname"</span>:<span class="hljs-string">"127.0.0.1:3000"</span>,
      <span class="hljs-attr">"remoteAddress"</span>:<span class="hljs-string">"127.0.0.1"</span>,
      <span class="hljs-attr">"remotePort"</span>:<span class="hljs-number">63548</span>
   },
   <span class="hljs-attr">"msg"</span>:<span class="hljs-string">"incoming request"</span>
}

<span class="hljs-comment">// generato da noi con fastify.log.info('Fastify logger!')</span>
{
   <span class="hljs-attr">"level"</span>:<span class="hljs-number">30</span>,
   <span class="hljs-attr">"time"</span>:<span class="hljs-number">1720678823877</span>,
   <span class="hljs-attr">"pid"</span>:<span class="hljs-number">2292</span>,
   <span class="hljs-attr">"hostname"</span>:<span class="hljs-string">"Manuels-MacBook-Pro.local"</span>,
   <span class="hljs-attr">"msg"</span>:<span class="hljs-string">"Fastify logger!"</span>
}

<span class="hljs-comment">// generato da noi con request.log.info('Request logger!')</span>
{
   <span class="hljs-attr">"level"</span>:<span class="hljs-number">30</span>,
   <span class="hljs-attr">"time"</span>:<span class="hljs-number">1720678823877</span>,
   <span class="hljs-attr">"pid"</span>:<span class="hljs-number">2292</span>,
   <span class="hljs-attr">"hostname"</span>:<span class="hljs-string">"Manuels-MacBook-Pro.local"</span>,
   <span class="hljs-attr">"reqId"</span>:<span class="hljs-string">"req-1"</span>,
   <span class="hljs-attr">"msg"</span>:<span class="hljs-string">"Request logger!"</span>
}

<span class="hljs-comment">// auto generato da Fastify (quando invia una risposta HTTP)</span>
{
   <span class="hljs-attr">"level"</span>:<span class="hljs-number">30</span>,
   <span class="hljs-attr">"time"</span>:<span class="hljs-number">1720678823880</span>,
   <span class="hljs-attr">"pid"</span>:<span class="hljs-number">2292</span>,
   <span class="hljs-attr">"hostname"</span>:<span class="hljs-string">"Manuels-MacBook-Pro.local"</span>,
   <span class="hljs-attr">"reqId"</span>:<span class="hljs-string">"req-1"</span>,
   <span class="hljs-attr">"res"</span>:{
      <span class="hljs-attr">"statusCode"</span>:<span class="hljs-number">200</span>
   },
   <span class="hljs-attr">"responseTime"</span>:<span class="hljs-number">3.9882499873638153</span>,
   <span class="hljs-attr">"msg"</span>:<span class="hljs-string">"request completed"</span>
}
</code></pre>
<p>Tra i quattro log c'è un intruso, riesci a vederlo?</p>
<p>L'intruso è il secondo: "Fastify logger!"</p>
<p>Infatti possiamo vedere che è l'unico che non ha il "reqId". Non ha il "reqId" perché è l'unico a non essere stato generato dal request logger.</p>
<p>Fastify per ogni richiesta genera un "reqId" utile per collegare tutti i log a quella specifica richiesta, nel nostro caso:</p>
<ul>
<li><p>"incoming request" è stato generato da Fastify alla ricezione della richiesta, e per questa richiesta è stato creato un id univoco "reqId":"req-1".</p>
</li>
<li><p>"Fastify logger!" è stato generato da noi con fastify.log.info('Fastify logger!'), ma siccome abbiamo usato fastify.log, questo log non farà parte del contesto della richiesta, quindi "reqId":"req-1" non verrà loggato.</p>
</li>
<li><p>"Request logger!" è stato generato da noi con request.log.info('Request logger!'), in questo caso siccome abbiamo usato request.log, questo log farà parte del contesto della richiesta quindi "reqId":"req-1" verrà loggato.</p>
</li>
<li><p>"request completed" è stato generato da Fastify quando la richiesta è terminata e la risposta è stata inviata.</p>
</li>
</ul>
<h3 id="heading-disabilitare-il-request-logger">Disabilitare il request logger</h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> app = Fastify({
  <span class="hljs-attr">disableRequestLogging</span>: <span class="hljs-literal">true</span>, <span class="hljs-comment">// disabilita il request logger</span>
  <span class="hljs-attr">logger</span>: <span class="hljs-literal">true</span>
})
</code></pre>
<p>Basta impostare disableRequestLogging a true per non far generare a Fastify in automatico i log alla ricezione della richiesta ed all'invio della risposta.</p>
<p>Vediamo l'esempio precedente con il request logger disabilitato:</p>
<pre><code class="lang-javascript"> <span class="hljs-keyword">const</span> Fastify = <span class="hljs-built_in">require</span>(<span class="hljs-string">'fastify'</span>)

<span class="hljs-keyword">const</span> fastify = Fastify({
  <span class="hljs-attr">disableRequestLogging</span>: <span class="hljs-literal">true</span>, <span class="hljs-comment">// disabilita il request logger</span>
  <span class="hljs-attr">logger</span>: <span class="hljs-literal">true</span>
})

<span class="hljs-comment">// Dichiara la rotta GET /</span>
fastify.get(<span class="hljs-string">'/'</span>, <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handler</span> (<span class="hljs-params">request, reply</span>) </span>{
  fastify.log.info(<span class="hljs-string">'Fastify logger!'</span>) <span class="hljs-comment">// viene loggato</span>
  request.log.info(<span class="hljs-string">'Request logger!'</span>) <span class="hljs-comment">// viene comunque loggato</span>
  <span class="hljs-keyword">return</span> { <span class="hljs-attr">hello</span>: <span class="hljs-string">'world'</span> }
})

<span class="hljs-comment">// Avvia il server sulla porta 3000</span>
fastify.listen({ <span class="hljs-attr">port</span>: <span class="hljs-number">3000</span> }, <span class="hljs-function">(<span class="hljs-params">err</span>) =&gt;</span> {
  <span class="hljs-keyword">if</span> (err) {
    fastify.log.error(err)
    process.exit(<span class="hljs-number">1</span>)
  }
})
</code></pre>
<p>Avviamo il server:</p>
<pre><code class="lang-bash">node server
</code></pre>
<pre><code class="lang-json">{<span class="hljs-attr">"level"</span>:<span class="hljs-number">30</span>,<span class="hljs-attr">"time"</span>:<span class="hljs-number">1722320649945</span>,<span class="hljs-attr">"pid"</span>:<span class="hljs-number">20123</span>,<span class="hljs-attr">"hostname"</span>:<span class="hljs-string">"Manuels-MacBook-Pro.local"</span>,<span class="hljs-attr">"msg"</span>:<span class="hljs-string">"Server listening at http://[::1]:3000"</span>}
{<span class="hljs-attr">"level"</span>:<span class="hljs-number">30</span>,<span class="hljs-attr">"time"</span>:<span class="hljs-number">1722320649947</span>,<span class="hljs-attr">"pid"</span>:<span class="hljs-number">20123</span>,<span class="hljs-attr">"hostname"</span>:<span class="hljs-string">"Manuels-MacBook-Pro.local"</span>,<span class="hljs-attr">"msg"</span>:<span class="hljs-string">"Server listening at http://127.0.0.1:3000"</span>}
</code></pre>
<p>I log automatici all'avvio del server vengono comunque generati.</p>
<p>Facciamo ancora la chiamata HTTP con Postman:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1720592120698/1d083e16-2e12-4631-a4e7-5e5646f05294.png" alt class="image--center mx-auto" /></p>
<pre><code class="lang-json">{<span class="hljs-attr">"level"</span>:<span class="hljs-number">30</span>,<span class="hljs-attr">"time"</span>:<span class="hljs-number">1722320840449</span>,<span class="hljs-attr">"pid"</span>:<span class="hljs-number">20123</span>,<span class="hljs-attr">"hostname"</span>:<span class="hljs-string">"Manuels-MacBook-Pro.local"</span>,<span class="hljs-attr">"msg"</span>:<span class="hljs-string">"Fastify logger!"</span>}
{<span class="hljs-attr">"level"</span>:<span class="hljs-number">30</span>,<span class="hljs-attr">"time"</span>:<span class="hljs-number">1722320840449</span>,<span class="hljs-attr">"pid"</span>:<span class="hljs-number">20123</span>,<span class="hljs-attr">"hostname"</span>:<span class="hljs-string">"Manuels-MacBook-Pro.local"</span>,<span class="hljs-attr">"reqId"</span>:<span class="hljs-string">"req-1"</span>,<span class="hljs-attr">"msg"</span>:<span class="hljs-string">"Request logger!"</span>}
</code></pre>
<p>Vediamo che ora sono solo 2 i log, mancano quelli auto generati da Fastify:</p>
<ul>
<li><p>"incoming request"</p>
</li>
<li><p>"request completed"</p>
</li>
</ul>
<p>Ma il request logger che abbiamo invocato manualmente con:</p>
<pre><code class="lang-javascript">request.log.info(<span class="hljs-string">'Request logger!'</span>)
</code></pre>
<p>Viene comunque loggato.</p>
<h2 id="heading-personalizziamo-il-request-logger">Personalizziamo il Request Logger</h2>
<p>Il Request Logger viene usato nei seguenti casi:</p>
<ul>
<li><p>chiamato esplicitamente con il metodo: "request.log"</p>
</li>
<li><p>chiamato automaticamente da Fastify ad ogni nuova richiesta HTTP ricevuta</p>
</li>
<li><p>chiamato automaticamente da Fastify ad ogni risposta HTTP inviata</p>
</li>
<li><p>chiamato automaticamente da Fastify ad ogni eccezione generata dentro una rotta (non lo abbiamo ancora visto ma lo vedremo dopo)</p>
</li>
</ul>
<p>Rivediamoli uno ad uno:</p>
<pre><code class="lang-json"><span class="hljs-comment">// chiamato esplicitamente con il metodo: "request.log"</span>
{
   <span class="hljs-attr">"level"</span>:<span class="hljs-number">30</span>,
   <span class="hljs-attr">"time"</span>:<span class="hljs-number">1720678823877</span>,
   <span class="hljs-attr">"pid"</span>:<span class="hljs-number">2292</span>,
   <span class="hljs-attr">"hostname"</span>:<span class="hljs-string">"Manuels-MacBook-Pro.local"</span>,
   <span class="hljs-attr">"reqId"</span>:<span class="hljs-string">"req-1"</span>,
   <span class="hljs-attr">"msg"</span>:<span class="hljs-string">"Request logger!"</span>
}

<span class="hljs-comment">// chiamato automaticamente da Fastify ad ogni nuova richiesta HTTP ricevuta</span>
{
   <span class="hljs-attr">"level"</span>:<span class="hljs-number">30</span>,
   <span class="hljs-attr">"time"</span>:<span class="hljs-number">1720678823876</span>,
   <span class="hljs-attr">"pid"</span>:<span class="hljs-number">2292</span>,
   <span class="hljs-attr">"hostname"</span>:<span class="hljs-string">"Manuels-MacBook-Pro.local"</span>,
   <span class="hljs-attr">"reqId"</span>:<span class="hljs-string">"req-1"</span>,
   <span class="hljs-attr">"req"</span>:{
      <span class="hljs-attr">"method"</span>:<span class="hljs-string">"GET"</span>,
      <span class="hljs-attr">"url"</span>:<span class="hljs-string">"/"</span>,
      <span class="hljs-attr">"hostname"</span>:<span class="hljs-string">"127.0.0.1:3000"</span>,
      <span class="hljs-attr">"remoteAddress"</span>:<span class="hljs-string">"127.0.0.1"</span>,
      <span class="hljs-attr">"remotePort"</span>:<span class="hljs-number">63548</span>
   },
   <span class="hljs-attr">"msg"</span>:<span class="hljs-string">"incoming request"</span>
}

<span class="hljs-comment">// chiamato automaticamente da Fastify ad ogni risposta HTTP inviata</span>
{
   <span class="hljs-attr">"level"</span>:<span class="hljs-number">30</span>,
   <span class="hljs-attr">"time"</span>:<span class="hljs-number">1720678823880</span>,
   <span class="hljs-attr">"pid"</span>:<span class="hljs-number">2292</span>,
   <span class="hljs-attr">"hostname"</span>:<span class="hljs-string">"Manuels-MacBook-Pro.local"</span>,
   <span class="hljs-attr">"reqId"</span>:<span class="hljs-string">"req-1"</span>,
   <span class="hljs-attr">"res"</span>:{
      <span class="hljs-attr">"statusCode"</span>:<span class="hljs-number">200</span>
   },
   <span class="hljs-attr">"responseTime"</span>:<span class="hljs-number">3.9882499873638153</span>,
   <span class="hljs-attr">"msg"</span>:<span class="hljs-string">"request completed"</span>
}

<span class="hljs-comment">// chiamato automaticamente da Fastify ad ogni eccezione generata dentro una rotta </span>
{
   <span class="hljs-attr">"level"</span>:<span class="hljs-number">50</span>,
   <span class="hljs-attr">"time"</span>:<span class="hljs-number">1720766398542</span>,
   <span class="hljs-attr">"pid"</span>:<span class="hljs-number">40069</span>,
   <span class="hljs-attr">"hostname"</span>:<span class="hljs-string">"Manuels-MacBook-Pro.local"</span>,
   <span class="hljs-attr">"reqId"</span>:<span class="hljs-string">"req-1"</span>,
   <span class="hljs-attr">"req"</span>:{
      <span class="hljs-attr">"method"</span>:<span class="hljs-string">"GET"</span>,
      <span class="hljs-attr">"url"</span>:<span class="hljs-string">"/"</span>,
      <span class="hljs-attr">"hostname"</span>:<span class="hljs-string">"127.0.0.1:3000"</span>,
      <span class="hljs-attr">"remoteAddress"</span>:<span class="hljs-string">"127.0.0.1"</span>,
      <span class="hljs-attr">"remotePort"</span>:<span class="hljs-number">54858</span>
   },
   <span class="hljs-attr">"res"</span>:{
      <span class="hljs-attr">"statusCode"</span>:<span class="hljs-number">500</span>
   },
   <span class="hljs-attr">"err"</span>:{
      <span class="hljs-attr">"type"</span>:<span class="hljs-string">"Error"</span>,
      <span class="hljs-attr">"message"</span>:<span class="hljs-string">"Boom!"</span>,
      <span class="hljs-attr">"stack"</span>:<span class="hljs-string">"Error: Boom!\n    at Object..."</span>
   },
   <span class="hljs-attr">"msg"</span>:<span class="hljs-string">"Boom!"</span>
}
</code></pre>
<p>Quindi possiamo vedere che dal Request Logger vengono aggiunti i seguenti campi:</p>
<ul>
<li><p>"reqId"</p>
</li>
<li><p>"req"</p>
</li>
<li><p>"res"</p>
</li>
<li><p>"err"</p>
</li>
</ul>
<p>In questa sezione vedremo come personalizzarli uno ad uno.</p>
<h3 id="heading-reqid">reqId</h3>
<p>La prima cosa che possiamo personalizzare del request logger è il "reqId", con il metodo: genReqId</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> currentId = <span class="hljs-number">1</span>

<span class="hljs-keyword">const</span> fastify = Fastify({
  <span class="hljs-attr">logger</span>: <span class="hljs-literal">true</span>,
  <span class="hljs-attr">genReqId</span>: <span class="hljs-function">(<span class="hljs-params">req</span>) =&gt;</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-string">`my-custom-id-<span class="hljs-subst">${currentId++}</span>`</span>
  }
})
</code></pre>
<pre><code class="lang-json">{
   <span class="hljs-attr">"level"</span>:<span class="hljs-number">30</span>,
   <span class="hljs-attr">"time"</span>:<span class="hljs-number">1722580879611</span>,
   <span class="hljs-attr">"pid"</span>:<span class="hljs-number">93284</span>,
   <span class="hljs-attr">"hostname"</span>:<span class="hljs-string">"Manuels-MacBook-Pro.local"</span>,
   <span class="hljs-attr">"reqId"</span>:<span class="hljs-string">"my-custom-id-1"</span>, <span class="hljs-comment">// Generato dal nostro genReqId</span>
   <span class="hljs-attr">"msg"</span>:<span class="hljs-string">"Request logger!"</span>
}
</code></pre>
<h3 id="heading-req-e-res">req e res</h3>
<p>Come facciamo a personalizzare gli oggetti "req" e "res" che vengono loggati? Per esempio aggiungere o rimuovere campi?</p>
<p>Si usa l'oggetto serializers per questo:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> fastify = Fastify({
  <span class="hljs-attr">logger</span>: {
    <span class="hljs-attr">serializers</span>: {
      req (request) { <span class="hljs-comment">// in req vogliamo solo: method e url</span>
        <span class="hljs-keyword">return</span> {
          <span class="hljs-attr">method</span>: request.method,
          <span class="hljs-attr">url</span>: request.url,
        };
      },
      res (reply) { <span class="hljs-comment">// in res vogliamo: method, url e statusCode</span>
        <span class="hljs-keyword">return</span> {
          <span class="hljs-attr">method</span>: reply.request.method,
          <span class="hljs-attr">url</span>: reply.request.url,
          <span class="hljs-attr">statusCode</span>: reply.statusCode
        }
      },
    }
  }
})
</code></pre>
<pre><code class="lang-json"><span class="hljs-comment">// ora req contiene solo method e url</span>
<span class="hljs-comment">// invece che il default: method, url, hostname, remoteAddress, remotePort</span>
{
   <span class="hljs-attr">"level"</span>:<span class="hljs-number">30</span>,
   <span class="hljs-attr">"time"</span>:<span class="hljs-number">1720765071638</span>,
   <span class="hljs-attr">"pid"</span>:<span class="hljs-number">38418</span>,
   <span class="hljs-attr">"hostname"</span>:<span class="hljs-string">"Manuels-MacBook-Pro.local"</span>,
   <span class="hljs-attr">"reqId"</span>:<span class="hljs-string">"req-1"</span>,
   <span class="hljs-attr">"req"</span>:{
      <span class="hljs-attr">"method"</span>:<span class="hljs-string">"GET"</span>,
      <span class="hljs-attr">"url"</span>:<span class="hljs-string">"/"</span>
   },
   <span class="hljs-attr">"msg"</span>:<span class="hljs-string">"incoming request"</span>
}

<span class="hljs-comment">// questo log non è affetto dal serializers perchè non contiene ne req ne res</span>
{
   <span class="hljs-attr">"level"</span>:<span class="hljs-number">30</span>,
   <span class="hljs-attr">"time"</span>:<span class="hljs-number">1720765071638</span>,
   <span class="hljs-attr">"pid"</span>:<span class="hljs-number">38418</span>,
   <span class="hljs-attr">"hostname"</span>:<span class="hljs-string">"Manuels-MacBook-Pro.local"</span>,
   <span class="hljs-attr">"msg"</span>:<span class="hljs-string">"Fastify logger!"</span>
}

<span class="hljs-comment">// anche questo log non è affetto dal serializers perchè non contiene ne req ne res</span>
{
   <span class="hljs-attr">"level"</span>:<span class="hljs-number">30</span>,
   <span class="hljs-attr">"time"</span>:<span class="hljs-number">1720765071638</span>,
   <span class="hljs-attr">"pid"</span>:<span class="hljs-number">38418</span>,
   <span class="hljs-attr">"hostname"</span>:<span class="hljs-string">"Manuels-MacBook-Pro.local"</span>,
   <span class="hljs-attr">"reqId"</span>:<span class="hljs-string">"req-1"</span>,
   <span class="hljs-attr">"msg"</span>:<span class="hljs-string">"Request logger!"</span>
}

<span class="hljs-comment">// ora res contiene: method, url, statusCode</span>
<span class="hljs-comment">// invece che il default: statusCode</span>
{
   <span class="hljs-attr">"level"</span>:<span class="hljs-number">30</span>,
   <span class="hljs-attr">"time"</span>:<span class="hljs-number">1720765071640</span>,
   <span class="hljs-attr">"pid"</span>:<span class="hljs-number">38418</span>,
   <span class="hljs-attr">"hostname"</span>:<span class="hljs-string">"Manuels-MacBook-Pro.local"</span>,
   <span class="hljs-attr">"reqId"</span>:<span class="hljs-string">"req-1"</span>,
   <span class="hljs-attr">"res"</span>:{
      <span class="hljs-attr">"method"</span>:<span class="hljs-string">"GET"</span>,
      <span class="hljs-attr">"url"</span>:<span class="hljs-string">"/"</span>,
      <span class="hljs-attr">"statusCode"</span>:<span class="hljs-number">200</span>
   },
   <span class="hljs-attr">"responseTime"</span>:<span class="hljs-number">2.362499952316284</span>,
   <span class="hljs-attr">"msg"</span>:<span class="hljs-string">"request completed"</span>
}
</code></pre>
<h3 id="heading-err">err</h3>
<p>Se dentro una rotta viene generata un eccezione, Fastify la gestisce e la logga per noi in automatico, vediamo un esempio.</p>
<p>Generiamo un eccezione nella nostra rotta:</p>
<pre><code class="lang-javascript">fastify.get(<span class="hljs-string">'/'</span>, <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handler</span> (<span class="hljs-params">request, reply</span>) </span>{
  <span class="hljs-keyword">throw</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'Boom!'</span>) <span class="hljs-comment">// Questo genererà l'errore</span>
  fastify.log.info(<span class="hljs-string">'Fastify logger!'</span>)
  request.log.info(<span class="hljs-string">'Request logger!'</span>)
  <span class="hljs-keyword">return</span> { <span class="hljs-attr">hello</span>: <span class="hljs-string">'world'</span> }
})
</code></pre>
<p>Se proviamo a fare la chiamata HTTP a GET "/" vedremo generato questo log:</p>
<pre><code class="lang-json">{
   <span class="hljs-attr">"level"</span>:<span class="hljs-number">50</span>,
   <span class="hljs-attr">"time"</span>:<span class="hljs-number">1720766398542</span>,
   <span class="hljs-attr">"pid"</span>:<span class="hljs-number">40069</span>,
   <span class="hljs-attr">"hostname"</span>:<span class="hljs-string">"Manuels-MacBook-Pro.local"</span>,
   <span class="hljs-attr">"reqId"</span>:<span class="hljs-string">"req-1"</span>,
   <span class="hljs-attr">"req"</span>:{
      <span class="hljs-attr">"method"</span>:<span class="hljs-string">"GET"</span>,
      <span class="hljs-attr">"url"</span>:<span class="hljs-string">"/"</span>,
      <span class="hljs-attr">"hostname"</span>:<span class="hljs-string">"127.0.0.1:3000"</span>,
      <span class="hljs-attr">"remoteAddress"</span>:<span class="hljs-string">"127.0.0.1"</span>,
      <span class="hljs-attr">"remotePort"</span>:<span class="hljs-number">54858</span>
   },
   <span class="hljs-attr">"res"</span>:{
      <span class="hljs-attr">"statusCode"</span>:<span class="hljs-number">500</span>
   },
   <span class="hljs-comment">// fastify di default logga un oggetto err, con dentro: type, message e stack</span>
   <span class="hljs-attr">"err"</span>:{
      <span class="hljs-attr">"type"</span>:<span class="hljs-string">"Error"</span>,
      <span class="hljs-attr">"message"</span>:<span class="hljs-string">"Boom!"</span>,
      <span class="hljs-attr">"stack"</span>:<span class="hljs-string">"Error: Boom!\n    at Object.handler (/Users/manuelsalinardi/..."</span>
   },
   <span class="hljs-attr">"msg"</span>:<span class="hljs-string">"Boom!"</span>
}
</code></pre>
<p>Vediamo che per l'errore, Fastify genera un log con dentro l'oggetto "err".</p>
<p>Se non volessimo loggare lo stack per motivi di sicurezza?</p>
<p>Anche in questo caso possiamo personalizzare l'oggetto "err" come abbiamo fatto con gli oggetti "req" e "res", usando l'oggetto serializers:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> fastify = Fastify({
  <span class="hljs-attr">logger</span>: {
    <span class="hljs-attr">serializers</span>: {
      req (request) {
        <span class="hljs-keyword">return</span> {
          <span class="hljs-attr">method</span>: request.method,
          <span class="hljs-attr">url</span>: request.url,
        };
      },
      res (reply) {
        <span class="hljs-keyword">return</span> {
          <span class="hljs-attr">method</span>: reply.request.method,
          <span class="hljs-attr">url</span>: reply.request.url,
          <span class="hljs-attr">statusCode</span>: reply.statusCode
        }
      },
      <span class="hljs-comment">// qui personalizziamo l'oggetto err</span>
      err (error) {
        <span class="hljs-keyword">return</span> {
          <span class="hljs-attr">type</span>: error.name,
          <span class="hljs-attr">message</span>: error.message,
        }
      }
    }
  }
})
</code></pre>
<p>Questo il log generato:</p>
<pre><code class="lang-json">{
   <span class="hljs-attr">"level"</span>:<span class="hljs-number">50</span>,
   <span class="hljs-attr">"time"</span>:<span class="hljs-number">1720767139181</span>,
   <span class="hljs-attr">"pid"</span>:<span class="hljs-number">40774</span>,
   <span class="hljs-attr">"hostname"</span>:<span class="hljs-string">"Manuels-MacBook-Pro.local"</span>,
   <span class="hljs-attr">"reqId"</span>:<span class="hljs-string">"req-1"</span>,
   <span class="hljs-attr">"req"</span>:{
      <span class="hljs-attr">"method"</span>:<span class="hljs-string">"GET"</span>,
      <span class="hljs-attr">"url"</span>:<span class="hljs-string">"/"</span>
   },
   <span class="hljs-attr">"res"</span>:{
      <span class="hljs-attr">"method"</span>:<span class="hljs-string">"GET"</span>,
      <span class="hljs-attr">"url"</span>:<span class="hljs-string">"/"</span>,
      <span class="hljs-attr">"statusCode"</span>:<span class="hljs-number">500</span>
   },
   <span class="hljs-comment">// ora stack non è più presente</span>
   <span class="hljs-attr">"err"</span>:{
      <span class="hljs-attr">"type"</span>:<span class="hljs-string">"Error"</span>,
      <span class="hljs-attr">"message"</span>:<span class="hljs-string">"Boom!"</span>
   },
   <span class="hljs-attr">"msg"</span>:<span class="hljs-string">"Boom!"</span>
}
</code></pre>
<h2 id="heading-aggiungere-nuovi-campi-al-request-logger">Aggiungere nuovi campi al Request Logger</h2>
<p>Fastify ci aiuta a mettere in relazione tra loro tutti i log di una specifica richiesta tramite il campo "reqId".</p>
<p>Abbiamo visto che "reqId" lo possiamo anche personalizzare, ma come facciamo ad aggiungere un altro campo simile a "reqId", ma scelto a nostro piacimento?</p>
<p>Ad esempio vogliamo aggiungere al request logger il campo "user", che ci viene passato come parametro nella URL da chi fa la chiamata HTTP.</p>
<p>Per fare questo bisogna chiamare il metodo di Fastify <a target="_blank" href="https://fastify.dev/docs/latest/Reference/Server/#setchildloggerfactory">setChildLoggerFactory</a>:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> Fastify = <span class="hljs-built_in">require</span>(<span class="hljs-string">'fastify'</span>)

<span class="hljs-keyword">const</span> fastify = Fastify({
  <span class="hljs-attr">logger</span>: <span class="hljs-literal">true</span>,
})

<span class="hljs-comment">// aggiunge campi al Request Logger</span>
fastify.setChildLoggerFactory(<span class="hljs-function">(<span class="hljs-params">logger, bindings, opts, rawReq</span>) =&gt;</span> {

  <span class="hljs-comment">// recupera user dalla URL</span>
  <span class="hljs-keyword">const</span> urlString = rawReq.url || <span class="hljs-string">''</span>
  <span class="hljs-keyword">const</span> queryString = urlString.split(<span class="hljs-string">'?'</span>)[<span class="hljs-number">1</span>]
  <span class="hljs-keyword">const</span> searchParams = <span class="hljs-keyword">new</span> URLSearchParams(queryString)
  <span class="hljs-keyword">const</span> user = searchParams.get(<span class="hljs-string">'user'</span>)

  <span class="hljs-comment">// aggiunge user ai bindings</span>
  bindings.user = user

  <span class="hljs-keyword">return</span> logger.child(bindings, opts)
})

<span class="hljs-comment">// dichiara la rotta</span>
fastify.get(<span class="hljs-string">'/'</span>, <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handler</span> (<span class="hljs-params">request, reply</span>) </span>{
  request.log.info(<span class="hljs-string">'Request logger!'</span>)
  <span class="hljs-keyword">return</span> { <span class="hljs-attr">hello</span>: <span class="hljs-string">'world'</span> }
})

<span class="hljs-comment">// avvia il server</span>
fastify.listen({ <span class="hljs-attr">port</span>: <span class="hljs-number">3000</span> }, <span class="hljs-function">(<span class="hljs-params">err</span>) =&gt;</span> {
  <span class="hljs-keyword">if</span> (err) {
    fastify.log.error(err)
    process.exit(<span class="hljs-number">1</span>)
  }
})
</code></pre>
<p>Ora facciamo una chiamata HTTP con il parametro user:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1722584394555/d3ba4b2a-eaeb-437d-9ec5-004fbf32e637.png" alt class="image--center mx-auto" /></p>
<p>Vedremo dei log del genere:</p>
<pre><code class="lang-json">{
   <span class="hljs-attr">"level"</span>:<span class="hljs-number">30</span>,
   <span class="hljs-attr">"time"</span>:<span class="hljs-number">1722583839646</span>,
   <span class="hljs-attr">"pid"</span>:<span class="hljs-number">95780</span>,
   <span class="hljs-attr">"hostname"</span>:<span class="hljs-string">"Manuels-MacBook-Pro.local"</span>,
   <span class="hljs-attr">"reqId"</span>:<span class="hljs-string">"req-2"</span>,
   <span class="hljs-attr">"user"</span>:<span class="hljs-string">"manuel"</span>, <span class="hljs-comment">// nuovo campo user</span>
   <span class="hljs-attr">"req"</span>:{
      <span class="hljs-attr">"method"</span>:<span class="hljs-string">"GET"</span>,
      <span class="hljs-attr">"url"</span>:<span class="hljs-string">"/?user=manuel"</span>,
      <span class="hljs-attr">"hostname"</span>:<span class="hljs-string">"127.0.0.1:3000"</span>,
      <span class="hljs-attr">"remoteAddress"</span>:<span class="hljs-string">"127.0.0.1"</span>,
      <span class="hljs-attr">"remotePort"</span>:<span class="hljs-number">52098</span>
   },
   <span class="hljs-attr">"msg"</span>:<span class="hljs-string">"incoming request"</span>
}{
   <span class="hljs-attr">"level"</span>:<span class="hljs-number">30</span>,
   <span class="hljs-attr">"time"</span>:<span class="hljs-number">1722583839646</span>,
   <span class="hljs-attr">"pid"</span>:<span class="hljs-number">95780</span>,
   <span class="hljs-attr">"hostname"</span>:<span class="hljs-string">"Manuels-MacBook-Pro.local"</span>,
   <span class="hljs-attr">"reqId"</span>:<span class="hljs-string">"req-2"</span>,
   <span class="hljs-attr">"user"</span>:<span class="hljs-string">"manuel"</span>, <span class="hljs-comment">// nuovo campo user</span>
   <span class="hljs-attr">"msg"</span>:<span class="hljs-string">"Request logger!"</span>
}{
   <span class="hljs-attr">"level"</span>:<span class="hljs-number">30</span>,
   <span class="hljs-attr">"time"</span>:<span class="hljs-number">1722583839648</span>,
   <span class="hljs-attr">"pid"</span>:<span class="hljs-number">95780</span>,
   <span class="hljs-attr">"hostname"</span>:<span class="hljs-string">"Manuels-MacBook-Pro.local"</span>,
   <span class="hljs-attr">"reqId"</span>:<span class="hljs-string">"req-2"</span>,
   <span class="hljs-attr">"user"</span>:<span class="hljs-string">"manuel"</span>, <span class="hljs-comment">// nuovo campo user</span>
   <span class="hljs-attr">"res"</span>:{
      <span class="hljs-attr">"statusCode"</span>:<span class="hljs-number">200</span>
   },
   <span class="hljs-attr">"responseTime"</span>:<span class="hljs-number">1.4203753471374512</span>,
   <span class="hljs-attr">"msg"</span>:<span class="hljs-string">"request completed"</span>
}
</code></pre>
<p>Ora il Request Logger oltre al "reqId" avrà anche il nuovo campo "user".</p>
<blockquote>
<p>Manuel Salinardi</p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Fastify: Type safe con i Type-Providers]]></title><description><![CDATA[Click here to read the article in english language

Pre requisiti
Node.js v20.13.1
Cosa sono i Type-Providers
Documentazione: Type-Providers
I Type-Providers sono una feature solo per i progetti Fastify che utilizzano Typescript come linguaggio.
Ci s...]]></description><link>https://blog.manuelsalinardi.it/fastify-type-safe-con-i-type-providers</link><guid isPermaLink="true">https://blog.manuelsalinardi.it/fastify-type-safe-con-i-type-providers</guid><category><![CDATA[Typebox]]></category><category><![CDATA[fastify]]></category><category><![CDATA[TypeScript]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[REST API]]></category><category><![CDATA[APIs]]></category><category><![CDATA[server]]></category><dc:creator><![CDATA[Manuel Salinardi]]></dc:creator><pubDate>Fri, 28 Jun 2024 06:42:07 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1719556664771/54cdc35a-d1be-4426-a22f-baaaa55efa54.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p><a target="_blank" href="https://blog.manuelsalinardi.dev/fastify-type-safe-with-type-providers">Click here to read the article in english language</a></p>
</blockquote>
<h2 id="heading-pre-requisiti">Pre requisiti</h2>
<p>Node.js v20.13.1</p>
<h2 id="heading-cosa-sono-i-type-providers">Cosa sono i Type-Providers</h2>
<p><a target="_blank" href="https://fastify.dev/docs/latest/Reference/Type-Providers/">Documentazione: Type-Providers</a></p>
<p>I Type-Providers sono una feature solo per i progetti Fastify che utilizzano Typescript come linguaggio.</p>
<p>Ci sono diversi tipi di Type-Providers, per questo articolo utilizzeremo: <a target="_blank" href="https://github.com/sinclairzx81/typebox#validation">TypeBox</a></p>
<h2 id="heading-creiamo-il-progetto-fastify-con-typescript">Creiamo il progetto Fastify con Typescript</h2>
<p>Apriamo il terminale e digitiamo questo comando:</p>
<pre><code class="lang-bash">npx fastify-cli generate my-app --lang=ts
</code></pre>
<p>Questo comando genererà un progetto Fastify con Typescript, utilizzando l'utility <a target="_blank" href="https://github.com/fastify/fastify-cli">fastify-cli</a></p>
<h2 id="heading-struttura-del-progetto">Struttura del progetto</h2>
<p>A questo punto abbiamo un progetto con la seguente struttura:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1718260761874/d8a079fb-480d-4cbc-9311-0b00c4270c9f.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-contenuto-del-packagejson-generato">Contenuto del package.json generato</h2>
<pre><code class="lang-json">{
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"my-app"</span>,
  <span class="hljs-attr">"version"</span>: <span class="hljs-string">"1.0.0"</span>,
  <span class="hljs-attr">"description"</span>: <span class="hljs-string">"This project was bootstrapped with Fastify-CLI."</span>,
  <span class="hljs-attr">"main"</span>: <span class="hljs-string">"app.ts"</span>,
  <span class="hljs-attr">"directories"</span>: {
    <span class="hljs-attr">"test"</span>: <span class="hljs-string">"test"</span>
  },
  <span class="hljs-attr">"scripts"</span>: {
    <span class="hljs-attr">"test"</span>: <span class="hljs-string">"npm run build:ts &amp;&amp; tsc -p test/tsconfig.json &amp;&amp; c8 node --test -r ts-node/register \"test/**/*.ts\""</span>,
    <span class="hljs-attr">"start"</span>: <span class="hljs-string">"npm run build:ts &amp;&amp; fastify start -l info dist/app.js"</span>,
    <span class="hljs-attr">"build:ts"</span>: <span class="hljs-string">"tsc"</span>,
    <span class="hljs-attr">"watch:ts"</span>: <span class="hljs-string">"tsc -w"</span>,
    <span class="hljs-attr">"dev"</span>: <span class="hljs-string">"npm run build:ts &amp;&amp; concurrently -k -p \"[{name}]\" -n \"TypeScript,App\" -c \"yellow.bold,cyan.bold\" \"npm:watch:ts\" \"npm:dev:start\""</span>,
    <span class="hljs-attr">"dev:start"</span>: <span class="hljs-string">"fastify start --ignore-watch=.ts$ -w -l info -P dist/app.js"</span>
  },
  <span class="hljs-attr">"keywords"</span>: [],
  <span class="hljs-attr">"author"</span>: <span class="hljs-string">""</span>,
  <span class="hljs-attr">"license"</span>: <span class="hljs-string">"ISC"</span>,
  <span class="hljs-attr">"dependencies"</span>: {
    <span class="hljs-attr">"@fastify/autoload"</span>: <span class="hljs-string">"^5.0.0"</span>,
    <span class="hljs-attr">"@fastify/sensible"</span>: <span class="hljs-string">"^5.0.0"</span>,
    <span class="hljs-attr">"@fastify/type-provider-typebox"</span>: <span class="hljs-string">"^4.0.0"</span>,
    <span class="hljs-attr">"fastify"</span>: <span class="hljs-string">"^4.26.1"</span>,
    <span class="hljs-attr">"fastify-cli"</span>: <span class="hljs-string">"^6.2.1"</span>,
    <span class="hljs-attr">"fastify-plugin"</span>: <span class="hljs-string">"^4.0.0"</span>
  },
  <span class="hljs-attr">"devDependencies"</span>: {
    <span class="hljs-attr">"@types/node"</span>: <span class="hljs-string">"^20.4.4"</span>,
    <span class="hljs-attr">"c8"</span>: <span class="hljs-string">"^9.0.0"</span>,
    <span class="hljs-attr">"concurrently"</span>: <span class="hljs-string">"^8.2.2"</span>,
    <span class="hljs-attr">"fastify-tsconfig"</span>: <span class="hljs-string">"^2.0.0"</span>,
    <span class="hljs-attr">"ts-node"</span>: <span class="hljs-string">"^10.4.0"</span>,
    <span class="hljs-attr">"typescript"</span>: <span class="hljs-string">"^5.2.2"</span>
  }
}
</code></pre>
<h2 id="heading-installiamo-le-dipendenze-del-packagejson">Installiamo le dipendenze del package.json</h2>
<p>Entriamo dentro alla cartella del progetto "my-app" appena creato e installiamo tutte le dipendenze del package.json con questo comando:</p>
<pre><code class="lang-bash">npm install
</code></pre>
<h2 id="heading-rotta-root-default">Rotta root default</h2>
<p>fastify-cli ha già creato per noi 2 rotte, le possiamo trovare dentro i file:</p>
<ol>
<li><p>src/routes/root.ts</p>
</li>
<li><p>src/routes/example/index.ts</p>
</li>
</ol>
<p>Vediamo il codice generato per la rotta root:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { FastifyPluginAsync } <span class="hljs-keyword">from</span> <span class="hljs-string">"fastify"</span>

<span class="hljs-keyword">const</span> example: FastifyPluginAsync = <span class="hljs-keyword">async</span> (fastify, opts): <span class="hljs-built_in">Promise</span>&lt;<span class="hljs-built_in">void</span>&gt; =&gt; {
  fastify.get(<span class="hljs-string">'/'</span>, <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">request, reply</span>) </span>{
    <span class="hljs-keyword">return</span> <span class="hljs-string">'this is an example'</span>
  })
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> example;
</code></pre>
<p>Questa rotta definisce una chiamata HTTP GET che come riposta restituisce una stringa: 'this is an example'.</p>
<h2 id="heading-aggiungiamo-la-nostra-rotta">Aggiungiamo la nostra rotta</h2>
<p>Per fare un esempio il più semplice possibile, Aggiungiamo una rotta che prende in input del testo e come risposta ritorna il testo tutto in maiuscolo.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { FastifyPluginAsync } <span class="hljs-keyword">from</span> <span class="hljs-string">"fastify"</span>

<span class="hljs-keyword">const</span> example: FastifyPluginAsync = <span class="hljs-keyword">async</span> (fastify, opts): <span class="hljs-built_in">Promise</span>&lt;<span class="hljs-built_in">void</span>&gt; =&gt; {

  fastify.get(<span class="hljs-string">'/'</span>, <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">request, reply</span>) </span>{
    <span class="hljs-keyword">return</span> <span class="hljs-string">'this is an example'</span>
  })

  fastify.post(<span class="hljs-string">'/uppercase'</span>, <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">request, reply</span>) </span>{
    <span class="hljs-keyword">const</span> body = request.body
    <span class="hljs-keyword">const</span> text = body.text
    <span class="hljs-keyword">return</span> { textResult: text.toUpperCase()} 
  })

}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> example;
</code></pre>
<p>Abbiamo aggiunto una rotta di tipo POST, la rotta si aspetta nel body una sola proprietà 'text' e ritorna un oggetto con un unica proprietà 'textResult'.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1718347595519/7d91eea8-1871-489b-a628-44312d5915b7.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-errore-typescript">Errore Typescript</h2>
<p>A questo punto Typescript genererà un errore, perché inizialmente body è typo unknown, quindi dobbiamo tipizzare l'oggetto body.</p>
<p>Potremmo tipizzare l'oggetto body direttamente con un interfaccia Typescript, ma siccome useremo uno schema per validare la rotta, lasceremo generare i tipi Typescript direttamente dal nostro schema, così da avere una sola fonte della verità.</p>
<h2 id="heading-installiamo-la-libreria-type-provider-typebox">Installiamo la libreria type-provider-typebox</h2>
<pre><code class="lang-bash">npm i @fastify/type-provider-typebox
</code></pre>
<h2 id="heading-creiamo-lo-schema-con-typebox">Creiamo lo schema con Typebox</h2>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { Type } <span class="hljs-keyword">from</span> <span class="hljs-string">'@fastify/type-provider-typebox'</span>
<span class="hljs-keyword">import</span> { FastifyPluginAsync } <span class="hljs-keyword">from</span> <span class="hljs-string">"fastify"</span>

<span class="hljs-keyword">const</span> example: FastifyPluginAsync = <span class="hljs-keyword">async</span> (fastify, opts): <span class="hljs-built_in">Promise</span>&lt;<span class="hljs-built_in">void</span>&gt; =&gt; {

  fastify.get(<span class="hljs-string">'/'</span>, <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">request, reply</span>) </span>{
    <span class="hljs-keyword">return</span> <span class="hljs-string">'this is an example'</span>
  })

  fastify.post(<span class="hljs-string">'/uppercase'</span>, {
    schema: {
      body: Type.Object({
        text: Type.String(),
      }),
      response: {
        <span class="hljs-number">200</span>: Type.Object({ 
          textResult: Type.String() 
        }),
      }
    }
  }, <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">request, reply</span>) </span>{
    <span class="hljs-keyword">const</span> body = request.body
    <span class="hljs-keyword">const</span> text = body.text
    <span class="hljs-keyword">return</span> { textResult: text.toUpperCase()} 
  })

}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> example;
</code></pre>
<p>Ora abbiamo creato uno schema che definisce che:</p>
<ul>
<li><p>nel body accetta solo una proprietà 'text' di tipo stringa</p>
</li>
<li><p>come risposta restituisce un oggetto con solo una proprietà 'textResult' di tipo stringa</p>
</li>
</ul>
<p>Lo schema fa si che:</p>
<ul>
<li><p>Il cliente se nel body non passa esattamente un oggetto con una proprietà 'text' di tipo stringa riceverà un errore 404</p>
</li>
<li><p>Il server se non ritorna esattamente un oggetto con dentro la proprietà 'textResult' di tipo stringa, il cliente riceverà un errore 500</p>
</li>
</ul>
<p>Ma c'è ancora un problema, lo schema è giusto ma Typescript ancora non compila perché request.body è ancora di tipo unknown.</p>
<h2 id="heading-usiamo-lo-schema-per-generare-i-tipi-per-la-rotta">Usiamo lo schema per generare i tipi per la rotta</h2>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { Type, FastifyPluginAsyncTypebox } <span class="hljs-keyword">from</span> <span class="hljs-string">'@fastify/type-provider-typebox'</span>

<span class="hljs-keyword">const</span> example: FastifyPluginAsyncTypebox = <span class="hljs-keyword">async</span> (fastify, opts): <span class="hljs-built_in">Promise</span>&lt;<span class="hljs-built_in">void</span>&gt; =&gt; {

  fastify.get(<span class="hljs-string">'/'</span>, <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">request, reply</span>) </span>{
    <span class="hljs-keyword">return</span> <span class="hljs-string">'this is an example'</span>
  })

  fastify.post(<span class="hljs-string">'/uppercase'</span>, {
    schema: {
      body: Type.Object({
        text: Type.String(),
      }),
      response: {
        <span class="hljs-number">200</span>: Type.Object({ 
          textResult: Type.String() 
        }),
      }
    }
  }, <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">request, reply</span>) </span>{
    <span class="hljs-keyword">const</span> body = request.body
    <span class="hljs-keyword">const</span> text = body.text
    <span class="hljs-keyword">return</span> { textResult: text.toUpperCase()} 
  })

}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> example;
</code></pre>
<p>Ci basta solo sostituire il tipo:</p>
<ul>
<li>FastifyPluginAsync (tipo per il plugin importato direttamente da 'fastify')</li>
</ul>
<p>Con il tipo:</p>
<ul>
<li>FastifyPluginAsyncTypebox (tipo per il plugin importato da '@fastify/type-provider-typebox')</li>
</ul>
<h2 id="heading-refactoring">Refactoring</h2>
<p>Cosi funziona tutto, ma personalmente per maggiore leggibilità preferisco separare la funzione della rotta e lo schema dalla dichiarazione della rotta stessa.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { Type, FastifyPluginAsyncTypebox } <span class="hljs-keyword">from</span> <span class="hljs-string">'@fastify/type-provider-typebox'</span>
<span class="hljs-keyword">import</span> { FastifyReply, FastifyRequest } <span class="hljs-keyword">from</span> <span class="hljs-string">'fastify'</span>

<span class="hljs-keyword">const</span> example: FastifyPluginAsyncTypebox = <span class="hljs-keyword">async</span> (fastify, opts): <span class="hljs-built_in">Promise</span>&lt;<span class="hljs-built_in">void</span>&gt; =&gt; {

  fastify.get(<span class="hljs-string">'/'</span>, <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">request, reply</span>) </span>{
    <span class="hljs-keyword">return</span> <span class="hljs-string">'this is an example'</span>
  })

  <span class="hljs-keyword">const</span> schemaPostUppercase = {
    body: Type.Object({
      text: Type.String(),
    }),
    response: {
      <span class="hljs-number">200</span>: Type.Object({ 
        textResult: Type.String() 
      }),
    }
  }

  <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">postUppercase</span>(<span class="hljs-params">request: FastifyRequest, reply: FastifyReply</span>) </span>{
    <span class="hljs-keyword">const</span> body = request.body
    <span class="hljs-keyword">const</span> text = body.text
    <span class="hljs-keyword">return</span> { textResult: text.toUpperCase()} 
  }

  fastify.post(<span class="hljs-string">'/uppercase'</span>, { schema: schemaPostUppercase }, postUppercase)

}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> example;
</code></pre>
<p>In questo modo abbiamo separato lo schema nella variabile: schemaPostUppercase e l'handler della rotta nella funzione: postUppercase.</p>
<p>Però ora Typescript non è più in grado di tipizzare il body ed il ritorno della funzione e genera l'errore:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1719326501941/721ef45d-6663-49c1-b093-c668a6284e52.png" alt class="image--center mx-auto" /></p>
<p>E da notare anche che il ritorno non è più tipizzato. Infatti Typescript ci permette di fare questo:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">return</span> { hello: text.toUpperCase()}
</code></pre>
<p>Ritornare la proprietà 'hello' dovrebbe generare un errore di compilazione Typescript, perché non rispetta lo schema che invece definisce che nella risposta ci sia solo la proprietà 'textResult'.</p>
<p>Questo perché la rotta non prende più i tipi dallo schema.</p>
<h2 id="heading-tipizzazione-dellhandler-della-rotta">Tipizzazione dell'handler della rotta</h2>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { Type, FastifyPluginAsyncTypebox, TypeBoxTypeProvider } <span class="hljs-keyword">from</span> <span class="hljs-string">'@fastify/type-provider-typebox'</span>
<span class="hljs-keyword">import</span> { ContextConfigDefault, FastifyBaseLogger, FastifyInstance, FastifyReply, FastifyRequest, FastifySchema, RawReplyDefaultExpression, RawRequestDefaultExpression, RawServerDefault, RouteGenericInterface } <span class="hljs-keyword">from</span> <span class="hljs-string">'fastify'</span>
<span class="hljs-keyword">import</span> { ResolveFastifyReplyReturnType } <span class="hljs-keyword">from</span> <span class="hljs-string">'fastify/types/type-provider'</span>

<span class="hljs-keyword">type</span> FastifyInstanceTypebox = FastifyInstance&lt;
  RawServerDefault,
  RawRequestDefaultExpression&lt;RawServerDefault&gt;,
  RawReplyDefaultExpression,
  FastifyBaseLogger,
  TypeBoxTypeProvider
&gt;

<span class="hljs-keyword">type</span> FastifyRequestTypebox&lt;TSchema <span class="hljs-keyword">extends</span> FastifySchema&gt; = FastifyRequest&lt;
  RouteGenericInterface,
  RawServerDefault,
  RawRequestDefaultExpression&lt;RawServerDefault&gt;,
  TSchema,
  TypeBoxTypeProvider
&gt;

<span class="hljs-keyword">type</span> FastifyReplyTypebox&lt;TSchema <span class="hljs-keyword">extends</span> FastifySchema&gt; = FastifyReply&lt;
  RawServerDefault,
  RawRequestDefaultExpression,
  RawReplyDefaultExpression,
  RouteGenericInterface,
  ContextConfigDefault,
  TSchema,
  TypeBoxTypeProvider
&gt;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">type</span> RouteHandlerTypebox&lt;TSchema <span class="hljs-keyword">extends</span> FastifySchema&gt; = <span class="hljs-function">(<span class="hljs-params">
  <span class="hljs-built_in">this</span>: FastifyInstanceTypebox,
  request: FastifyRequestTypebox&lt;TSchema&gt;,
  reply: FastifyReplyTypebox&lt;TSchema&gt;
</span>) =&gt;</span> ResolveFastifyReplyReturnType&lt;TypeBoxTypeProvider, TSchema, RouteGenericInterface&gt;

<span class="hljs-keyword">const</span> example: FastifyPluginAsyncTypebox = <span class="hljs-keyword">async</span> (fastify, opts): <span class="hljs-built_in">Promise</span>&lt;<span class="hljs-built_in">void</span>&gt; =&gt; {

  fastify.get(<span class="hljs-string">'/'</span>, <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">request, reply</span>) </span>{
    <span class="hljs-keyword">return</span> <span class="hljs-string">'this is an example'</span>
  })

  <span class="hljs-keyword">const</span> schemaPostUppercase = {
    body: Type.Object({
      text: Type.String(),
    }),
    response: {
      <span class="hljs-number">200</span>: Type.Object({ 
        textResult: Type.String() 
      }),
    }
  }

  <span class="hljs-keyword">const</span> postUppercase: RouteHandlerTypebox&lt;<span class="hljs-keyword">typeof</span> schemaPostUppercase&gt; = <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">request, reply</span>) </span>{
    <span class="hljs-keyword">const</span> body = request.body
    <span class="hljs-keyword">const</span> text = body.text
    <span class="hljs-keyword">return</span> { textResult: text.toUpperCase()} 
  }

  fastify.post(<span class="hljs-string">'/uppercase'</span>, { schema: schemaPostUppercase }, postUppercase)

}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> example;
</code></pre>
<p>Purtroppo per poter separare la dichiarazione della funzione 'postUppercase' dalla definizione della rotta bisogna creare a mano il suo tipo.</p>
<p>Per semplicità ho definito tutti i tipi all'interno dello stesso file, ma in un progetto reale generalmente si definiscono i tipi in un file separato ed importati nei file in cui vengono utilizzati.</p>
<p>Vediamo passo dopo passo questo processo:</p>
<ul>
<li>Abbiamo trasformato la funzione postUppercase da così:</li>
</ul>
<pre><code class="lang-typescript"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">postUppercase</span>(<span class="hljs-params">request: FastifyRequest, reply: FastifyReply</span>) </span>{
    <span class="hljs-keyword">const</span> body = request.body
    <span class="hljs-keyword">const</span> text = body.text
    <span class="hljs-keyword">return</span> { textResult: text.toUpperCase()} 
}
</code></pre>
<ul>
<li>A così:</li>
</ul>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> postUppercase: RouteHandlerTypebox&lt;<span class="hljs-keyword">typeof</span> schemaPostUppercase&gt; = <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">request, reply</span>) </span>{
    <span class="hljs-keyword">const</span> body = request.body
    <span class="hljs-keyword">const</span> text = body.text
    <span class="hljs-keyword">return</span> { textResult: text.toUpperCase()} 
}
</code></pre>
<p>postUppercase diventa da una funzione ad una variabile che contiene una funzione anonima, questa sintassi ci permette di definire i tipi per i parametri, il ritorno ed il contesto (this) della funzione con un solo tipo:</p>
<pre><code class="lang-typescript">RouteHandlerTypebox&lt;<span class="hljs-keyword">typeof</span> schemaPostUppercase&gt;
</code></pre>
<p>Vediamo la sua definizione:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">type</span> RouteHandlerTypebox&lt;TSchema <span class="hljs-keyword">extends</span> FastifySchema&gt; = <span class="hljs-function">(<span class="hljs-params">
  <span class="hljs-built_in">this</span>: FastifyInstanceTypebox,
  request: FastifyRequestTypebox&lt;TSchema&gt;,
  reply: FastifyReplyTypebox&lt;TSchema&gt;
</span>) =&gt;</span> ResolveFastifyReplyReturnType&lt;TypeBoxTypeProvider, TSchema, RouteGenericInterface&gt;
</code></pre>
<p>Nel tipo RouteHandlerTypebox, stiamo andando a definire i tipi per:</p>
<ul>
<li><p>this: il contesto della funzione ovvero quando si vuole accedere all'oggetto 'fastify' tramite 'this'.</p>
</li>
<li><p>request: il primo parametro della funzione</p>
</li>
<li><p>reply: il secondo parametro della funzione</p>
</li>
<li><p>ritorno della funzione: ResolveFastifyReplyReturnType</p>
</li>
</ul>
<p>Di fatto manualmente tipizzando con lo schema: request, reply ed il ritorno della funzione, che prendono il tipo generico: TSchema.</p>
<blockquote>
<p>Manuel Salinardi</p>
</blockquote>
]]></content:encoded></item></channel></rss>