<?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: The Latino CTO</title>
    <description>The latest articles on DEV Community by The Latino CTO (@raulcornejo).</description>
    <link>https://dev.to/raulcornejo</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%2F1282207%2F5ca2b426-b1ee-4d1d-afe4-754a911c6b03.jpg</url>
      <title>DEV Community: The Latino CTO</title>
      <link>https://dev.to/raulcornejo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/raulcornejo"/>
    <language>en</language>
    <item>
      <title>NestJS API Versioning: Supercharging Our CICD</title>
      <dc:creator>The Latino CTO</dc:creator>
      <pubDate>Fri, 23 Feb 2024 10:07:03 +0000</pubDate>
      <link>https://dev.to/raulcornejo/nestjs-api-versioning-supercharging-our-cicd-1fb7</link>
      <guid>https://dev.to/raulcornejo/nestjs-api-versioning-supercharging-our-cicd-1fb7</guid>
      <description>&lt;p&gt;Joining a startup from its inception was thrilling. From architecting frameworks to coding frontends and backends, each day brought new challenges. However, as clients came on board, so did a flood of bugs with every release. Hot-fixes and rollbacks became routine despite rigorous testing.&lt;/p&gt;

&lt;p&gt;Reflecting on our practices, a pattern emerged: constant modifications to existing code. This approach was unsustainable for a startup with limited time and resources.&lt;/p&gt;

&lt;p&gt;How do we minimise constant modifications?&lt;/p&gt;

&lt;p&gt;Instead of endlessly modifying tested code, we focused on adding new features without disrupting the existing framework. This shift in mindset led to streamlined development and sustainable growth.&lt;/p&gt;

&lt;p&gt;By decoupling new functionalities from existing code, we minimised disruptions and maximised efficiency. This approach empowered our team to adapt swiftly to client needs.&lt;/p&gt;

&lt;p&gt;To tackle our challenges, I proposed a gradual approach: implementing API versioning. This simple change paved the way for safer feature delivery using tools like Feature Flags and A/B testing.&lt;/p&gt;

&lt;h3&gt;
  
  
  NestJS Versioning
&lt;/h3&gt;

&lt;p&gt;NOTE: You can skip reading and go directly to the code and inspect it here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/RaulCornejo/tutorials/tree/main/nestjs-version-example"&gt;https://github.com/RaulCornejo/tutorials/tree/main/nestjs-version-example&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Assuming you took a look at the github repository, you will be able to see the &lt;code&gt;app.controller.ts&lt;/code&gt; which contains 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="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;/api&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;AppController&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;appService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AppService&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;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;/greetings&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&gt;getHello&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getHello&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;You can start the project by running&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn start:dev
or
npm start:dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll see something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;10:36:08 PM] Starting compilation &lt;span class="k"&gt;in &lt;/span&gt;watch mode...

&lt;span class="o"&gt;[&lt;/span&gt;10:36:14 PM] Found 0 errors. Watching &lt;span class="k"&gt;for &lt;/span&gt;file changes.

