<?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[API surfer]]></title><description><![CDATA[Web dev surfing on APIs]]></description><link>https://apisurfer.com</link><generator>RSS for Node</generator><lastBuildDate>Sat, 18 Apr 2026 20:20:57 GMT</lastBuildDate><atom:link href="https://apisurfer.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[My first thoughts on Next.js 13 and  the new app router]]></title><description><![CDATA[There are two good articles that touch on some of the concerns and annoyances I also share, but I'll try to cover some of my views about it.
I had a chance to use Next.js 13 for a few weeks and noticed that some things make my job a lot simpler, whil...]]></description><link>https://apisurfer.com/my-first-thoughts-on-nextjs-13-and-the-new-app-router</link><guid isPermaLink="true">https://apisurfer.com/my-first-thoughts-on-nextjs-13-and-the-new-app-router</guid><category><![CDATA[Next.js]]></category><category><![CDATA[router]]></category><category><![CDATA[thoughts]]></category><dc:creator><![CDATA[Luka Vidakovic]]></dc:creator><pubDate>Sat, 13 Jan 2024 08:23:27 GMT</pubDate><content:encoded><![CDATA[<p>There are two <a target="_blank" href="https://www.mayank.co/blog/react-server-components">good</a> <a target="_blank" href="https://www.flightcontrol.dev/blog/nextjs-app-router-migration-the-good-bad-and-ugly">articles</a> that touch on some of the concerns and annoyances I also share, but I'll try to cover some of my views about it.</p>
<p>I had a chance to use Next.js 13 for a few weeks and noticed that some things make my job a lot simpler, while others hinder my basic coding tasks. So I wanted to share my initial thoughts about the downsides I can see, while they are still fresh, mainly related to developer experience and maintainability:</p>
<ul>
<li><p>it's weird working with files containing ( ) @ [ ] and all the remaining jungle in their paths inside a terminal. Also, following a file hierarchy with such names can be very hard</p>
</li>
<li><p>tedious to split up and organize components in a meaningful way. Decisions on where and how to split the server &lt;-&gt; client code adds overhead. It can be hard to have a good organization for where things are supposed to be without having to constantly switch their places(server component broken down into server + client component or completely promoted to client component over time). Apps can hold a bunch of UI state, and it often happens that you start with server-side components pulling in data directly, and then afterward you figure out that you need to add some UI state for interactivity... then you need to reorganize the code, and create a new hierarchy. I usually love splitting up components but with Next.js it feels like I need to spend 50% more time working and thinking about it if I want to make optimal use of RSC(if not, why even use Next.js in the first place?!). Maybe you get used to it after a couple of projects. But I can't shake the feeling that it's a trade-off no one talks about. A simple design change can lead to rehaul of multiple files.</p>
</li>
<li><p>writing 'use client' and 'use server' all over the place gets annoying and if you don't settle on a good convention for placing things you'll never know which files contain only the server-side logic and which ones only the client, or both without taking a peek at the source and for me this is tiring. Even if you manage to neatly organize it, like I said, server &lt;–&gt; client boundaries tend to move places over time and require you to reorganize the functions/files. The alternative is to just "leave it for later" and eventually give up on any kind of organization in this regard</p>
</li>
<li><p>there are many downsides when it comes to the data layer. It's hard to debug, understand, and keep it in a good shape. Client-only apps usually call HTTP APIs which return pure data. That is very easy to debug using browser dev tools in terms of correctness and performance. Writing tests, mocks, automation scripts, and load tests is easy. There are a bunch of well-known and battle-tested tools for doing that. Next.js and React RSC move all of this into the new and unexplored territory and no one mentions this issue. I want to be able to dissect the complete data layer at any point in time using multiple approaches, add tests, and automation scripts. It seems like a bunch of inexperienced developers are currently jumping on this hype train without really thinking things through. For a framework that intertwines so much client and the backend side, I want to be able to debug and analyze all of the things we usually do through browser and backend side dev tools and do it with comparable ease</p>
</li>
<li><p>a lot of implicit logic comes from Next.js that you are just supposed to know. Somehow reminds me of AWS cloud service pricing where you need to hire an expert to help you figure out which things are used in a suboptimal way because it wasn't clear from the start. If Next.js takes over I can easily imagine that specialized consultants and agencies will thrive because IMO inexperienced devs can create such a mess with this stack that the pure client-side-apps could only dream of</p>
</li>
<li><p>more complex servers are required which could mean higher costs in some cases. This is not only about the actual server that serves the app's content, it's also about the servers that will run CI/CD and automation tasks in general, run alternative environments to production one, etc. If everything is primarily server rendered from the start and you don't expose digestible APIs, does that mean that you can no longer rely on basic test scripts to verify data returned by the an HTTP API, but rather that you need to move everything towards more costly and less stable e2e tests because you need to digest JSX generated markup instead? OK you can still build everything with HTTP endpoints, but I'd argue that that moves you back, one step closer to good old SPAs(comparing the advertised advantages of working with data in Next.js)</p>
</li>
<li><p>if you build a web app totally behind auth + app meant for users on a good internet connection, internal use apps etc. then low cache hits between user requests, and no SEO benefits means that if you go the Next.js route you trade client-side complexity for backend/infrastructure/hosting complexity and costs for the questionably low benefit</p>
</li>
</ul>
<p>These are my 13 cents based on my limited experience with the version of Next.js. For now, I think I'll hold off on joining the Next.js hype but who knows, I'm sure that Vercel will work hard to improve the DX and the product overall. At the same time, I have an uneasy feeling that those improvements will come through a set of proprietary tools and services that will lock everyone in with Vercel over time.</p>
<p>Close and tight collaboration with React team gave Vercel a head start with RSC implementation but generated a thing that is still relatively open and free. From the business perspective, there must be something else. I'm afraid that the next iterations of solutions and tools they produce for this open platform will start to be more and more cloud-based and found behind a subscription plan. At the moment they have the edge in infrastructure hosting Next.js projects, but that could change over time since Next.js is an open project. I believe that there will be something else that you will need to pay for to have the best development and hosting experience.</p>
]]></content:encoded></item><item><title><![CDATA[JS Date constructor timezone oddity]]></title><description><![CDATA[For a long time, I only did Date manipulation in JS via some date library that wrapped over the native Date object. There were always a lot of missing features on the native Date. But for some time now I've been listening to how date libraries are ra...]]></description><link>https://apisurfer.com/js-date-constructor-timezone-oddity</link><guid isPermaLink="true">https://apisurfer.com/js-date-constructor-timezone-oddity</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[date_object]]></category><category><![CDATA[datetime]]></category><category><![CDATA[timezone]]></category><dc:creator><![CDATA[Luka Vidakovic]]></dc:creator><pubDate>Wed, 10 Jan 2024 17:30:42 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/QrPDA15pRkM/upload/73d75a69d3e66c15605a7a9543771795.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>For a long time, I only did Date manipulation in JS via some date library that wrapped over the native Date object. There were always a lot of missing features on the native Date. But for some time now I've been listening to how date libraries are rarely necessary these days.</p>
<p>After my short encounter with it, I wouldn't say so. I wasn't aware of how well-protected we all were by those battle-tested date libraries. It's such an easy way to shoot yourself in the foot using plain Date objects. This example alone is enough to make you rethink some things:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(<span class="hljs-string">'2024-01-01'</span>)
</code></pre>
<p>What do you think this does? The date format is OK. The date should be parsed and we should get a Date object with the specified date set internally, right? Well, correct to some extent, but it depends on the time zone...</p>
<p>What if I told you that <strong>you could end up with a 2023-12-31</strong> when converting that object to a string? If you don't know about Date quirks it's almost a certainty you will end up with this bug eventually.</p>
<p>What if I told you that <strong>you could end up with two different dates</strong> for these 2 inputs to the Date constructor:</p>
<ul>
<li><p>2024-01-01</p>
</li>
<li><p>2024-01-01T00:00:00</p>
</li>
</ul>
<p>This is the reality in which we as web devs live. It's not commonly known behavior unless you worked with something that required a deeper knowledge of the Date internals. This is something that caused a bug in my code the other day. It was reported by users that are behind the UTC. Now, why would that be the case?</p>
<p>It happens because Date assumes the time zone differently based on having time specified vs. the case with no time specified. And by assuming an input in the wrong time zone it ends up with the wrong date being set internally(shifted by a difference in a client's time zone offset to the UTC).</p>
<p>Look at this example, where I'm running the JS on the client located in the UTC+1 zone:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1704831300138/fdc3a120-b614-4d19-a8b5-bc85bf09336e.png" alt class="image--center mx-auto" /></p>
<p>Do you notice any difference? :)</p>
<p>It's weird, it's unexpected, it's unintuitive. But, does it have some meaningful background, maybe some well-defined standard to back it up? Also no. This is a remnant of the old JS world, an error in specification that does not adhere to the ISO 8601 standard and never will because of backward compatibility issues.</p>
<p><code>new Date('2024-01-01')</code> should do the same as <code>new Date('2024-01-01T00:00:00')</code> but it doesn't. Without time explicitly being set, it assumes that the input parameter(<code>'2024-01-01')</code> is in UTC. Now if that code runs on the client that's behind UTC, for example, a western part of the globe. If the client's time zone lags for e.g. 6 hours, the Date constructor will produce a new Date object with the date/time being set to <code>2023-12-31T18:00:00</code>. On the other hand, if you pass in the <code>2024-01-01T00:00:00</code> string(notice that one also doesn't specify a time zone) it <strong>won't</strong> assume UTC but rather use your local time zone offset and you will get the date object with the exact date/time for your time zone :).</p>
<p>This is where the bugs come from. Date constructor assumes UTC time zone for one input format while on the other hand, it uses the local time zone for the format that has time specified. Pretty unexpected. If you try to do the same inputs using <a target="_blank" href="https://momentjs.com/">moment.js</a> you will get the expected and consistent behavior because date libraries handle so many weird cases for us out of the box.</p>
<p>It was a bit hard to dig out the exact reason why it behaves like this, but I eventually found the reason on <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date">MDN page</a>:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1704831044590/6394aab6-57dc-45a1-b142-8c2bdbac48c3.png" alt class="image--center mx-auto" /></p>
<p>Also, see the <a target="_blank" href="https://maggiepint.com/2017/04/11/fixing-javascript-date-web-compatibility-and-reality/">mentioned post</a> on the topic and the proposal for a solution — <a target="_blank" href="https://tc39.es/proposal-temporal/docs/">proposed Temporal API</a>. But at the moment this is the situation with Temporal API:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1704832135079/665b0fa3-529b-40f4-86ea-5a5c35883009.png" alt class="image--center mx-auto" /></p>
<p>Leaving us with plenty of reasons to still choose to work with external libraries for dates. Just like the following couple of reasons:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// 1. Months are zero-indexed, while days and years are one-indexed</span>
<span class="hljs-keyword">const</span> date = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(<span class="hljs-number">2022</span>, <span class="hljs-number">0</span>, <span class="hljs-number">1</span>); <span class="hljs-comment">// January 1, 2022</span>

<span class="hljs-comment">// 2. Two-digit years are interpreted as 1900-1999</span>
<span class="hljs-keyword">const</span> y2k = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(<span class="hljs-number">99</span>, <span class="hljs-number">0</span>, <span class="hljs-number">1</span>); <span class="hljs-comment">// January 1, 1999</span>

<span class="hljs-comment">// 3. Negative years are allowed, representing years B.C.</span>
<span class="hljs-keyword">const</span> ancient = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(<span class="hljs-number">-44</span>, <span class="hljs-number">2</span>, <span class="hljs-number">15</span>); <span class="hljs-comment">// March 15, 45 B.C.</span>

<span class="hljs-comment">// 4. Parsing date strings can be inconsistent across browsers and locales</span>
<span class="hljs-keyword">const</span> parsedDate = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(<span class="hljs-string">'03/15/2022'</span>); <span class="hljs-comment">// May not work consistently</span>

<span class="hljs-comment">// 5. Daylight Saving Time can cause unexpected behavior</span>
<span class="hljs-keyword">const</span> dst = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(<span class="hljs-number">2021</span>, <span class="hljs-number">2</span>, <span class="hljs-number">14</span>, <span class="hljs-number">2</span>, <span class="hljs-number">30</span>); <span class="hljs-comment">// March 14, 2021, 03:30 (skipped hour)</span>
</code></pre>
<p>By the way, please consider a bit more modern options for handling dates like <a target="_blank" href="https://day.js.org/">Day.js</a> or <a target="_blank" href="https://date-fns.org/">date-fns</a>. I mentioned moment.js just because it was the easiest to reach and verify the behavior, but other than that its usage is no longer recommended.</p>
]]></content:encoded></item><item><title><![CDATA[Testing complex flag conditions and ensuring complete case coverage]]></title><description><![CDATA[Recently I stumbled upon an interesting test case where I needed to cover many possibilities and make sure everything continues to work as intended in the future. I had 7 boolean flags that needed to be used to toggle an app functionality.
It's a bit...]]></description><link>https://apisurfer.com/testing-complex-flag-conditions-and-ensuring-complete-case-coverage</link><guid isPermaLink="true">https://apisurfer.com/testing-complex-flag-conditions-and-ensuring-complete-case-coverage</guid><category><![CDATA[Testing]]></category><category><![CDATA[Boolean]]></category><category><![CDATA[flag-arguments]]></category><dc:creator><![CDATA[Luka Vidakovic]]></dc:creator><pubDate>Fri, 25 Aug 2023 17:31:09 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/qDgTQOYk6B8/upload/feab764312c9b08debe725e072f82a05.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Recently I stumbled upon an interesting test case where I needed to cover many possibilities and make sure everything continues to work as intended in the future. I had 7 boolean flags that needed to be used to toggle an app functionality.</p>
<p>It's a bit tricky to cover this with tests and to make sure that the function triggers correctly for all the proper cases, ensuring it won't break with future changes. To make things even more serious this logic makes decisions for a critical user path during the app onboarding and could potentially break the app for new users.</p>
<p>Therefore tests should cover all the possibilities and lock the correct behaviour to make it future-proof. There are 2^7 = 128 flag combinations. Writing this by hand would be extremely time-consuming and error-prone. I decided to generate all the possible flag combinations in a 2D array matrix, iterate over rows and use row values to initialize flags. The test simply needs to go over all 128 rows and test for correct output based on all the input combinations.</p>
<p>First I needed to create a function that generates a 2D matrix with all the possible boolean combinations:</p>
<pre><code class="lang-typescript">  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">generateFlagCombinations</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> flags = [];
    <span class="hljs-keyword">const</span> numFlags = <span class="hljs-number">7</span>;

    <span class="hljs-comment">// Generate all possible combinations of boolean values for the flags</span>
    <span class="hljs-comment">// [[false, false, false, false, false, false, false], [true, false, false, false, false, false, false], ...</span>
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-built_in">Math</span>.pow(<span class="hljs-number">2</span>, numFlags); i++) {
      <span class="hljs-keyword">const</span> flagCombination = [];
      <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> j = <span class="hljs-number">0</span>; j &lt; numFlags; j++) {
        flagCombination.push(<span class="hljs-built_in">Boolean</span>(i &amp; (<span class="hljs-number">1</span> &lt;&lt; j)));
      }
      flags.push(flagCombination);
    }

    <span class="hljs-keyword">return</span> flags;
  }
