
<?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[Juho Viitasalo's dev blog]]></title><description><![CDATA[Juho Viitasalo's dev blog]]></description><link>https://jiv-e.com</link><generator>RSS for Node</generator><lastBuildDate>Thu, 09 Jul 2026 09:22:09 GMT</lastBuildDate><atom:link href="https://jiv-e.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[How to run Matomo in Docker locally?]]></title><description><![CDATA[Do you want to run Matomo on your local machine for development and testing purposes? Then this article is for you! You will learn how to set up Matomo using docker-compose, how to configure the initial database and how to reset the environment to th...]]></description><link>https://jiv-e.com/how-to-run-matomo-in-docker-locally</link><guid isPermaLink="true">https://jiv-e.com/how-to-run-matomo-in-docker-locally</guid><category><![CDATA[matomo]]></category><category><![CDATA[Docker]]></category><category><![CDATA[Docker compose]]></category><category><![CDATA[Developer]]></category><category><![CDATA[Testing]]></category><dc:creator><![CDATA[Juho Viitasalo]]></dc:creator><pubDate>Fri, 24 Mar 2023 23:46:07 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1679949138306/573470cd-4a28-4046-b35b-103ef8b0b6a2.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Do you want to run Matomo on your local machine for development and testing purposes? Then this article is for you! You will learn how to set up Matomo using docker-compose, how to configure the initial database and how to reset the environment to the original configuration. Resetting is especially handy when you want to run a clean install of Matomo for automated tests.</p>
<p>Let's hop right in!</p>
<p>Based on <a target="_blank" href="https://github.com/matomo-org/docker/tree/a9ce756ce0ca0957ed7a8987e562f37e6b5e4a45/.examples/nginx">https://github.com/matomo-org/docker/tree/a9ce756ce0ca0957ed7a8987e562f37e6b5e4a45/.examples/nginx</a></p>
<h1 id="heading-initial-folder-content">Initial folder content</h1>
<p>Here are the initial files in a folder. Copy and paste the file contents below.</p>
<pre><code class="lang-plaintext">/matomo
|__db.env
|__docker-compose.yml
|__matomo.conf
</code></pre>
<p><strong>docker-compose.yml</strong></p>
<pre><code class="lang-yaml"><span class="hljs-attr">version:</span> <span class="hljs-string">"3"</span>

<span class="hljs-attr">services:</span>
  <span class="hljs-attr">db:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">mariadb</span>
    <span class="hljs-attr">command:</span> <span class="hljs-string">--max-allowed-packet=64MB</span>
    <span class="hljs-attr">restart:</span> <span class="hljs-string">always</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">db:/var/lib/mysql</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">MYSQL_ROOT_PASSWORD=</span>
    <span class="hljs-attr">env_file:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">./db.env</span>

  <span class="hljs-attr">app:</span>
    <span class="hljs-attr">image:</span>
      <span class="hljs-comment"># Select your Matomo container from: </span>
      <span class="hljs-comment"># https://hub.docker.com/_/matomo</span>
      <span class="hljs-string">matomo:4.13.0-fpm-alpine</span>
    <span class="hljs-attr">restart:</span> <span class="hljs-string">always</span>
    <span class="hljs-attr">links:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">db</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-comment">#- ./config.ini.php:/var/www/html/config/config.ini.php</span>
      <span class="hljs-comment">#- ./logs:/var/www/html/logs</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">matomo:/var/www/html</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">MATOMO_DATABASE_HOST=db</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">PHP_MEMORY_LIMIT=2048M</span>
    <span class="hljs-attr">env_file:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">./db.env</span>

  <span class="hljs-attr">web:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">nginx:alpine</span>
    <span class="hljs-attr">restart:</span> <span class="hljs-string">always</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">matomo:/var/www/html:ro</span>
      <span class="hljs-comment"># see https://github.com/matomo-org/matomo-nginx</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">./matomo.conf:/etc/nginx/conf.d/default.conf:ro</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-number">8111</span><span class="hljs-string">:80</span>

<span class="hljs-attr">volumes:</span>
  <span class="hljs-attr">db:</span>
  <span class="hljs-attr">matomo:</span>
</code></pre>
<p><strong>db.env</strong></p>
<pre><code class="lang-bash">MARIADB_ROOT_PASSWORD=root
MYSQL_PASSWORD=matomo
MYSQL_DATABASE=matomo
MYSQL_USER=matomo
MATOMO_DATABASE_ADAPTER=mysql
MATOMO_DATABASE_TABLES_PREFIX=matomo_
MATOMO_DATABASE_USERNAME=matomo
MATOMO_DATABASE_PASSWORD=matomo
MATOMO_DATABASE_DBNAME=matomo
MARIADB_AUTO_UPGRADE=1
MARIADB_INITDB_SKIP_TZINFO=1
</code></pre>
<p><strong>matomo.conf</strong></p>
<pre><code class="lang-nginx"><span class="hljs-attribute">upstream</span> php-handler {
    <span class="hljs-attribute">server</span> app:<span class="hljs-number">9000</span>;
}