&lt;span class="o"&gt;[&lt;/span&gt;Nest] 24054  - 02/23/2024, 10:36:15 PM     LOG &lt;span class="o"&gt;[&lt;/span&gt;NestFactory] Starting Nest application...
&lt;span class="o"&gt;[&lt;/span&gt;Nest] 24054  - 02/23/2024, 10:36:15 PM     LOG &lt;span class="o"&gt;[&lt;/span&gt;InstanceLoader] AppModule dependencies initialized +16ms
&lt;span class="o"&gt;[&lt;/span&gt;Nest] 24054  - 02/23/2024, 10:36:15 PM     LOG &lt;span class="o"&gt;[&lt;/span&gt;RoutesResolver] AppController &lt;span class="o"&gt;{&lt;/span&gt;/api&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;version: Neutral&lt;span class="o"&gt;)&lt;/span&gt;: +26ms
&lt;span class="o"&gt;[&lt;/span&gt;Nest] 24054  - 02/23/2024, 10:36:15 PM     LOG &lt;span class="o"&gt;[&lt;/span&gt;RouterExplorer] Mapped &lt;span class="o"&gt;{&lt;/span&gt;/api/greetings, GET&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;version: 1,Neutral&lt;span class="o"&gt;)&lt;/span&gt; route +3ms
&lt;span class="o"&gt;[&lt;/span&gt;Nest] 24054  - 02/23/2024, 10:36:15 PM     LOG &lt;span class="o"&gt;[&lt;/span&gt;RouterExplorer] Mapped &lt;span class="o"&gt;{&lt;/span&gt;/api/greetings, GET&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;version: 2&lt;span class="o"&gt;)&lt;/span&gt; route +0ms
&lt;span class="o"&gt;[&lt;/span&gt;Nest] 24054  - 02/23/2024, 10:36:15 PM     LOG &lt;span class="o"&gt;[&lt;/span&gt;NestApplication] Nest application successfully started +4ms

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

&lt;/div&gt;



&lt;p&gt;There's an example route called &lt;code&gt;getHello&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You can send a get request to it using curl/postman whatever you like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;raul@local:~|⇒  curl http://localhost:3000/api/greetings
Hello World!%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It responds with a &lt;code&gt;Hello World!&lt;/code&gt; string.&lt;/p&gt;

&lt;p&gt;Great!&lt;/p&gt;

&lt;h3&gt;
  
  
  Imagine this scenario:
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;The "greetings" endpoint has been functioning flawlessly for ages. Suddenly, a request arises: the need to return greetings in both English and Spanish.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One of the recurring challenges we faced was the constant need to tweak endpoints and controllers to accommodate new functionalities. This perpetual cycle not only increased the likelihood of introducing bugs but also posed significant challenges despite our rigorous testing and monitoring efforts.&lt;/p&gt;

&lt;p&gt;No matter how thorough our manual and automated testing processes were, there always seemed to be an edge case waiting to disrupt our workflow and cause headaches.&lt;/p&gt;

&lt;p&gt;Most people, including myself, usually think about versioning APIs by adding a version number to the URI such as&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ie: &lt;a href="https://example.com/v1/route"&gt;https://example.com/v1/route&lt;/a&gt; and &lt;a href="https://example.com/v2/route"&gt;https://example.com/v2/route&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;However, that solution didn't quite fit our needs. It entailed additional configuration steps on our API gateway, load balancers, and beyond. Moreover, such versioning mechanisms seemed better suited for major API changes, rather than the incremental updates we required. What we truly sought was a seamless method to introduce new versions of an endpoint, conduct thorough testing, and gracefully retire the old version once validated.&lt;/p&gt;

&lt;p&gt;Interestingly enough, every time I read the Nestjs docs, there's something new that surprises me. This was one of the cases. They have an opinionated way of handling versions and they have multiple options on how to implement versioing:&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;URI Versionining: The version will be passed within the URI of the request (default)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Header Versioning: A custom request header will specify the version&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Media Type Versioning: The Accept header of the request will specify the version&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Custom Versioning: Any aspect of the request may be used to specify the version(s). A custom function is provided to extract said version(s).&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;As we mentioned before, URI versioning wouldn't work for us. All of the other 3 types are quite similar, we decided to use &lt;code&gt;Header Versioning&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NestFactory&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AppModule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./app.module&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;VERSION_NEUTRAL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;VersioningType&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/common&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;bootstrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;NestFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AppModule&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// enable nestjs versioning&lt;/span&gt;
  &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enableVersioning&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;VersioningType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;HEADER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;header&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;route-version&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;defaultVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;VERSION_NEUTRAL&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nf"&gt;bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code above creates a nest app, and then it activates versioning using &lt;code&gt;enableVersioning&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;VersioningType.HEADER&lt;/em&gt; and &lt;em&gt;VERSION_NEUTRAL&lt;/em&gt; are part of &lt;code&gt;@nestjs/common&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;header: 'version',