</code></pre>
<p>For the actual test to simplify naming and focus on what's important, I'll just call the boolean flags A – G:</p>
<pre><code class="lang-typescript">it(<span class="hljs-string">'should activate in correct cases'</span>, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> flagCombinations = generateFlagCombinations();

  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> [A,B,C,D,E,F,G] <span class="hljs-keyword">of</span> flagCombinations) {
    <span class="hljs-keyword">const</span> result = shouldShowProceedButton({A,B,D,E,C,F,G});

    <span class="hljs-keyword">if</span> (E) {
      expect(result).toBe(A &amp;&amp; !F);
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (!G) {
      expect(result).toBe(<span class="hljs-literal">false</span>);
    } <span class="hljs-keyword">else</span> {
      expect(result).toBe(A &amp;&amp; (!C || (B &amp;&amp; D &amp;&amp; C)) &amp;&amp; !F);
    }
  }
});
</code></pre>
<p>And that would be it. We've created:<br />- a small function that generates all the possible flag combinations — 128 rows representing 128 possible flag permutations<br />- a simple test that uses the generated combinations. It iterates over all the rows of a 2D matrix and uses the values to initialize the flags to test for every single possible combination</p>
<p>This way we can be sure that we've covered all the possibilities and that we know what to expect from a given function for every combination. This locks the function's behaviour in a way and makes it harder to accidentally change the behaviour.</p>
<p>By the way, I know that using so many flags to make a decision might not be the greatest strategy but sometimes you don't have other choice than to use what's already there 😶‍🌫️</p>
]]></content:encoded></item><item><title><![CDATA[Minimal example of file upload using Next.js app router]]></title><description><![CDATA[Some old methods of accessing uploaded file contents inside a Next.js route handler don't work anymore or at least not in a way many tutorials explain it. E.g. many outdated online examples use page router which has route handlers that receive differ...]]></description><link>https://apisurfer.com/minimal-example-of-file-upload-using-nextjs-app-router</link><guid isPermaLink="true">https://apisurfer.com/minimal-example-of-file-upload-using-nextjs-app-router</guid><category><![CDATA[Next.js]]></category><category><![CDATA[HTML5]]></category><category><![CDATA[File Upload]]></category><category><![CDATA[Input]]></category><dc:creator><![CDATA[Luka Vidakovic]]></dc:creator><pubDate>Sat, 19 Aug 2023 19:53:04 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1692474737481/aadb29ce-80d4-43df-9824-dcfbd81a3c4c.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Some old methods of accessing uploaded file contents inside a Next.js route handler don't work anymore or at least not in a way many tutorials explain it. E.g. many outdated online examples use page router which has route handlers that receive different types of arguments.</p>
<p>Following is a minimal proof of concept that you can extend upon to provide better UX and save or process the file, add error handling etc. The example is intentionally bare bones and not using any libraries to get down to the gist of things. It sets up a minimal example that accepts and reads a file.</p>
<h2 id="heading-html-side">HTML side</h2>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"file"</span> /&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">
    <span class="hljs-built_in">document</span>
      .querySelector(<span class="hljs-string">'input[type="file"]'</span>)
      .addEventListener(<span class="hljs-string">'change'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">event</span>) </span>{
          <span class="hljs-keyword">const</span> file = event.target.files[<span class="hljs-number">0</span>];
          <span class="hljs-keyword">const</span> formData = <span class="hljs-keyword">new</span> FormData();
          formData.append(<span class="hljs-string">'file'</span>, file);
          fetch(
            <span class="hljs-string">'/api/upload-route'</span>,
            { <span class="hljs-attr">method</span>: <span class="hljs-string">'POST'</span>, <span class="hljs-attr">body</span>: formData }
          );
      })
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>This creates a raw HTML input field with "onchange" event handler attached to it. When a file is selected, it automatically appends its content to FormData object as a <code>file</code> field and makes a POST request using it for the body of the request. By using FormData, we produce a <code>multipart/form-data</code> encoded POST request. Simply put, we are sending a specific type of message to the server that can have multiple parts(i.e. text fields, files, etc.) and they are separated by a specified boundary.</p>
<h2 id="heading-nextjs-app-route-handler-side">Next.js app route handler side</h2>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> readFile = (req: Request): <span class="hljs-built_in">Promise</span>&lt;<span class="hljs-built_in">string</span>&gt; =&gt;
  <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-keyword">async</span> (resolve, reject) =&gt; {
    <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> req.formData();
    <span class="hljs-keyword">const</span> file = data.get(<span class="hljs-string">'file'</span>) <span class="hljs-keyword">as</span> File | <span class="hljs-literal">null</span>;

    <span class="hljs-keyword">if</span> (!file) {
      <span class="hljs-keyword">return</span> reject(<span class="hljs-string">'No file sent!'</span>);
    }

    <span class="hljs-keyword">const</span> buffer = Buffer.from(<span class="hljs-keyword">await</span> file.arrayBuffer());
    <span class="hljs-keyword">const</span> content = buffer.toString(<span class="hljs-string">'utf8'</span>);

    resolve(content);
  });

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">POST</span>(<span class="hljs-params">req: Request</span>) </span>{
  <span class="hljs-keyword">const</span> content = <span class="hljs-keyword">await</span> readFile(req);
  <span class="hljs-keyword">const</span> responseBody = <span class="hljs-built_in">JSON</span>.stringify({ content })
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Response(responseBody);
}
</code></pre>
<p>On the Next.js side, we reverse the process. We call <code>req.formData()</code> to get the FormData sent from the client side. Read a specific field named <code>file</code> by using <code>data.get('file')</code> and then load the binary file content into a new Buffer and decode the content as a <code>utf8</code> string.<br /><code>POST handler function</code> just uses decoded file contents to loop them back to the client inside a response to this API call.</p>
<p>This example assumes you have a textual file being uploaded and you need to access its contents inside an API route handler function. In case the file is binary, you'd probably want to skip the "read" part and just save or transition the contents to some other place.</p>
<p>Thank you for reading and Godspeed!</p>
]]></content:encoded></item><item><title><![CDATA[The reason we specify type="button" for button elements]]></title><description><![CDATA[If you didn't have experience with the web ~10 years or more ago it might be weird to use buttons on the web, especially when you don't want them to trigger form submits. You might have asked yourself: "Why do I need to specify button type to be a bu...]]></description><link>https://apisurfer.com/the-reason-we-specify-typebutton-for-button-elements</link><guid isPermaLink="true">https://apisurfer.com/the-reason-we-specify-typebutton-for-button-elements</guid><category><![CDATA[HTML5]]></category><category><![CDATA[button]]></category><category><![CDATA[HTML tags ]]></category><dc:creator><![CDATA[Luka Vidakovic]]></dc:creator><pubDate>Sat, 19 Aug 2023 14:30:50 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1692455852974/7e02dd24-8792-45f3-ae13-9c8cf737ac7a.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you didn't have experience with the web ~10 years or more ago it might be weird to use buttons on the web, especially when you don't want them to trigger form submits. You might have asked yourself: "Why do I need to specify button type to be a button?". Shouldn't that be the default behaviour?</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"button"</span>&gt;</span>text<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
</code></pre>
<p>Historically, the primary purpose for buttons was submitting and resetting HTML forms. “submit” was always the default type since button elements were used mostly for that purpose, submitting forms.</p>
<p>And we used them like so:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">action</span>=<span class="hljs-string">"/send"</span> <span class="hljs-attr">method</span>=<span class="hljs-string">"post"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Enter your message"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">button</span>&gt;</span>Save<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
</code></pre>
<p>Then as JS evolved and became more and more powerful, people started building dynamic interfaces and apps. HTML buttons needed to follow and mimic the behaviour we see in native apps. There was a new requirement to have them trigger various actions inside the app, even more so than submitting forms. But, at the same time web platform has always been backwards compatible. For that reason, changing the existing HTML element to default to <code>type="button"</code> was not an option.</p>
<p><code>type="button"</code> was introduced to support "new" behaviour that avoids the default of submitting forms.</p>
]]></content:encoded></item><item><title><![CDATA[How long is "new" new anyway]]></title><description><![CDATA[Quick hint and a reminder for myself:
In programming, try not to prefix things with "new".
"New" usually doesn't stay new for very long :). It becomes especially problematic when you introduce the new stuff for the 2nd time. At that point, you have a...]]></description><link>https://apisurfer.com/how-long-is-new-new-anyway</link><guid isPermaLink="true">https://apisurfer.com/how-long-is-new-new-anyway</guid><category><![CDATA[naming]]></category><category><![CDATA[#namingconvention]]></category><dc:creator><![CDATA[Luka Vidakovic]]></dc:creator><pubDate>Wed, 16 Aug 2023 21:24:27 GMT</pubDate><content:encoded><![CDATA[<p>Quick hint and a reminder for myself:</p>
<p>In programming, <strong>try not to prefix things with "new"</strong>.</p>
<p>"New" usually doesn't stay new for very long :). It becomes especially problematic when you introduce the new stuff for the 2nd time. At that point, you have a choice of living with the OLD stuff prefixed with a "new" word and figuring out how to name the new new stuff or having to refactor a bunch of names just to avoid the confusion.</p>
<p>End of story. Bye, bye 🙃</p>
]]></content:encoded></item><item><title><![CDATA[PRs for Organizing File Hierarchy]]></title><description><![CDATA[Tldr: be swift :)
I just wanted to mention 2 things I noticed helping with file hierarchy refactors.
Stick to only file organization and imports
This makes it easier to do code reviews, check and spot import misreferences etc. It'll also make things ...]]></description><link>https://apisurfer.com/prs-for-organizing-file-hierarchy</link><guid isPermaLink="true">https://apisurfer.com/prs-for-organizing-file-hierarchy</guid><category><![CDATA[Git]]></category><category><![CDATA[versioning]]></category><dc:creator><![CDATA[Luka Vidakovic]]></dc:creator><pubDate>Tue, 15 Aug 2023 22:39:58 GMT</pubDate><content:encoded><![CDATA[<p>Tldr: be swift :)</p>
<p>I just wanted to mention 2 things I noticed helping with file hierarchy refactors.</p>
<h2 id="heading-stick-to-only-file-organization-and-imports">Stick to only file organization and imports</h2>
<p>This makes it easier to do code reviews, check and spot import misreferences etc. It'll also make things easier to revert, or discard the work altogether</p>
<h2 id="heading-put-priority-on-this-kind-of-prs">Put priority on this kind of PRs</h2>
<p>This type of PRs can bring chaos to reviews and merge process of new branches if they stay open for too long. You typically want to do the review and <mark>merge back to the source branch as soon as possible</mark>.<br />The most common problem with this kind of PRs is when someone else decides to work on the same files that no longer exist under a certain path. When merging code changes with a branch that changed file paths it's too easy to overlook code changes and lose them by just accepting the file location change. This can be time-consuming to troubleshoot afterwards. It's much easier to spot this if a PR that does the file organization is merged sooner rather than later.</p>
]]></content:encoded></item><item><title><![CDATA[Project status: finished!]]></title><description><![CDATA[When was the last time you finished a software project? Right?  
Well, I'm dealing with some legacy project and I stumbled upon moment.js' documentation that explains the purpose of the project, how it was finally achieved, and that the project is co...]]></description><link>https://apisurfer.com/project-status-finished</link><guid isPermaLink="true">https://apisurfer.com/project-status-finished</guid><category><![CDATA[library]]></category><category><![CDATA[MomentJS]]></category><dc:creator><![CDATA[Luka Vidakovic]]></dc:creator><pubDate>Wed, 19 Jul 2023 22:42:01 GMT</pubDate><content:encoded><![CDATA[<p>When was the last time you finished a software project? Right?  </p>
<p>Well, I'm dealing with some legacy project and I stumbled upon <a target="_blank" href="https://momentjs.com/docs/">moment.js' documentation</a> that explains the purpose of the project, how it was finally achieved, and that the project is considered being finished!</p>
<p>It must have been a great feeling to write that chunk of text. In my career, having dozens of projects behind me it's hard to find a couple of them that I'd consider finished as in filling out their purpose and that could remain exactly the same to continue to do so. This is so hard to achieve in software engineering long term where everything is in the state of flux, but I guess scoping things and solving particular problems is the key.</p>
<p>Congratulations to Moment.js team and all the contributors! Your project was a massive undertaking that helped make the web a more humane place for the rest of us.</p>
]]></content:encoded></item></channel></rss>