<span class="hljs-section">server</span> {
    <span class="hljs-attribute">listen</span> <span class="hljs-number">80</span>;

    <span class="hljs-attribute">add_header</span> Referrer-Policy origin; <span class="hljs-comment"># make sure outgoing links don't show the URL to the Matomo instance</span>
    <span class="hljs-attribute">root</span> /var/www/html; <span class="hljs-comment"># replace with path to your matomo instance</span>
    <span class="hljs-attribute">index</span> index.php;
    <span class="hljs-attribute">try_files</span> <span class="hljs-variable">$uri</span> <span class="hljs-variable">$uri</span>/ =<span class="hljs-number">404</span>;

    <span class="hljs-comment">## only allow accessing the following php files</span>
    <span class="hljs-attribute">location</span> <span class="hljs-regexp">~ ^/(index|matomo|piwik|js/index|plugins/HeatmapSessionRecording/configs).php</span> {
        <span class="hljs-comment"># regex to split $uri to $fastcgi_script_name and $fastcgi_path</span>
        <span class="hljs-attribute">fastcgi_split_path_info</span><span class="hljs-regexp"> ^(.+\.php)(/.+)$</span>;

        <span class="hljs-comment"># Check that the PHP script exists before passing it</span>
        <span class="hljs-attribute">try_files</span> <span class="hljs-variable">$fastcgi_script_name</span> =<span class="hljs-number">404</span>;

        <span class="hljs-attribute">include</span> fastcgi_params;
        <span class="hljs-attribute">fastcgi_param</span> SCRIPT_FILENAME <span class="hljs-variable">$document_root</span><span class="hljs-variable">$fastcgi_script_name</span>;
        <span class="hljs-attribute">fastcgi_param</span> PATH_INFO <span class="hljs-variable">$fastcgi_path_info</span>;
        <span class="hljs-attribute">fastcgi_param</span> HTTP_PROXY <span class="hljs-string">""</span>; <span class="hljs-comment"># prohibit httpoxy: https://httpoxy.org/</span>
        <span class="hljs-attribute">fastcgi_pass</span> php-handler;
    }

    <span class="hljs-comment">## deny access to all other .php files</span>
    <span class="hljs-attribute">location</span> <span class="hljs-regexp">~* ^.+\.php$</span> {
        <span class="hljs-attribute">deny</span> all;
        <span class="hljs-attribute">return</span> <span class="hljs-number">403</span>;
    }

    <span class="hljs-comment">## disable all access to the following directories</span>
    <span class="hljs-attribute">location</span> <span class="hljs-regexp">~ /(config|tmp|core|lang)</span> {
        <span class="hljs-attribute">deny</span> all;
        <span class="hljs-attribute">return</span> <span class="hljs-number">403</span>; <span class="hljs-comment"># replace with 404 to not show these directories exist</span>
    }
    <span class="hljs-attribute">location</span> <span class="hljs-regexp">~ /\.ht</span> {
        <span class="hljs-attribute">deny</span> all;
        <span class="hljs-attribute">return</span> <span class="hljs-number">403</span>;
    }

    <span class="hljs-attribute">location</span> <span class="hljs-regexp">~ js/container_.*_preview\.js$</span> {
        <span class="hljs-attribute">expires</span> <span class="hljs-literal">off</span>;
        <span class="hljs-attribute">add_header</span> Cache-Control <span class="hljs-string">'private, no-cache, no-store'</span>;
    }

    <span class="hljs-attribute">location</span> <span class="hljs-regexp">~ \.(gif|ico|jpg|png|svg|js|css|htm|html|mp3|mp4|wav|ogg|avi|ttf|eot|woff|woff2|json)$</span> {
        <span class="hljs-attribute">allow</span> all;
        <span class="hljs-comment">## Cache images,CSS,JS and webfonts for an hour</span>
        <span class="hljs-comment">## Increasing the duration may improve the load-time, but may cause old files to show after an Matomo upgrade</span>
        <span class="hljs-attribute">expires</span> <span class="hljs-number">1h</span>;
        <span class="hljs-attribute">add_header</span> Pragma public;
        <span class="hljs-attribute">add_header</span> Cache-Control <span class="hljs-string">"public"</span>;
    }

    <span class="hljs-attribute">location</span> <span class="hljs-regexp">~ /(libs|vendor|plugins|misc/user)</span> {
        <span class="hljs-attribute">deny</span> all;
        <span class="hljs-attribute">return</span> <span class="hljs-number">403</span>;
    }

    <span class="hljs-comment">## properly display textfiles in root directory</span>
    <span class="hljs-attribute">location</span> ~/(.*\.md|LEGALNOTICE|LICENSE) {
        <span class="hljs-attribute">default_type</span> text/plain;
    }
}