&lt;/code&gt;&lt;/pre&gt;
&lt;/blockquote&gt;

&lt;p&gt;Can have any value you like. call it &lt;code&gt;route-version&lt;/code&gt;, &lt;code&gt;v&lt;/code&gt;, &lt;code&gt;api-version&lt;/code&gt;, etc. We'll talk about this later.&lt;/p&gt;

&lt;blockquote&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;defaultVersion: [VERSION_NEUTRAL],
&lt;/code&gt;&lt;/pre&gt;
&lt;/blockquote&gt;

&lt;p&gt;We configured the defaultVersion to VERSION_NEUTRAL. Essentially, this means that in the absence of a version header in the request, and when multiple versions are available, NestJS will automatically serve the version designated as VERSION_NEUTRAL.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating a New Version of getHello()
&lt;/h3&gt;

&lt;p&gt;Our AppController has this endpoint&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  @Get('/greetings')
  getHello(): string {
    return this.appService.getHello();
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some might suggest a straightforward solution: just add a parameter to getHello() and adjust appService.getHello() accordingly. &lt;/p&gt;

&lt;p&gt;Yes, theoretically, we could do that. However, I was hesitant to tamper with an endpoint that had undergone thorough testing by both our team and our clients. (Not to mention, our real-world scenarios were far more intricate, making any changes significantly more complex.)&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What can Nestjs Versioning do for us?&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    @Version(['1', VERSION_NEUTRAL])
    @Get('/greetings')
    getHello(): string {
        return this.appService.getHello();
    }

    @Version(['2'])
    @Get('/greetings')
    getHelloV2() {
        // return this.appService.getHelloV2();
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, I added&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a class="mentioned-user" href="https://dev.to/version"&gt;@version&lt;/a&gt;(['1', VERSION_NEUTRAL])&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The &lt;em&gt;&lt;a class="mentioned-user" href="https://dev.to/arul47"&gt;@arul47&lt;/a&gt;  annotation in NestJS informs the framework that the route below it corresponds to version 1. However, the true magic lies in *VERSION_NEUTRAL&lt;/em&gt;. When multiple versions are available for the same route and no version is specified in the request, NestJS will serve the version marked with &lt;em&gt;VERSION_NEUTRAL&lt;/em&gt;. If the request includes a &lt;code&gt;route-version=1&lt;/code&gt; header, then this specific version will also be served.&lt;/p&gt;

&lt;p&gt;Conversely, if the request specifies &lt;em&gt;version 2&lt;/em&gt;, then the route annotated with &lt;code&gt;@Version('2')&lt;/code&gt; will be served. This flexible approach allows for seamless version management and ensures that the appropriate version is delivered based on the request parameters.&lt;/p&gt;

&lt;p&gt;Let's give it a try and call old version 1&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;raul@local:~|⇒  curl http://localhost:3000/api/greetings &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'route-version: 1'&lt;/span&gt;
Hello World!%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now version 2&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;raul@local:~|⇒  curl http://localhost:3000/api/greetings &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'route-version: 2'&lt;/span&gt;
i&lt;span class="s1"&gt;'m version 2%
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, the critical functionality lies in the fact that our frontend comprises numerous instances where api/greetings is called. We're not yet ready to make modifications on the frontend, or perhaps we prefer to implement changes gradually. Therefore, it's imperative that the old implementation of api/greetings continues to be served by the old version until we're prepared for the transition.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:3000/api/greetings
Hello World!%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Exactly. Since we haven't included a version header in the request, and our defaultVersion is set to 1 (our NEUTRAL_VERSION), the old version 1 is automatically served.&lt;/p&gt;

&lt;p&gt;This functionality is incredibly useful. It means we can confidently modify our service, introducing new or partially new methods to accommodate greetings in additional languages, without disrupting the existing functionality.&lt;/p&gt;

&lt;p&gt;Modify &lt;code&gt;/greetings&lt;/code&gt; version 2 so it accepts a language parameter&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    @Version(['2'])
    @Get('/greetings')
    getHelloV2(@Query('language') language): string {
        return this.appService.getHelloV2(language);
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, implement the new functionality on &lt;code&gt;appService.getHelloV2&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  getHelloV2(language: 'en' | 'es'): string {
    // if language is not provided, default to English (using a case)
    switch (language) {
      case 'es':
        return '¡Hola Mundo!';
      default:
        return 'Hello World!';
    }
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Go ahead now and test our new version!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;raul@local:~|⇒  curl http://localhost:3000/api/greetings&lt;span class="se"&gt;\?&lt;/span&gt;language&lt;span class="se"&gt;\=&lt;/span&gt;es &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'route-version: 2'&lt;/span&gt;
¡Hola Mundo!%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great! we can now hit &lt;code&gt;route-version: 2&lt;/code&gt; and we pass the &lt;code&gt;?language=es&lt;/code&gt; query parameter to get a greeting in Spanish!&lt;/p&gt;

&lt;p&gt;If we also call version 2 with &lt;code&gt;language=en&lt;/code&gt; or without a language parameter we should get &lt;code&gt;Hello World&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;raul@local:~|⇒  curl http://localhost:3000/api/greetings&lt;span class="se"&gt;\?&lt;/span&gt;language&lt;span class="se"&gt;\=&lt;/span&gt;en &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'route-version: 2'&lt;/span&gt;
Hello World!%
raul@local:~|⇒  curl http://localhost:3000/api/greetings &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'route-version: 2'&lt;/span&gt;
Hello World!%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And finally, what if we don't specify a version? or if we specify version 1?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;raul@local:~|⇒  curl http://localhost:3000/api/greetings
Hello World!%
raul@local:~|⇒  curl http://localhost:3000/api/greetings &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'route-version: 1'&lt;/span&gt;
Hello World!%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even better, if someone accidentally calls version 1 (with or without headers) and passes a language parameter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;raul@local:~|⇒  curl http://localhost:3000/api/greetings&lt;span class="se"&gt;\?&lt;/span&gt;language&lt;span class="se"&gt;\=&lt;/span&gt;en &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'route-version: 1'&lt;/span&gt;
Hello World!%
raul@local:~|⇒  curl http://localhost:3000/api/greetings&lt;span class="se"&gt;\?&lt;/span&gt;language&lt;span class="se"&gt;\=&lt;/span&gt;en
Hello World!%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since both cases get served by version 1, the greeting is still always &lt;code&gt;Hello World!&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Indeed, simplicity often conceals the brilliance of a solution, and this case is no exception. Embracing different versions of an endpoint, coupled with feature flags, has been a game-changer for us.&lt;/p&gt;

&lt;p&gt;Transitioning from GitLab-flow to GitHub-flow marked a significant milestone in our development journey. We've overcome challenges associated with prolonged deployment cycles and bottlenecks in our pre-production environment. Previously, we encountered delays due to multiple features awaiting testing and debugging. With versioning and feature flags in place, we've streamlined our workflow, enabling more efficient testing and deployment processes.&lt;/p&gt;

&lt;p&gt;Our ability to adapt and learn from our mistakes has propelled us forward. Today, we're operating at a much faster pace, and our releases are more valuable than ever before.&lt;/p&gt;

&lt;p&gt;This topic is a testament to the power of continuous improvement, and I look forward to revisiting it in the future.&lt;/p&gt;

</description>
      <category>nestjs</category>
      <category>node</category>
      <category>cicd</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Do Not Commit Secrets!</title>
      <dc:creator>The Latino CTO</dc:creator>
      <pubDate>Fri, 16 Feb 2024 09:04:27 +0000</pubDate>
      <link>https://dev.to/raulcornejo/do-not-commit-secrets-4p8n</link>
      <guid>https://dev.to/raulcornejo/do-not-commit-secrets-4p8n</guid>
      <description>&lt;p&gt;Picture this: every team I've had the pleasure (or sometimes, the pain) of working with has had one thing in common – someone, somewhere, committing secrets to Git.&lt;/p&gt;

&lt;p&gt;In my recent adventures as a Technical Lead, CTO, and all-around code wizard, I've witnessed firsthand the struggles of distinguishing between public and private keys. And you know what's even trickier? Accidentally spilling those private keys and secrets onto Git for all to see. It's like leaving your front door wide open in the middle of the night and wondering why your house got burgled.&lt;/p&gt;

&lt;p&gt;I've seen .env files with more secrets than my ex-wife and Axios requests spilling the beans on sensitive credentials. &lt;/p&gt;

&lt;p&gt;Now, you might be thinking, "What's the harm? Can't we just commit another version without the credentials?" Ah, if only it were that simple.&lt;/p&gt;

&lt;p&gt;Well, let's conduct a little experiment, shall we? Create a local Git repository (assuming Git is already in your system)&lt;/p&gt;

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

raul@mbp:~|⇒  mkdir fake-git-repo
raul@mbp:~|⇒  cd fake-git-repo
raul@mbp:~/fake-git-repo|⇒  git init
Initialized empty Git repository in /Users/raul/fake-git-repo/.git/
raul@mbp:~/fake-git-repo|master ⇒  touch .env


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

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Add some super secret values to the &lt;em&gt;.env&lt;/em&gt; file
```
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;raul@mbp:~/fake-git-repo|master ⇒  vi .env&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
Add something like this:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AWS_ACCESS_KEY_ID = 'AKJEGCKAQNSGFN2N68''&lt;br&gt;
AWS_SECRET_ACCESS_KEY = 'mC4gRDpo/o+M1uOfTWgdfgqMqYe38eGmt'&lt;br&gt;
~&lt;br&gt;
~&lt;br&gt;
~&lt;br&gt;
:wq&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
Save the changes by typing `wq` (write and quit)

- Now commit those changes! although, you shouldn't!

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

&lt;/div&gt;



&lt;p&gt;raul@mbp:~/fake-git-repo|master⚡ ⇒  git add .&lt;br&gt;
raul@mbp:~/fake-git-repo|master⚡ ⇒  git commit -m "let's commit something we shouldn't commit"&lt;br&gt;
[master (root-commit) 2be5583] let's commit something we shouldn't commit&lt;br&gt;
 1 file changed, 2 insertions(+)&lt;br&gt;
 create mode 100644 .env&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
- Now, for the sake of the story, let's assume you follow a Code Review procedure and someone caught you committing these secrets. They told you it's not safe. You immediately said, "OK! I'll remove them," so you go to `vi`, remove those values from the file, and commit again without the secrets.


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

&lt;/div&gt;



&lt;p&gt;AWS_ACCESS_KEY_ID =&lt;br&gt;
AWS_SECRET_ACCESS_KEY =&lt;br&gt;
~&lt;br&gt;
~&lt;br&gt;
~&lt;br&gt;
:wq&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
- Let's use `git` to double-check our changes before commit

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

&lt;/div&gt;



&lt;p&gt;diff --git a/.env b/.env&lt;br&gt;
index 90912b1..3c569d6 100644&lt;br&gt;
--- a/.env&lt;br&gt;
+++ b/.env&lt;br&gt;
@@ -1,2 +1,2 @@&lt;br&gt;
-AWS_ACCESS_KEY_ID = 'AKJEGCKAQNSGFN2N68''&lt;br&gt;
-AWS_SECRET_ACCESS_KEY = 'mC4gRDpo/o+M1uOfTWgdfgqMqYe38eGmt'&lt;br&gt;
+AWS_ACCESS_KEY_ID =&lt;br&gt;
+AWS_SECRET_ACCESS_KEY =&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
If you pay attention, the lines with a `-` are the ones deleted, and the ones with `+` are the added ones. So we're all good! We removed the values of our super secrets! Yay!

Now, commit the safer .env file.

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

&lt;/div&gt;



&lt;p&gt;raul@mbp:~/fake-git-repo|master⚡ ⇒  git commit -a -m 'removes secrets'&lt;br&gt;
[master 61a83f4] removes secrets&lt;br&gt;
 1 file changed, 2 insertions(+), 2 deletions(-)&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
If you open the file now, secrets are no longer there
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;raul@mbp:~/fake-git-repo|master ⇒  cat .env&lt;br&gt;
AWS_ACCESS_KEY_ID =&lt;br&gt;
AWS_SECRET_ACCESS_KEY =&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
### You're Wrong!

You might believe you're all safe now. But the whole purpose of Git is to keep track of changes and allow us to go back in the history of our files if necessary. Let's investigate the history of our `fake-repository` and `.env` file.

- Use `git log -p -2`.
  - `-p` means patch, and when used along with log, it will show what changed (removed, added, modified).
  - `-2` tells the log to show the latest 2 commits.


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

&lt;/div&gt;



&lt;p&gt;commit 61a83f419b174f0750d7dab56c9e0469b0c27784 (HEAD -&amp;gt; master)&lt;br&gt;
Date:   Fri Feb 16 20:57:26 2024 +1300&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;removes secrets
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;diff --git a/.env b/.env&lt;br&gt;
index 90912b1..3c569d6 100644&lt;br&gt;
--- a/.env&lt;br&gt;
+++ b/.env&lt;br&gt;
@@ -1,2 +1,2 @@&lt;br&gt;
-AWS_ACCESS_KEY_ID = 'AKJEGCKAQNSGFN2N68''&lt;br&gt;
-AWS_SECRET_ACCESS_KEY = 'mC4gRDpo/o+M1uOfTWgdfgqMqYe38eGmt'&lt;br&gt;
+AWS_ACCESS_KEY_ID =&lt;br&gt;
+AWS_SECRET_ACCESS_KEY =&lt;/p&gt;

&lt;p&gt;commit 2be5583e6c94c7b70e0f4232fea5ff1fb13603a8&lt;br&gt;
Date:   Fri Feb 16 20:46:59 2024 +1300&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let's commit something we shouldn't commit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
If you take a deep look, `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` appear twice. But one with a `-` and another with a `+`, meaning that the line with `-` was removed and the one with `+` was added.

So, we could see what was the old value (with our secrets) which we thought we safely removed from the public!

Now, you might ask...why do I care so much about this?


### Real-Life Example from GitHub

If the previous example doesn't seem scary, let's head over to `github.com` and see if secrets are being leaked or not, and why we should be worried.

Here's a direct link for you to search for commits on GitHub public repositories:

[https://github.com/search?q=remove+secrets&amp;amp;type=commits](https://github.com/search?q=remove+secrets&amp;amp;type=commits) 

You'll see a lot of repositories with commits named `remove secrets`


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bxbbspc357f0mlygsrxz.png)

If you click on some of them, you might be able to see files like this:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aymqcdm0j28gbko3fuu9.png)

That's actually `google cloud computing secrets! I blurred the values, and I hope this person already rotated this certificate or they could be in serious problems having people accessing their Google Cloud services and probably incurring charges.

Look at this other example where someone tried to remove AWS secrets, MySQL DB connection strings, etc.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l50wy58jro6rg3yfav7e.png)

## Summary

I hope this helps people understand why there's a Tech Lead, Senior Dev, etc., asking you not to commit secrets.

I've come to realize we usually tell others what not to do without giving a reason of why it shouldn't be done.

Hopefully, this blog will help me too, to teach some of the developers I work with. And if in the meantime, someone else can learn something, well, you're welcome!









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

&lt;/div&gt;

</description>
      <category>beginners</category>
      <category>github</category>
      <category>git</category>
      <category>secrets</category>
    </item>
  </channel>
</rss>
