<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Anton Bahurinsky</title>
    <description>The latest articles on DEV Community by Anton Bahurinsky (@antoncodes).</description>
    <link>https://dev.to/antoncodes</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F739253%2Fc2af8bac-0e81-48ef-b9f0-a5ad4f588581.png</url>
      <title>DEV Community: Anton Bahurinsky</title>
      <link>https://dev.to/antoncodes</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/antoncodes"/>
    <language>en</language>
    <item>
      <title>5 tips for better Swagger docs in NestJS</title>
      <dc:creator>Anton Bahurinsky</dc:creator>
      <pubDate>Thu, 04 Jul 2024 13:05:25 +0000</pubDate>
      <link>https://dev.to/antoncodes/5-tips-for-better-swagger-docs-in-nestjs-ng9</link>
      <guid>https://dev.to/antoncodes/5-tips-for-better-swagger-docs-in-nestjs-ng9</guid>
      <description>&lt;p&gt;Swagger is a powerful tool for documenting RESTful APIs when working with NestJS. And the NestJS developers have done great job for our comfortable work with Swagger within the framework. Now it is our responsibility as developers to apply this tool at its full potential and thus to make another bit of contribution of making this world a better place.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Document all endpoint response codes
&lt;/h2&gt;

&lt;p&gt;When you develop a RESTful API (for example, on NestJS), there usually is a consumer of it: a frontend. The frontend is often developed by other people (than you/backend) who may not have access to the backend and/or enough backend skills. Thus, an API description is the simplest way to know what they are working with (against).&lt;/p&gt;

&lt;p&gt;Frontend developers often have to handle different cases like invalid input, failed authentication, taken email address etc. And response codes are the way for them to know what actually happened on the backend.&lt;/p&gt;

&lt;p&gt;Take some time to add decorators like &lt;code&gt;@ApiCreatedResponse()&lt;/code&gt;, &lt;code&gt;@ApiBadRequestResponse()&lt;/code&gt; or &lt;code&gt;@ApiConflictResponse()&lt;/code&gt; to your endpoints. Some of them (like &lt;code&gt;@ApiInternalServerErrorResponse()&lt;/code&gt;) don't even have to be added to every endpoint, but simply to a controller.&lt;/p&gt;

&lt;p&gt;Additional descriptions may also me helpful:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="c1"&gt;// auth.controller.ts&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ApiConflictResponse&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;When the username is already taken.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;signup&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;signup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzpq34pjasssffeoj4utn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzpq34pjasssffeoj4utn.png" alt="Response codes with description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Describe request and response return types
&lt;/h2&gt;

&lt;p&gt;Just like with response codes, API consumers have to know what an endpoint consumes and returns. It is relatively easy to have those types in sync with actual code (see how &lt;code&gt;SignupDto&lt;/code&gt; and &lt;code&gt;LoggedInDto&lt;/code&gt; are used here):&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="c1"&gt;// auth.controller.ts&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ApiBody&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SignupDto&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ApiCreatedResponse&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;LoggedInDto&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;signup&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;signup&lt;/span&gt;&lt;span class="p"&gt;(@&lt;/span&gt;&lt;span class="nd"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;signupDto&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SignupDto&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;LoggedInDto&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftoxjj67va4edn8cj3b16.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftoxjj67va4edn8cj3b16.png" alt="Request and response return types"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Provide examples of field values
&lt;/h2&gt;

&lt;p&gt;Remember, Swagger is not only declarative, it is &lt;strong&gt;interactive&lt;/strong&gt;. This means that users are not only able to read the API docs, but also to make requests.&lt;/p&gt;

&lt;p&gt;Defining field value examples will not only tell API consumers about what to expect. If a field value example is defined for an input entity or DTO, it becomes a default value for making requests:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="c1"&gt;// login.dto.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LoginDto&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ApiProperty&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;example&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;alice&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Allow&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ApiProperty&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;example&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Alice1111$&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Allow&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

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

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3yurv0pnirtrf4pxgcao.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3yurv0pnirtrf4pxgcao.png" alt="Field value examples as defaults"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Enable user authentication
&lt;/h2&gt;

&lt;p&gt;When applicable, of course.&lt;/p&gt;

&lt;p&gt;One of the most popular authentication mechanisms for RESTful APIs are bearer access tokens. These tokens are often JWT, but their format doesn't affect our example.&lt;/p&gt;

&lt;p&gt;In order to enable bearer token authentication you will first need to enable it in your &lt;code&gt;main.ts&lt;/code&gt;, where you are defining and applying Swagger:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="c1"&gt;// main.ts -&amp;gt; bootstrap()&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DocumentBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addBearerAuth&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bearer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;in&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;header&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And then apply the &lt;code&gt;@ApiBearerAuth()&lt;/code&gt; for an auth-restricted endpoint (or for entire controller if all its endpoints are restricted):&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="c1"&gt;// auth.controller.ts&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;me&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ApiBearerAuth&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;me&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

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

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

&lt;h2&gt;
  
  
  5. Manage tags
&lt;/h2&gt;

&lt;p&gt;While being optional, tags are a great way of ordering the API endpoint groups. With tags, you can place the most used endpoints of the API to the top, which will increase the Swagger API efficiency for the entire team. In our example, database seeding and authentication endpoints are the most frequently used. Here is how we place them at the top:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="c1"&gt;// auth.controller.ts&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ApiTags&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;auth&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Controller&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;auth&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AuthController&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

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