<span class="hljs-comment"># vim: filetype=nginx</span>
</code></pre>
<h1 id="heading-prepare-the-database">Prepare the database</h1>
<p>Ok, we are ready to start the Docker containers. Use --detach switch to run the containers in the background.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Run the command:</span>
docker-compose up --detach
</code></pre>
<p>After running the above command visit <code>http://localhost:8111</code> and you should see the following Matomo install wizard.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1679697452931/29b79f29-7882-4759-8f29-6d029cc54b48.png" alt class="image--center mx-auto" /></p>
<p>Go through the installation process step by step. After that, depending on your needs, you may want to add some configuration for automated tests or basic development setup.</p>
<h1 id="heading-mount-the-config-file">Mount the config file</h1>
<p>The next command will mount the config file created during installation to your host computer for editing.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Run the command:</span>
docker cp matomo_app_1:/var/www/html/config/config.ini.php .
</code></pre>
<p>Now your folder should look like this:</p>
<pre><code class="lang-plaintext">/matomo
|__config.ini.php &lt;-- This is added.
|__db.env
|__docker-compose.yml
|__matomo.conf
</code></pre>
<p>Edit the <em>config.ini.php</em> file.</p>
<pre><code class="lang-bash">...
[General]
salt = <span class="hljs-string">"f5c8452a6d1491ac3b3ac475bb18e8d0"</span> &lt;-- This may differ.
trusted_hosts[] = <span class="hljs-string">"localhost:8111"</span> &lt;-- Add the port.
...
</code></pre>
<p>Edit the <em>docker-compose.yml</em> file.</p>
<pre><code class="lang-bash">...
  app:
    ...
    volumes:
      <span class="hljs-comment"># Uncomment the below line like this.</span>
      - ./config.ini.php:/var/www/html/config/config.ini.php
      <span class="hljs-comment">#- ./logs:/var/www/html/logs</span>
      - matomo:/var/www/html
    ...
</code></pre>
<h1 id="heading-export-the-database-volume">Export the database volume</h1>
<p>Next, we export the database volume by running the command below.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Run the command:</span>
docker run --rm --volume matomo_db:/dbdata --volume $(<span class="hljs-built_in">pwd</span>):/backup ubuntu tar cvzf /backup/initial-db.gz /dbdata
</code></pre>
<p>Now your folder should look like this.</p>
<pre><code class="lang-plaintext">/matomo
|__config.ini.php
|__db.env
|__docker-compose.yml
|__initial-db.gz &lt;-- This is added.
|__matomo.conf
</code></pre>
<h1 id="heading-take-it-down">Take it down...</h1>
<p>Now we remove the containers and volumes to clean up. Run the below command.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Run the command.</span>
docker-compose down -v
</code></pre>
<p>Now the containers and volumes are gone. Everything you need is ready as files in the folder. You can commit the files to your version control and it's easy to share the same setup with others</p>
<h1 id="heading-and-up-again">...and up again</h1>
<p>Let's try to start the containers with the initial database from the files.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Import the database:</span>
docker run --rm --volume matomo_db:/dbdata --volume $(<span class="hljs-built_in">pwd</span>):/backup ubuntu tar xvzf /backup/initial-db.gz -C /dbdata --strip 1
<span class="hljs-comment"># Start Matomo:</span>
docker-compose up --detach
</code></pre>
<p>Now you should have the Matomo running. Go and check the URL <code>http://localhost:8111</code>.</p>
<h1 id="heading-extra-tip-for-nodejs-users">Extra tip for Node.js users</h1>
<p>If you are using Node.js, it might be handy to put something like this in your package.json file.</p>
<pre><code class="lang-json">...
 <span class="hljs-string">"scripts"</span>: {
    <span class="hljs-attr">"matomo:start"</span>: <span class="hljs-string">"cd matomo; docker-compose up --detach"</span>,
    <span class="hljs-attr">"matomo:stop"</span>: <span class="hljs-string">"cd matomo; docker-compose down"</span>,
    <span class="hljs-attr">"matomo:reset"</span>: <span class="hljs-string">"cd matomo; npm run matomo:stop; docker run --rm --volume matomo_db:/dbdata --volume $(pwd):/backup ubuntu tar xvzf /backup/initial-db.gz -C /dbdata --strip 1; npm run matomo:start"</span>,
    <span class="hljs-attr">"matomo:destroy"</span>: <span class="hljs-string">"cd matomo; docker-compose down -v"</span>
  },