&lt;span class="c1"&gt;// main.ts -&amp;gt; bootstrap()&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DocumentBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addTag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;seed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addTag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;auth&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;// ... rest of tags&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

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

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

&lt;p&gt;That's it for now! Hope these tips will make you a good service.&lt;/p&gt;

&lt;p&gt;If you want to see how everything from this post is implemented within an actual NestJS application, check out &lt;a href="https://github.com/AntonCodesCom/simple-todo-nest" rel="noopener noreferrer"&gt;this repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you have more ideas of how to improve Swagger API documenting, don't hesitate to leave a comment!&lt;/p&gt;

&lt;p&gt;Don't stop coding, don't stop growing, and choose the good side 🇺🇦&lt;/p&gt;

</description>
      <category>node</category>
      <category>nestjs</category>
      <category>swagger</category>
      <category>restapi</category>
    </item>
    <item>
      <title>NestJS: stop handling errors like this!</title>
      <dc:creator>Anton Bahurinsky</dc:creator>
      <pubDate>Wed, 30 Mar 2022 15:20:42 +0000</pubDate>
      <link>https://dev.to/antoncodes/nestjs-stop-handling-errors-like-this-2446</link>
      <guid>https://dev.to/antoncodes/nestjs-stop-handling-errors-like-this-2446</guid>
      <description>&lt;p&gt;I see people making this mistake all the time.&lt;/p&gt;

&lt;p&gt;Let's say you've got an API server written in NestJS, and you need an endpoint for fetching a single product by its ID. So, in your &lt;code&gt;product.service.ts&lt;/code&gt; you would typically write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;findOne&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;productRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;findOneOrFail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;NotFoundException&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(I'm using TypeORM here, but the same principle can be applied to other libraries as well.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So what we've got here? You attempt to query a product by its ID, and if there is no product with this ID, you just throw the 404. And in your &lt;code&gt;product.controller.ts&lt;/code&gt; you simply write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;:id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;findOne&lt;/span&gt;&lt;span class="p"&gt;(@&lt;/span&gt;&lt;span class="nd"&gt;Param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;productService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;findOne&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And everything indeed works fine.&lt;/p&gt;

&lt;h2&gt;
  
  
  So what's the problem with this?
&lt;/h2&gt;

&lt;p&gt;The thing is that the above code would work well for REST APIs. But what if tomorrow you will need to fetch that product via GraphQL or WebSockets? The &lt;code&gt;NotFoundException()&lt;/code&gt; and its fellow HTTP-related exceptions will not be suitable for that any more. You will definitely need different error handling.&lt;/p&gt;

&lt;p&gt;Therefore, by throwing HTTP-related errors (exceptions) from services &lt;strong&gt;you simply make your code less reusable&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to do instead?
&lt;/h2&gt;

&lt;p&gt;As we have implicitly mentioned above, the HTTP-related exceptions are suited only for REST APIs. But so are &lt;em&gt;controllers&lt;/em&gt;!&lt;/p&gt;

&lt;p&gt;You see, NestJS controllers are used only during REST API development, while services can have wider use. This makes controllers the perfect place for throwing HTTP-related exceptions.&lt;/p&gt;

&lt;p&gt;Thus, in the simplest scenario, your &lt;code&gt;product.service.ts&lt;/code&gt; code (fragment) would look just like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;findOne&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;productRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;findOneOrFail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And in the &lt;code&gt;product.controller.ts&lt;/code&gt; you now handle the "not found" error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;:id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;findOne&lt;/span&gt;&lt;span class="p"&gt;(@&lt;/span&gt;&lt;span class="nd"&gt;Param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;productService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;findOne&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;NotFoundException&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course, if you need more sophisticated error handling, you can define custom error classes to throw from services and handle in controllers (for REST) or resolvers (for GraphQL).&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Don't throw HTTP-related exceptions from services, throw them from controllers!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Have you been making this mistake before reading this article? Comment down below!&lt;/p&gt;

&lt;p&gt;And, of course, leave your reaction to this article, share it with your friends via social media and follow me on this platform!&lt;/p&gt;

&lt;p&gt;Don't stop coding,&lt;br&gt;
don't stop growing,&lt;br&gt;
&lt;strong&gt;stand with Ukraine!&lt;/strong&gt; 🇺🇦&lt;/p&gt;

</description>
      <category>node</category>
      <category>typescript</category>
      <category>javascript</category>
      <category>nestjs</category>
    </item>
    <item>
      <title>Stop using polyrepo (until you read this)</title>
      <dc:creator>Anton Bahurinsky</dc:creator>
      <pubDate>Tue, 22 Mar 2022 10:51:39 +0000</pubDate>
      <link>https://dev.to/antoncodes/stop-using-polyrepo-until-you-read-this-25j6</link>
      <guid>https://dev.to/antoncodes/stop-using-polyrepo-until-you-read-this-25j6</guid>
      <description>&lt;h1&gt;
  
  
  Stop using polyrepo (until you read this)
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;"Monorepo vs polyrepo" is one of important questions that arise when you start working on a new web project. In this article you will learn about basic rules of when to apply either monorepo or polyrepo approach to your web application and the benefits each of the approaches provides.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There are a lot of great frameworks and platforms for developing web applications, front-end and back-end. However, most (all?) of them provide tools for initialization and further work with an application as a standalone repository.&lt;/p&gt;

&lt;p&gt;Initially, there's nothing wrong with that: it is the simplest way to get started. But as a consequence, during your web developer career you basically get educated to the polyrepo-first approach.&lt;/p&gt;

&lt;p&gt;Later on, as a constantly learning developer, you start to explore what the monorepo is and its potential benefits. So when you start developing your next web application, you ask a fair question: &lt;em&gt;"What are the reasons to use monorepo for this project?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;But the truth is that the power of monorepo approach can very much make us change the thinking direction itself! So upon starting a new web project we now ask: &lt;em&gt;&lt;strong&gt;"What are the reasons NOT to use monorepo?"&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Monorepo-first approach
&lt;/h2&gt;

&lt;p&gt;Imagine you are starting the work on a new client-server web application (i.e. its architecture contains two nodes: a front-end client and a back-end server). If you choose the monorepo approach for this project, this will provide you a line of benefits.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Integrity of development process
&lt;/h3&gt;

&lt;p&gt;Developing a feature usually involves changes in both client and server. With monorepo, all the feature code is wintin a &lt;strong&gt;single pull request&lt;/strong&gt;, which makes development, testing and code review processes more stable.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Better deployment automation
&lt;/h3&gt;

&lt;p&gt;Since a new feature code is under a single pull request, it allows you to automate the CI/CD flow for all (affected) nodes upon a single merge. Thus, you are able to re-deploy both client and server as a single operation, and this in turn allows to automate execution of end-to-end tests in correct moments.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Easier for pipelines
&lt;/h3&gt;

&lt;p&gt;With some additional tools for managing monorepos you can trigger testing and re-deployment only for the nodes that contain changes and their dependents, aka affected nodes. For example, if you have changes only on the client, you don't have to re-deploy the server. This will reduce overall pipeline usage, which will eventually benefit your wallet and clock.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Easier code sharing
&lt;/h3&gt;

&lt;p&gt;If all the nodes of your monorepo are written in the same language (e.g. TypeScript), it is very easy to share code between nodes. For example, you can import data types from server to client, so the client knows which data to expect upon network requests.&lt;/p&gt;

&lt;p&gt;All this comes with relatively low cost: one more tool int your toolchain for monorepo management and some additional configuration of your frameworks to adapt them for monorepo.&lt;/p&gt;

&lt;p&gt;As you can see, the benefits fo using monorepo outweigh its maintenance costs. And this is a sufficient reason to consider &lt;strong&gt;monorepo as a default approach&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  When not to use monorepo
&lt;/h2&gt;

&lt;p&gt;Of course, "default approach" doesn't mean "the only approach". Everything is a trade-off, and "monorepo vs polyrepo" is not an exception.&lt;/p&gt;

&lt;p&gt;The highlighted benefits of monorepo are the most notable when your development team consists of full stack developers, i.e. when each feature (module) is being developed by a single responsible person.&lt;/p&gt;

&lt;p&gt;On the other hand, if you have separate front-end and back-end developers for the client and the server respectively, then the benefits of monorepo diminish.&lt;/p&gt;

&lt;p&gt;Another valid reason to consider polyrepo is when you have some kind of secret algorithm or flow on, for example, the server, so you want to restrict access to it only to trusted people. With polyrepo you can achieve that, still being able to involve freelancers for frontend-only tasks.&lt;/p&gt;

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

&lt;p&gt;In this article we have discussed why you should take monorepo as default approach for web development. We have also named valid reasons when you should consider polyrepo for your project.&lt;/p&gt;

&lt;p&gt;Of course, as an example we took an application consisting only of two nodes: client and server. But the ideas from this article can certainly be scaled up for more complex architectures.&lt;/p&gt;

&lt;p&gt;What is your experience with monorepos and polyrepos? Don't agree with something? Share it in comments!&lt;/p&gt;

&lt;p&gt;If you like the ideas from this article and find them useful, don't forget to leave your reaction and follow me on this platform!&lt;/p&gt;

&lt;p&gt;Don't stop coding,&lt;br&gt;
don't stop growing,&lt;br&gt;
&lt;strong&gt;stand with Ukraine!&lt;/strong&gt; 🇺🇦&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>monorepo</category>
      <category>polyrepo</category>
    </item>
    <item>
      <title>Testing web apps: what you didn't know</title>
      <dc:creator>Anton Bahurinsky</dc:creator>
      <pubDate>Wed, 23 Feb 2022 18:21:03 +0000</pubDate>
      <link>https://dev.to/antoncodes/testing-web-apps-what-you-didnt-know-1pdg</link>
      <guid>https://dev.to/antoncodes/testing-web-apps-what-you-didnt-know-1pdg</guid>
      <description>&lt;p&gt;Quality assurance is an important part of web development. In correspondence, a significant part of the QA is &lt;strong&gt;testing&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The testing itself is a resource-consuming activity. So it is important to perform it correctly in order to achieve maximum benefit with minimal effort (the definition of efficiency).&lt;/p&gt;

&lt;p&gt;Of course, you already heard about unit, integration and end-to-end (E2E) tests. And you have undoubtedly seen this testing pyramid:&lt;/p&gt;

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

&lt;p&gt;The main idea represented by this pyramid is that the majority of test cases must be covered with tests of a lower level, you write a higher-level test only when the test case can not be covered with the lower-level ones.&lt;/p&gt;

&lt;p&gt;What does this mean for us in practice? Since, due to the pyramid, unit tests are the most basic ones, they should be written in the first place, and only when we feel we are not able to cover a test case, we proceed with integration tests, right?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WRONG!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;What is the problem with this approach?&lt;/p&gt;

&lt;p&gt;The thing is that the primary criteria of correctness of work of a web application is its &lt;strong&gt;technical requirements&lt;/strong&gt;. Such requirements are usually written by stakeholders of the application (or under their review).&lt;/p&gt;

&lt;p&gt;So when you write E2E tests, you are actually covering test cases defined by the requirements. This means that you are 100% sure that your tests cover a necessary thing.&lt;/p&gt;

&lt;p&gt;On the other hand, if you start covering the app from a unit test against, for example, a utility function which calculates the total cost of the cart, how do you even know you are ever going to need this function in your code?&lt;/p&gt;

&lt;p&gt;I mean, most probably you will be right in &lt;em&gt;assuming&lt;/em&gt; that you will eventually need it. But even if you will, you are guaranteed to fail in predicting the exact API of the function (e.g. number and type of arguments or return value format). And this alone may make all the unit tests against this function completely useless.&lt;/p&gt;

&lt;p&gt;In turn, because E2E tests are the highest-level tests, they cover the backbone of a feature (e.g. the entire cart), while leaving you some flexibility for implementation details.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;In other words, do whatever you want in the code, just don't break the E2E test.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So now, going back to the cart total cost utility function, I might not cover it with unit tests at all, because it &lt;em&gt;is&lt;/em&gt; an implementation detail by itself (which barely worth my attention)! Even if I make a mistake in the function's logic, that's not a big deal, since it is easy to fix. I might even replace the function with a different one (with identical API, of course).&lt;/p&gt;

&lt;p&gt;So you have already got my point: &lt;strong&gt;write E2E tests first&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Of course, although E2E tests are the most significant tests, they are also the most expensive to write and maintain. That's why it is important to cover with E2E tests only what can't be covered with lower-level tests to avoid over-testing. Otherwise, this will increase cost of the tests themselves and reduce flexibility of the code, which in turn will make further development harder.&lt;/p&gt;

&lt;p&gt;So, we have determined the only correct direction of writing automated tests due to the testing pyramid: &lt;strong&gt;from top to bottom&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Manual tests
&lt;/h2&gt;

&lt;p&gt;Nevertheless, there is a type of tests that is even on a higher level than E2E tests. Those are &lt;strong&gt;manual tests&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Everything tested by an E2E test can be tested by a determined manual test as well. In fact, an E2E test is a derivative of a corresponding manual test. The only reason you are writing E2E tests is that you don't want to perform manual testing on every update (i.e. you want to &lt;em&gt;automate&lt;/em&gt; the testing process).&lt;/p&gt;

&lt;p&gt;Thus, the testing pyramid might as well look like this:&lt;/p&gt;

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

&lt;p&gt;With manual tests you can cover even more test cases than with E2E tests (in fact, you could cover those cases with E2E tests too, but that would make them unreasonably expensive).&lt;/p&gt;

&lt;p&gt;Manual tests have the highest maintenance cost, but the lowest creation cost. You can simply write down test steps on a piece of paper - and you've got a manual test!&lt;/p&gt;

&lt;p&gt;This makes manual tests especially useful for some cases like, for example, when you are not sure about your code structure yet, or when the technical requirements are incomplete or are likely to change, or when there is a time pressure and it may make sense to temporarily sacrifice automated testing.&lt;/p&gt;

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

&lt;p&gt;Now, after reading this article you know the only correct way of web application testing: don't start from unit tests, start from E2E or manual tests. Do you agree with this approach? Or have you invented something better? Write in comments!&lt;/p&gt;

&lt;p&gt;And of course leave your reaction to this article, share it with friends, and follow me on this platform for future articles!&lt;/p&gt;

&lt;p&gt;Don't stop coding, don't stop growing!&lt;/p&gt;

</description>
      <category>testing</category>
      <category>programming</category>
      <category>webdev</category>
    </item>
    <item>
      <title>SQL vs MongoDb: what nobody tells you</title>
      <dc:creator>Anton Bahurinsky</dc:creator>
      <pubDate>Tue, 22 Feb 2022 18:07:21 +0000</pubDate>
      <link>https://dev.to/antoncodes/sql-vs-mongodb-what-nobody-tells-you-hb7</link>
      <guid>https://dev.to/antoncodes/sql-vs-mongodb-what-nobody-tells-you-hb7</guid>
      <description>&lt;p&gt;So you are starting the work on a brand new web application. You're picking up tools for the development, and one of them is a database (or, better said, a database management system - DBMS). You already know all the differences between SQL-based systems and MongoDb (there is plenty of information about this, so I won't duplicate it in this article).&lt;/p&gt;

&lt;p&gt;But despite of this knowledge, you are still in doubt what kind of DBMS to choose (and there's nothing strange to this: &lt;em&gt;it is a damn important decision!&lt;/em&gt;). If you've ever experienced this feeling, like this article right now and share it with friends!&lt;/p&gt;

&lt;p&gt;However, this is much simpler than it may seem to be, at least in theory. I'll give you a straightforward rule about choosing a DBMS:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;if you application works with &lt;strong&gt;unstructured&lt;/strong&gt; data, then choosing NoSQL would be a no-brainer (here I don't necessarily mean MongoDb, since depending on your needs, other type of NoSQL databases may fit your app better);&lt;/li&gt;
&lt;li&gt;otherwise, if the data of your application has a &lt;strong&gt;predefined structure&lt;/strong&gt;, then SQL would be a good choice.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That sounds easy, doesn't it?&lt;/p&gt;

&lt;p&gt;Most of web applications work with structured data (arguably). But before executing that &lt;code&gt;CREATE DATABASE ...&lt;/code&gt; command for your new web app, it may make sense to consider a couple of factors.&lt;/p&gt;

&lt;p&gt;First of all, initial maintenance costs of SQL-based systems are higher than of MongoDb (yes, from here I'm focusing on MongoDb again).&lt;/p&gt;

&lt;p&gt;What do I mean by that? The thing is that due to the fixed data structure of SQL databases you will have to deal with &lt;strong&gt;database migrations&lt;/strong&gt;. This means adding an entire step to your CI/CD flow. And this step is not fool-proof at all: remember that your app will eventually work with production data.&lt;/p&gt;

&lt;p&gt;On the other hand, however, SQL databases (if used correctly, of course) will definitely make your data more stable, e.g. they will eventually help to reduce the number of bugs.&lt;/p&gt;

&lt;p&gt;This means that the SQL vs MongoDb stuff is a classic &lt;strong&gt;stability-flexibility tradeoff&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;At this point I may suppose that you probably expected a more exact advice when you read the title of this article. Giving advices is easy, that's why I won't do that :)&lt;/p&gt;

&lt;p&gt;Instead, I can tell you about my own approach of selecting a DBMS.&lt;/p&gt;

&lt;p&gt;In most cases (if not always) I work with web apps of structured data, I have mentioned something like that above. However, I often choose MongoDb for such apps, and not only because of its greater flexibility. The thing is that remote MongoDb storage services are generally more affordable than SQL, which is especially beneficial for toy projects and startups.&lt;/p&gt;

&lt;p&gt;But ultimately I don't like asking the question: &lt;em&gt;"Which DBMS should I marry my app with?"&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Instead, I ask: &lt;em&gt;"What if I choose wrong DBMS now, and how can I fix this mistake in the future?"&lt;/em&gt; (remember that humans are bad in predicting the future?).&lt;/p&gt;

&lt;p&gt;So, to mitigate this risk, there are a couple of good ORMs we can apply, for example, Prisma. If at some point you decide to switch from SQL to MongoDb or vice versa, Prisma will help you to reduce costs of such transition.&lt;/p&gt;

&lt;p&gt;And this is basically it what I think about the SQL vs MongoDb problem. Did I miss something? Write in comments! And don't forget to follow me so you don't miss out future articles!&lt;/p&gt;

&lt;p&gt;Don't stop coding, don't stop growing!&lt;/p&gt;

</description>
      <category>sql</category>
      <category>mongodb</category>
      <category>webdev</category>
      <category>tooling</category>
    </item>
    <item>
      <title>TypeScript vs vanilla JavaScript for your next project</title>
      <dc:creator>Anton Bahurinsky</dc:creator>
      <pubDate>Tue, 22 Feb 2022 08:45:36 +0000</pubDate>
      <link>https://dev.to/antoncodes/typescript-vs-vanilla-javascript-for-your-next-project-37l3</link>
      <guid>https://dev.to/antoncodes/typescript-vs-vanilla-javascript-for-your-next-project-37l3</guid>
      <description>&lt;p&gt;While choosing between TypeScript and vanilla JavaScript for a web application it is important to understand that all web applications work with &lt;strong&gt;data&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In case when your the data structure of your application is not fixed, then TypeScript would be an overkill for such application.&lt;/p&gt;

&lt;p&gt;But most of web applications work with data that has a &lt;strong&gt;strictly defined structure&lt;/strong&gt; (of course the structure evolves during the app lifetime, but this still doesn't change the game rules).&lt;/p&gt;

&lt;p&gt;In such case applying TypeScript to you app will significantly &lt;strong&gt;improve the quality of your code&lt;/strong&gt;. And that comes with almost no additional maintenance cost, since most of modern development tools offer great integration with TypeScript.&lt;/p&gt;

&lt;p&gt;What is your opinion about this? If you agree with me, leave your reaction to this post! If not, comment your objections down below!&lt;/p&gt;

&lt;p&gt;And remember: don't stop coding, don't stop growing! &lt;/p&gt;

</description>
      <category>javascript</category>
      <category>typescript</category>
      <category>programming</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Why choosing right development tools is so important</title>
      <dc:creator>Anton Bahurinsky</dc:creator>
      <pubDate>Mon, 21 Feb 2022 10:13:52 +0000</pubDate>
      <link>https://dev.to/antoncodes/why-choosing-right-development-tools-is-so-important-no</link>
      <guid>https://dev.to/antoncodes/why-choosing-right-development-tools-is-so-important-no</guid>
      <description>&lt;p&gt;&lt;em&gt;TL;DR: This is a story from my own professional experience about how picking wrong libraries, frameworks, infrastructure services and development approaches causes damages to a web application, thus doubling time and cost of its development.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It was a brand new e-commerce project with a fresh business idea, which involved development of a web application. The stakeholders were two ladies, and one of them has known a guy called Thomas, who is a programmer. Therefore, they invited him to the project as a lead developer (CTO is you will).&lt;/p&gt;

&lt;p&gt;At that time I was working as a React front-end developer in the same company where Thomas did. So when he offered me a post of the front-end developer in the new project, I gladly accepted it.&lt;/p&gt;

&lt;p&gt;Thomas is a cool buddy, but, to my surprise, he had significantly less experience in web than I did (his specialty has been robotics). But his level of influence and responsibility in the project was far greater than mine (no surprise though, unlike me, Thomas had his skin in the game).&lt;/p&gt;

&lt;p&gt;So this is where the things became "interesting". BUT, before blaming others, I want to highlight my own programming mistakes in that project. So let's finally get started!&lt;/p&gt;

&lt;h2&gt;
  
  
  Front-end
&lt;/h2&gt;

&lt;p&gt;And we'll start from the client part of the web application. Since, as I've already said, I was a front-end developer, this is where I did have power of taking technical decisions.&lt;/p&gt;

&lt;p&gt;Since the very beginning we chose &lt;strong&gt;React&lt;/strong&gt; as the primary framework for our front-end. Maybe it was because humans are bad in predicting the future, or maybe I just wasn't smart enough, but I didn't bother with selecting a React platform and simply went with &lt;strong&gt;create-react-app&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;After some time, when our front-end grew a bit, it became clear that there is a lot of routing logic (of course, our e-commerce application was never meant to be a one-pager!). I then understood that it would be way more efficient to go with something like Next.js to handle that heavy routing. Luckily, the &lt;strong&gt;Reach Router&lt;/strong&gt; I applied to the front-end was good enough to satisfy our routing needs.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(As an excuse at this point I can say that although create-react-app might not be the best choice for our front-end, it was working just fine. With some extra political will it wouldn't be too hard to move it to Next.js, and it would neither be necessary to rewrite the Reach Router stuff right away. We'd be able to do that gradually.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The next topic was the user interface. No surprise that it was my job to convert that beautiful UI sketches our designer provided to us into responsive and cross-browser markup. It was time to choose a UI library, and I went with &lt;strong&gt;Bootstrap&lt;/strong&gt;. Everything was going good, I was developing the markup page after page, section after section. I even started to override the default Bootstrap theme to match our specific design.&lt;/p&gt;

&lt;p&gt;Then it was time for dynamic elements, such as toggleable sidebar menu for mobile screens, for example. The truth is that when it comes to dynamic UI elements, you almost end up with something React-specific like &lt;strong&gt;Material UI&lt;/strong&gt; instead of generic Bootstrap. Thus, I introduced another dependency to our front-end...&lt;/p&gt;

&lt;p&gt;Till that moment a lot of markup code had already been written in Bootstrap. What I could do was to declare all the Bootstrap code as legacy and to forbid the team to write new code on Bootstrap. The long-term plan was to gradually replace the Bootstrap code with Material UI and eventually remove the Bootstrap dependency. It never happened, but only because there were a lot of more serious problems in the project than the Bootstrap vs Material UI stuff, so nobody &lt;del&gt;really cared about&lt;/del&gt; noticed that.&lt;/p&gt;

&lt;p&gt;Finally, another mistake I take 100% responsibility for was not going with &lt;strong&gt;TypeScript&lt;/strong&gt; since the very beginning. The data of our e-commerce application had a very much defined structure, so it was a no-brainer to eventually include TypeScript to our toolchain. Luckily, create-react-app provides an easy way to add TypeScript to an existing application.&lt;/p&gt;

&lt;p&gt;And you have already guessed what I decided to do with previously written vanilla JavaScript code: declared it as legacy and started a campaign of gradual replacing that code with Typescript. And yeah, I can confidently claim that most of the vanilla JavaScript code has been replaced with TypeScript till the moment when I left the project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Back-end
&lt;/h2&gt;

&lt;p&gt;Now it's time for some hardcore }:)&lt;/p&gt;

&lt;p&gt;At the beginning of the development of our project there were only two people in the dev team: our tech lead Thomas and me. Early on we split our coding responsibilities by a simple principle: front-end for me, back-end for him. Despite his not so rich experience with web, Thomas has had some knowledge of &lt;strong&gt;AWS&lt;/strong&gt;, so it was a logical step for him to pick AWS as a back-end infrastructure for our project.&lt;/p&gt;

&lt;p&gt;Initially Thomas didn't share the details about our back-end with me, nor I thought that was necessary. In fact, I trusted his back-end skills.&lt;/p&gt;

&lt;p&gt;But what was happening at that stage was while I was successfully completing my front-end milestones, I was periodically hearing Thomas complaining about his struggles with the back-end part. And not because of laziness at all: it became usual for me watching his red eyes craving to close during our video calls. But I still had no idea what was going on with back-end. What actually happened was that Thomas sold his soul to &lt;strong&gt;AWS Amplify&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Not to blame him though. In fact, all of us must pass through this circle of hell due to the lack of experience. I just did that somewhat earlier than him.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;However, I still didn't rush myself to take a look at the back-end. So Thomas took a decision to involve more people to the project. Thus, two new men joined our team: Alex and Barry.&lt;/p&gt;

&lt;p&gt;I have known Alex from another project where we used to work together, so he responded to my invitation for a position of the second front-end developer in the project. Actually I won't say much about Alex, he must be the wisest person in our team because he's the only one I can't blame in anything :D&lt;/p&gt;

&lt;p&gt;The tough guy Barry had always had in Thomas's eyes a reputation of a super savvy back-end dev and (what will become important later on) a god of Google Cloud Platform.&lt;/p&gt;

&lt;p&gt;In the meanwhile, I realized that our application was becoming brittle without proper quality assurance. It became clear that it was impossible to write meaningful end-to-end tests on &lt;strong&gt;Cypress&lt;/strong&gt; without integration with real back-end. This is when I went out of my front-end capsule and faced the AWS Amplify devil.&lt;/p&gt;

&lt;p&gt;The first thing I didn't like was that all our back-end logic was split into modules (product, order, user etc) so each module was deployed to an &lt;strong&gt;AWS Lambda&lt;/strong&gt;. It was still not that bad since each of the Lambda modules was written in plain Express (although even then there was no acceptable way to run the back-end logic on localhost).&lt;/p&gt;

&lt;p&gt;Then Barry suddenly announced: &lt;em&gt;"We are now going to write our back-end code in &lt;strong&gt;NestJS&lt;/strong&gt;!"&lt;/em&gt;. I didn't have anything against NestJS, but I was wondering how they were going to split a monolithic NestJS app into Lambdas, as we had it in Express.&lt;/p&gt;

&lt;p&gt;Even though it resulted to be only one Lambda for the entire NestJS server, maybe I don't understand something, but man, &lt;em&gt;functions are not meant for that&lt;/em&gt;! Not to mention that it became much harder to run the server on a local machine.&lt;/p&gt;

&lt;p&gt;This is when I started to talk about changing our back-end infrastructure. Thomas wanted to stay within AWS, but we couldn't find a better deployment method for the NestJS back-end on the platform (write in comments your furious objections to this right now!). So I told the team: &lt;em&gt;"It is not going to perform well this way. If you want to stay with Amplify so much I vote for sticking with Express for back-end instead of NestJS."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Right below that Slack comment of mine Alex wrote: &lt;em&gt;"I have worked with NestJS, it is awesome! I vote for NestJS!"&lt;/em&gt;. Sorry, Alex, I lied a couple of paragraphs above, I'm still blaming you :)&lt;/p&gt;

&lt;p&gt;But that was not the only problem with AWS Amplify. &lt;em&gt;Because Amplify's primary evil is its heavy configuration&lt;/em&gt;. We ended up having more configuration code committed in our repository that the feature code. We were spending more time f@cking with the configuration than actually developing things. And we still could not make the Cypress tests running within Amplify, I am not even sure if the TypeScript decorators stuff of NestJS eventually worked...&lt;/p&gt;

&lt;p&gt;Alongside with Amplify, another evil thing of AWS is &lt;strong&gt;DynamoDB&lt;/strong&gt;. And I'm not even talking about its vendor lock-in model. The DynamoDB API itself is probably the most disgusting API I've even seen.&lt;/p&gt;

&lt;p&gt;(What I did like in AWS, however, is &lt;strong&gt;Cognito&lt;/strong&gt;. It is OAuth2 compliant, its SDK supports both in-browser login as well as popup/redirect to the authorization server (see OAuth 2.0 specs). And, unlike my favourite Auth0, it doesn't send plain user password via the network!)&lt;/p&gt;

&lt;p&gt;Threatened by all this AWS stuff, I started to look for better alternatives. Thomas had always wanted to stay within a single cloud platform for not having to bother with too many accounts. Long story short, between GCP and Azure I chose the latter due to some factors that are beyond this story since we never applied Azure to our project.&lt;/p&gt;

&lt;p&gt;What is important is that I had no doubts that switching to Azure will benefit our project, and was trying to explain that to Thomas. I managed to create an Azure account, deployed our front-end there so it was partially working (after all, I spent only 1 evening for that).&lt;/p&gt;

&lt;p&gt;Thomas refused to consider all that, he didn't want to take such risk. As he told me some months later: &lt;em&gt;"I thought you were just a front-end guy. I thought you didn't know how to do all that, otherwise I wouldn't be so stubborn."&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Thomas, however, was not the only resistant one. The mastermind Barry also continued to struggle with Amplify, despite his experience in GCP. As for me, during the next 1.5 months there was enough front-end tasks for me which allowed me to distance from the back-end for this time.&lt;/p&gt;

&lt;p&gt;After the mentioned 1.5 months passed, I see Thomas posting in Slack: &lt;em&gt;"Barry has moved our project to &lt;strong&gt;Google Cloud Platform&lt;/strong&gt;, team get ready for a group call where he'll explain us about the new infrastructure."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Oh really? So when I was talking about switching the infrastructure, nobody wanted to listen, but when Barry on his own took the decision to move to GCP, it has suddenly become welcomed? What that means, guys, is that eventually I was right, and you just wasted 1.5 months of the project time. I took that personally (not proud of that though), and told Thomas that I was leaving the project. Thomas, however, convinced me to stay.&lt;/p&gt;

&lt;p&gt;By staying in the project, I agreed to the new GCP rules. Barry took care of all the deployment and infrastructure processes. Our NestJS server could now be run locally. Nevertheless, after getting known with GCP, I understood that I was right in choosing Azure.&lt;/p&gt;

&lt;p&gt;We used &lt;strong&gt;Firebase Authentication&lt;/strong&gt; to work with users, and it worked just fine. I was even able to perform a fancy trick with it in order to close a ticket. The disadvantage of the Firebase Authentication is that it is not OAuth-compliant, which ultimately means vendor lock-in for a project. However, that didn't affect our development work.&lt;/p&gt;

&lt;p&gt;The things were harder with &lt;strong&gt;Firestore&lt;/strong&gt;. Similarly to DynamoDB, it is a proprietary DBMS which means what? Right, the vendor lock-in. And this time it made its impact on our work. Firestore has its own SDK for Node.js with its API, which is, however, not as horrible as the one of DynamoDB. Still, due to being proprietary, Firestore doesn't have decent development tools (e.g. ORMs) to simplify developers' lives. You can even agrue with me right now claiming that there are ORMs for Firestore. But they definitely can't be as good as the ones targeting open DBMS like MongoDb or MySQL (let's take Prisma, for example).&lt;/p&gt;

&lt;p&gt;Another consequence of using Firestore is having to run the &lt;strong&gt;Firebase Emulators&lt;/strong&gt; on a local machine, since it was the only way to work with the proprietary Firestore locally during our NestJS server development. However, the emulators simulate not just Firestore, but other Firebase services as well. And I have to admit that in case of the Authentication it was quite convenient to work with it locally, unlike it would be possible with Auth0.&lt;/p&gt;

&lt;p&gt;There were a couple of other services applied to our project, like mailing or file storage. I can't judge anything about those since I almost didn't work on those parts.&lt;/p&gt;

&lt;p&gt;Now, some of you might argue with me since many paragraphs ago: if we were developing an e-commerce application, why the hell did we develop a custom server instead of applying something like headless BigCommerce or Shopify, or at least leveraging Vendure? And you are absolutely damn right! Moreover, I'm taking my part of responsibility for this counter-productive decision. However, if we did that right since the beginning, would we now have such an extensive overview of the popular development tools? ;)&lt;/p&gt;

&lt;p&gt;And you, what is your experience in selecting right or wrong tools for web development? Share your stories in the comments!&lt;/p&gt;

&lt;p&gt;Also, don't forget to leave your reaction to this article and hit that "Follow" button!&lt;/p&gt;

&lt;p&gt;Don't stop coding, don't stop growing!&lt;/p&gt;

</description>
      <category>programming</category>
      <category>webdev</category>
      <category>tooling</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How to design the architecture of your next web app?</title>
      <dc:creator>Anton Bahurinsky</dc:creator>
      <pubDate>Fri, 18 Feb 2022 11:26:10 +0000</pubDate>
      <link>https://dev.to/antoncodes/how-to-design-the-architecture-of-your-next-web-app-4bf0</link>
      <guid>https://dev.to/antoncodes/how-to-design-the-architecture-of-your-next-web-app-4bf0</guid>
      <description>&lt;p&gt;So you've got a new contract about developing a brand new web application, your client has provided the technical requirements to you. You have also chosen the development stack for you future programming masterpiece: e.g. React for the client, Node.js for the server, perhaps a DBMS etc.&lt;/p&gt;

&lt;p&gt;Now your task is simple: &lt;strong&gt;not to kill the web application before it is even born&lt;/strong&gt; by avoiding mistakes in critical design decisions.&lt;/p&gt;

&lt;p&gt;So where to start this process?&lt;/p&gt;

&lt;p&gt;This first thing you must understand at this point is that the primary object your web application works with is &lt;strong&gt;data&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The data usually has its predefined structure. For example, if you are developing a new e-commerce application, you know that there will be products, cart items, orders and so on, and each product, for example, will have its title, price, description, image list etc.&lt;/p&gt;

&lt;p&gt;This is how you usually define the structure of your data, and this is where it gets split into &lt;strong&gt;entities&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Thus, defining data entities is the first step of designing the architecture of your future application, since an entity within the app defines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a module on the React client;&lt;/li&gt;
&lt;li&gt;a module on the Node.js server;&lt;/li&gt;
&lt;li&gt;a table/collection in the database;&lt;/li&gt;
&lt;li&gt;an end-to-end test (manual or automated).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All these compose a logical &lt;strong&gt;module&lt;/strong&gt; (or feature) of your application. So now, when you have defined your first module, you know where and how to start the development process.&lt;/p&gt;

&lt;p&gt;It is also worth noting that data entities (and respectively the modules) may depend on each other. Therefore, defining &lt;strong&gt;dependencies&lt;/strong&gt; between modules (and avoiding circular dependencies, of course) will help you to prioritize development tasks. And if you have a team of developers, this will also help you to split the tasks between them.&lt;/p&gt;

&lt;p&gt;Hope this article was useful for you. Give a like to this post and hit that "Follow" button!&lt;/p&gt;

&lt;p&gt;Don't stop coding, don't stop growing!&lt;/p&gt;

</description>
      <category>programming</category>
      <category>webdev</category>
      <category>node</category>
      <category>react</category>
    </item>
  </channel>
</rss>