...
</code></pre>
<p>Now you can simply run:</p>
<pre><code class="lang-json">npm run matomo:start
# OR
npm run matomo:stop
# OR
npm run matomo:reset
# OR
npm run matomo:destroy
</code></pre>
<h1 id="heading-afterwords">Afterwords</h1>
<p>Thanks for reading! You are welcome to drop a comment if you found this useful or if you have some ideas to share with the community.</p>
]]></content:encoded></item><item><title><![CDATA[How to add validation to a custom API in Strapi?]]></title><description><![CDATA[Vivek Agarwal wrote a great tutorial for adding custom API endpoints in Strapi. It didn't include any tips for validating the request input. In this tutorial, I will show how to create a pleasant user-friendly API validation experience in Strapi 4.
S...]]></description><link>https://jiv-e.com/how-to-add-validation-to-a-custom-api-in-strapi</link><guid isPermaLink="true">https://jiv-e.com/how-to-add-validation-to-a-custom-api-in-strapi</guid><category><![CDATA[Strapi]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[KoaJS]]></category><category><![CDATA[Validation]]></category><dc:creator><![CDATA[Juho Viitasalo]]></dc:creator><pubDate>Mon, 09 Jan 2023 22:08:29 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1673301922903/434b9aa2-958e-433f-bd5c-da7081e518d2.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Vivek Agarwal wrote a great <a target="_blank" href="https://strapi.io/blog/how-to-create-a-custom-api-endpoint-in-strapi">tutorial for adding custom API endpoints in Strapi</a>. It didn't include any tips for validating the request input. In this tutorial, I will show how to create a pleasant user-friendly API validation experience in Strapi 4.</p>
<p>Strapi is using <a target="_blank" href="https://koajs.com">Koa</a>. An easy way to add validation for custom API endpoints is by using Koa compatible validation library. I've had a good experience with <a target="_blank" href="https://www.npmjs.com/package/node-input-validator">node-input-validator</a>. It has a very nice validation syntax and good flexibility for defining new validation rules.</p>
<h2 id="heading-this-tutorial-is-tested-with"><strong>This tutorial is tested with</strong></h2>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Framework / Library</td><td>Version</td></tr>
</thead>
<tbody>
<tr>
<td>Strapi</td><td>4.5.5</td></tr>
<tr>
<td>node-input-validator</td><td>4.5.1</td></tr>
</tbody>
</table>
</div><h2 id="heading-prerequisites">Prerequisites</h2>
<ol>
<li><p><a target="_blank" href="https://docs.strapi.io/developer-docs/latest/setup-deployment-guides/installation.html">Install Strapi</a> with Typescript option.</p>
</li>
<li><p><code>$ npm i node-input-validator --save</code></p>
</li>
</ol>
<h2 id="heading-steps">Steps</h2>
<h3 id="heading-1-create-a-custom-api-endpoint">1. Create a custom API endpoint</h3>
<pre><code class="lang-plaintext">$ npx strapi generate
? Strapi Generators api - Generate a basic API
? API name check-number
? Is this API for a plugin? No
✔  ++ /api/check-number/routes/check-number.ts
✔  ++ /api/check-number/controllers/check-number.ts
✔  ++ /api/check-number/services/check-number.ts
</code></pre>
<h3 id="heading-2-create-a-route-middleware">2. Create a route middleware</h3>
<pre><code class="lang-plaintext">$ npx strapi generate      
? Strapi Generators middleware - Generate a middleware for an API
? Middleware name validate
? Where do you want to add this middleware? Add middleware to an existing API
? Which API is this for? check-number
✔  ++ /api/check-number/middlewares/validate.ts
</code></pre>
<h3 id="heading-3-implement-validation-middleware">3. Implement validation middleware</h3>
<pre><code class="lang-typescript"><span class="hljs-comment">// File: /api/check-number/middlewares/validate.ts.</span>
<span class="hljs-keyword">import</span> niv <span class="hljs-keyword">from</span> <span class="hljs-string">"node-input-validator"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> (config) =&gt; {
  <span class="hljs-keyword">return</span> niv.koa();
};
</code></pre>
<h3 id="heading-4-implement-a-route">4. Implement a route</h3>
<pre><code class="lang-typescript"><span class="hljs-comment">// File: /api/check-number/routes/check-number.ts</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  routes: [
    {
     method: <span class="hljs-string">'POST'</span>,
     path: <span class="hljs-string">'/is-it-big'</span>,
     handler: <span class="hljs-string">'check-number.checkBig'</span>,
     config: {
       policies: [],
       middlewares: [<span class="hljs-string">"api::check-number.validate"</span>],
     },
    },
  ],
};
</code></pre>
<h3 id="heading-5-implement-a-controller">5. Implement a controller</h3>
<pre><code class="lang-typescript"><span class="hljs-comment">// File: /api/check-number/controllers/check-number.ts</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  checkBig: <span class="hljs-keyword">async</span> (ctx, next) =&gt; {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">await</span> ctx.validate({
        <span class="hljs-built_in">number</span>: <span class="hljs-string">"required|integer|min:0"</span>,
      });

      <span class="hljs-keyword">const</span> checkNumberService = strapi.service(
        <span class="hljs-string">"api::check-number.check-number"</span>
      );

      ctx.body = checkNumberService.confirmBigNumber(
        <span class="hljs-built_in">Number</span>(ctx.request.body.number)
      );
    } <span class="hljs-keyword">catch</span> (err) {
      ctx.body = err;
    }
  },
};
</code></pre>
<h3 id="heading-6-implement-a-service">6. Implement a service</h3>
<pre><code class="lang-typescript"><span class="hljs-comment">// File: /api/check-number/services/check-number.ts</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  confirmBigNumber: (<span class="hljs-built_in">number</span>: <span class="hljs-built_in">number</span>): <span class="hljs-function"><span class="hljs-params">string</span> =&gt;</span> {
    <span class="hljs-keyword">if</span> (<span class="hljs-built_in">number</span> &gt;= <span class="hljs-number">1000000</span>) {
      <span class="hljs-keyword">return</span> <span class="hljs-string">`<span class="hljs-subst">${<span class="hljs-built_in">number</span>}</span> is big! Yay! 👍`</span>;
    }
    <span class="hljs-keyword">if</span> (<span class="hljs-built_in">number</span> &lt; <span class="hljs-number">1000000</span>) {
      <span class="hljs-keyword">return</span> <span class="hljs-string">`<span class="hljs-subst">${<span class="hljs-built_in">number</span>}</span> is too small! Boo! 👎`</span>;
    }
  },
};
</code></pre>
<h3 id="heading-7-allow-access-to-the-api-endpoint">7. Allow access to the API endpoint</h3>
<p>Log in to the Strapi admin interface and allow the <em>checkBig</em> endpoint. See the screenshot below for reference.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1673301196677/c4b915a2-5fe4-4c15-ae7e-18c619442875.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-8-test-the-api-endpoint">8. Test the API endpoint</h3>
<p>Ensure that Strapi is running on http://localhost:1337. Then try these curl commands.</p>
<pre><code class="lang-plaintext">$ curl -X POST -F 'number=word' localhost:1337/api/is-it-big
{"message":"Unprocessable Entity","body":{"message":"The given data is invalid.","errors":{"number":{"message":"The number must be an integer.","rule":"integer"}}}}

$ curl -X POST -F 'number=-1' localhost:1337/api/is-it-big
{"message":"Unprocessable Entity","body":{"message":"The given data is invalid.","errors":{"number":{"message":"The number must be at least 0.","rule":"min"}}}}

$ curl -X POST -F 'number=10000' localhost:1337/api/is-it-big 
10000 is small! 👎

$ curl -X POST -F 'number=11234152' localhost:1337/api/is-it-big 
11234152 is big! Yay! 👍
</code></pre>
<h2 id="heading-how-about-get-params">How about GET params?</h2>
<p>Here we go.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// File: /api/check-number/routes/check-number.ts</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  routes: [
    ...
    {
      method: <span class="hljs-string">"GET"</span>,
      path: <span class="hljs-string">"/credit-card/:input"</span>,
      handler: <span class="hljs-string">"check-number.checkCreditCard"</span>,
      config: {
        policies: [],
        middlewares: [<span class="hljs-string">"api::check-number.validate"</span>],
      },
    },
  ],
};

<span class="hljs-comment">// File: /api/check-number/controllers/check-number.ts</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  ...
  checkCreditCard: <span class="hljs-keyword">async</span> (ctx, next) =&gt; {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">await</span> ctx.validate(
        { input: <span class="hljs-string">"required|creditCard"</span> },
        ctx.params
      );

      ctx.body =
        <span class="hljs-string">"Are you crazy!? You just sent a credit card number inside a GET parameter."</span>;
    } <span class="hljs-keyword">catch</span> (err) {
      ctx.body = err;
    }
  },
};
</code></pre>
<h2 id="heading-postfix">Postfix</h2>
<p>Let me know if you found this useful!</p>
<p>Please, comment also if you have any critical notes or some ideas on how to do validation even better! It's always valuable to share ideas and insights so we can all be better developers together. Thanks!</p>
]]></content:encoded></item><item><title><![CDATA[Dismantling the Full-Stack Buzz]]></title><description><![CDATA[Full-stack terminology is buzzy as an angry bee locked in a tin can trying to attack you with a 10,000-volt taser. I just read an article telling me what a full-stack designer is. It seems that both words, "full" and "stack", have lost their meaning;...]]></description><link>https://jiv-e.com/dismantling-the-full-stack-buzz</link><guid isPermaLink="true">https://jiv-e.com/dismantling-the-full-stack-buzz</guid><category><![CDATA[Developer]]></category><category><![CDATA[jobs]]></category><category><![CDATA[history]]></category><category><![CDATA[hiring]]></category><category><![CDATA[full stack]]></category><dc:creator><![CDATA[Juho Viitasalo]]></dc:creator><pubDate>Thu, 10 Feb 2022 11:52:31 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1644528241738/e9UTgTEtk.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Full-stack terminology is buzzy as an angry bee locked in a tin can trying to attack you with a 10,000-volt taser. I just read an article telling me <a target="_blank" href="https://www.mockplus.com/blog/post/full-stack-designer/?r=grace">what a full-stack designer is</a>. It seems that both words, "full" and "stack", have lost their meaning; they are mushed together just to make a cool sound signifying nothing but some general nonsense.</p>
<p>Compared to other buzzwords like DevOps, it’s hard to find any decent definition for Full-Stack Developer, even in professional discourse. It’s time to put an end to this! After reading the article you will have a deeper understanding of how the title "full-stack developer" has been used and how it is used today. I’ll also give my recommendations on how to turn down the buzz as much as possible.</p>
<p><em>Originally published Oct 12, 2018, on Hackernoon.com</em></p>
<h2 id="heading-background-story">Background story</h2>
<p>I have occasionally used the term "full-stack developer" to describe myself. I first started to use it when I was asked if I was more of a back-end or front-end guy. I couldn’t make the distinction. After hearing about the full-stack title I started to use it. For me, it just meant doing both back- and front-end without specializing in either.</p>
<p>I didn’t give it too much thought until later when I noticed some controversy around the title. Full-stack prefixes started to feel dirty. It was cool to say the F-word while marketing myself, but with some fellow developers, it caused awkward silence and even some requests to wash my mouth. I was confused and decided to explore the true meaning of the title.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1644447317549/bTOf9barw.jpeg" alt="Oscar Wilde quote: I am so clever that sometimes I don't understand a single word of what I am saying." /></p>
<p>My journey began by searching for a widely accepted definition of the term by reading blogs. I read many of them and ended up even more confused.</p>
<p>I found long lists of different skill sets a full-stack developer must have. On the other hand, I found very loose definitions, some of which could give almost any web developer a full-stack status. Others were thinking a full-stack developer is someone more experienced than a senior developer while others were talking about junior to mid-level full-stack developers.</p>
<p><a target="_blank" href="https://www.pluralsight.com/blog/software-development/full-stack-developer-solution">Some</a> <a target="_blank" href="https://techcrunch.com/2014/11/08/the-rise-and-fall-of-the-full-stack-developer/">people</a> said that it’s not even <a target="_blank" href="https://medium.com/@boboshady/you-are-not-a-full-stack-developer-b66e227d3ebb">possible</a> to be a full-stack developer, but employers didn’t seem to listen. I glanced through job listings and found a LOT of open positions for full-stack developers.</p>
<p>If employers were hiring full-stack developers, they had to know what they were doing, right? I started digging into the job offers for an answer, but there were too many different requirement levels lumped under the same term. I couldn’t get a clear picture of what the common factors were.</p>
<p>At this point, I felt I had to go deeper. When and why did all this begin? My journey took a turn, into history. <em>← Lower your voice for the last words to get in the mood.</em></p>
<h2 id="heading-research">Research</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1644448465831/ydVxg69Jx.jpeg" alt="Dermot Mulroney quote: What people actually refer to as research nowadays is really just Googling." /></p>
<p><em>While doing the research, I realized how vital</em> <a target="_blank" href="https://archive.org/"><em>archive.org</em></a> <em>is for preserving historical documents. Consider donating!</em></p>
<p>I tracked the history of full-stack terminology back to the following blog post by <a target="_blank" href="https://twitter.com/r38y">Randy Schmidt</a>, written in 2008.</p>
<p><strong>Full Stack Web Developers</strong><br /><a target="_blank" href="https://web.archive.org/web/20101204221657/http://forge38.com/blog/2008/06/full-stack-web-developers/"><em>Forge38.com, June 03, 2008, Randy Schmidt - web.archive.org</em></a></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1644493016416/z-EQSrRwmW.png" alt="Screenshot of an old web page article" /></p>
<p>It’s a mythical story about three heroes, called full stack developers, who could do “design, markup, styling, behavior, and programming” and could single-handedly spin up glorious websites and applications. Amazingly these people and applications really did exist. So seemingly it wasn’t impossible to be an actual full-stack developer, at least by the original definition.</p>
<p>So, what happened? Why are some people so upset about using the term? I started to understand this, as I went forward. A later influential piece from 2010 is a post from <a target="_blank" href="https://www.facebook.com/cmb">Carlos Bueno</a> who was working at Facebook at the time.</p>
<p><strong>The Full Stack, Part I</strong><br /><em>engineering.fb.com, December 02, 2010, Carlos Bueno</em></p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://engineering.fb.com/2010/12/02/core-data/the-full-stack-part-i/">https://engineering.fb.com/2010/12/02/core-data/the-full-stack-part-i/</a></div>
<p> </p>
<p>He refers to Schmidt’s post and offers this definition.</p>
<blockquote>
<p>A “<a target="_blank" href="https://web.archive.org/web/20101204221657/http://forge38.com/blog/2008/06/full-stack-web-developers/">full-stack programmer</a>” is a generalist, someone who can create a non-trivial application by themselves.</p>
</blockquote>
<p>He continues.</p>
<blockquote>
<p>People who develop broad skills also tend to develop a good mental model of how different layers of a system behave. This turns out to be especially valuable for performance &amp; optimization work. No one can know everything about everything, but you should be able to visualize what happens up and down the stack as an application does its thing.</p>
</blockquote>
<p>After this, he gives an example of a performance problem where in-depth knowledge of a technology stack, down to the hardware level, helps to solve the problem better. The post is apparently about large and complex applications not built by one individual. It seems that Bueno’s post has already derailed the discussion from the original definition.</p>
<p>Later posts on the subject use the term in the context of large teams, complex stacks, and millions of users. The original meaning was lost. We see this in the next post by Laurence Gellert from 2012.</p>
<p><strong>What is a Full Stack developer?</strong><br /><em>laurencegellert.com, August 01, 2012, Laurence Gellert</em></p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="http://www.laurencegellert.com/2012/08/what-is-a-full-stack-developer/">http://www.laurencegellert.com/2012/08/what-is-a-full-stack-developer/</a></div>
<p> </p>
<blockquote>
<p>To me, a Full Stack Developer is someone with familiarity in each layer, if not mastery in many and a genuine interest in all software technology.</p>
<p>Good developers who are familiar with the entire stack know how to make life easier for those around them.</p>
</blockquote>
<p>Here’s another influential piece from 2014 written by Mike Loukides.</p>
<p><strong>Full-stack developers</strong><br /><em>radar.oreilly.com, April 04, 2014, Mike Loukides</em></p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="http://radar.oreilly.com/2014/04/full-stack-developers.html">http://radar.oreilly.com/2014/04/full-stack-developers.html</a></div>
<p> </p>
<blockquote>
<p>Sometimes, the mythical “full-stack developer” sounds like: “we got rid of the silos, and now we want one person to replace them all.” That’s nonsense. What’s really needed isn’t someone who can replace all the specializations that the silos represented, but someone who can work across those specializations, someone who can work productively with people on other parts of the team. Full-stack development is about exposing yourself to a broad range of ideas.</p>
</blockquote>
<p>In the above post, the idea of full-stack developers as “silo breakers” is amplified. Instead of building something by themselves, full-stack developers are the ones solving the specialization problems of large teams and existing products.</p>
<p>And now we land in the year 2017. A recent guide for becoming a full-stack developer tells us that the title has become the most popular developer occupation today! It also offers a following short definition.</p>
<p><strong>A Guide to Becoming a Full-Stack Developer in 2017</strong><br /><em>Medium.com, April 01, 2017, Daniel Borowski, Coderbyte</em></p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://medium.com/coderbyte/a-guide-to-becoming-a-full-stack-developer-in-2017-5c3c08a1600c">https://medium.com/coderbyte/a-guide-to-becoming-a-full-stack-developer-in-2017-5c3c08a1600c</a></div>
<p> </p>
<blockquote>
<p>A Full-Stack Web Developer is someone who is <a target="_blank" href="https://www.sitepoint.com/full-stack-developer/">able to work</a> on both the front-end and back-end portions of an application.</p>
</blockquote>
<p>This definition seems quite relaxed and easy to fulfil. It reminds me of my original usage of the term. But if you read forward, Borowski doesn’t give too much slack for poor full-stack dev candidates. There’s a lot to learn, as stressed by almost all recent writing on the subject. It’s not just knowing some PHP, MySQL, and HTML as it used to be.</p>
<p>A lot more could probably be said, and the discussion has more points, but I believe this chapter summarizes the history of the term nicely.</p>
<h2 id="heading-analysis">Analysis</h2>
<p>For some people, Borowski’s short definition seems to work as a minimum requirement to start the full-stack buzz. A full-stack developer is just someone “not specialized”. Others would like to add team skills, and interest to learn new technologies, to the mix, but it still sounds quite fluffy.</p>
<p>On the other end of the spectrum, employers and bloggers are chasing for magical full-stack unicorns who know everything from web usability and React to server security and AWS with <a target="_blank" href="https://www.theregister.co.uk/2016/01/13/docker_job_figures/">ten years of Docker experience</a> in corporate team environments. Don’t forget the CSS!</p>
<p>If you compare the above to the original definition, you can notice a significant difference. The original definition is based on what some people are able to <strong><em>do</em></strong>, namely build functioning websites and applications. Later attempts to define the term revolve around <strong><em>knowing</em></strong> technologies or <strong><em>having</em></strong> team skills, can-do-attitude, or a genuine interest towards the whole stack. It seems difficult to find an agreement on that basis.</p>
<p>When I tried to formulate a definition based on what a full-stack developer does, it became clear that two separate concepts can be teased out.</p>
<p><strong>Full-stack developer (I)</strong> <em>builds and maintains whole websites, applications or services without assistance.</em></p>
<p><strong>Full-stack developer (II)</strong> <em>implements features and solves problems across a technology stack as part of a team.</em></p>
<p>The first is the original definition by Schmidt. The second is spelt out in the articles that followed. You just have to read between the lines.</p>
<p>Full-stack developer…<br /><em>…should be able to visualize what happens up and down the stack…</em>  - <a target="_blank" href="http://carlos.bueno.org/2010/11/full-stack.html">Bueno</a><br /><em>…knows how to make life easier for those around them.</em>  - <a target="_blank" href="http://www.laurencegellert.com/2012/08/what-is-a-full-stack-developer/">Gellert</a><br /><em>…is someone who can work across those specializations, someone who can work productively with people on other parts of the team.</em> -  <a target="_blank" href="http://radar.oreilly.com/2014/04/full-stack-developers.html">Loukides</a></p>
<h2 id="heading-discussion">Discussion</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1644493430900/7dImy5mFq.jpeg" alt="Eminem quote: If you're the parent, be a parent. You know what I mean? I'm a parent. I have daughters." /></p>
<p>The repeating question in the discussions has been when can someone claim to <strong>be</strong> a full-stack developer. “Am I really a full-stack developer?”, “What does a real full-stack developer have to know in the upcoming year?”, “Is it even possible to be a full-stack developer?”. If we adopt the way of defining the term I showed above, all these questions get a simple answer. If you are <strong>doing</strong> the job of a full-stack developer, you are one. It’s a separate issue of how skilled or qualified you are, or what is your current stack. Your work is not primarily about what skills you have, but what responsibilities you have.</p>
<p>Because we have two different meanings for the same term, it would be helpful to use different job titles for each definition like <strong><em>Full-Stack Builder</em></strong> for definition <strong>(I)</strong> and <strong><em>Cross-Stack Developer</em></strong> for definition <strong>(II)</strong>. If you do a web search, you can see these terms already do exist, and that their usage corresponds with the above definitions nicely.</p>
<p>Definition <strong>(II)</strong>, Cross-Stack Developer, could be an excellent replacement for Full-Stack Developer in enterprise and startup contexts. As a stack gets bigger and more complex, one person can’t handle it “fully”, so using the word “cross” should be more fitting.</p>
<p>If we step outside the world of large development teams, there is a huge amount of smaller businesses, organizations and nimble startups that will benefit from one person doing it all. In this context, it’s not so much about technology, but who gets the business problem solved as efficiently as possible. Definition <strong>(I)</strong> and term like Full-Stack Builder, could be used in these situations.</p>
<h2 id="heading-recommendations">Recommendations</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1644490962828/sqnkNYCWQ.jpeg" alt="Bob Marley quote: You have to be someone." /></p>
<p>To be conceptually clear, and faithful to the original meaning, we should use Full-Stack Developer in the sense of the definition <strong>(I)</strong>, Full-Stack Builder. Unfortunately, we can’t fix the situation anymore. The new meaning, <strong>(II)</strong> Cross-Stack Developer, is too widely spread. So if you are, or you’re looking for, an old-school full-stacker, for legacy reasons, you could use the title Full-Stack Builder instead.</p>
<p>In the context of larger teams, we could drop Full-Stack Developer in favour of Cross-Stack Developer as it usually describes the job better.</p>
<p>This way we could at least raise the question of what exactly we are talking about and tune down the buzz a little bit.</p>
<p>When using the title full-stack developer, it could be helpful to clarify what we mean by it. The current usage is more on the side of the cross-stack developer, so without any additional clarification, I think that meaning should be assumed.</p>
<h2 id="heading-endnote">Endnote</h2>
<p>Thanks for following my journey! Maybe you don’t agree with everything I wrote, but I’m sure we can agree on one thing: Nobody listened to Mike.</p>
<blockquote>
<p>I sincerely hope that “full stack” doesn’t appear in job titles anywhere.<br />-- Mike Loukides, 2014</p>
</blockquote>
<p>I welcome any discussion, so please comment!</p>
]]></content:encoded></item></channel></rss>