<?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>Forem: lajibolala</title>
    <description>The latest articles on Forem by lajibolala (@lajibolala298).</description>
    <link>https://forem.com/lajibolala298</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%2F3858392%2F92bc1f5a-83ae-4349-90ef-206f29b3b759.PNG</url>
      <title>Forem: lajibolala</title>
      <link>https://forem.com/lajibolala298</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/lajibolala298"/>
    <language>en</language>
    <item>
      <title>Deep Technical Breakdown of a CI/CD Pipeline for a JavaScript API (GitLab, Node.js, Docker, Security, and Deployment)</title>
      <dc:creator>lajibolala</dc:creator>
      <pubDate>Fri, 03 Apr 2026 12:02:56 +0000</pubDate>
      <link>https://forem.com/lajibolala298/cicd-pipeline-for-a-flask-api-25aj</link>
      <guid>https://forem.com/lajibolala298/cicd-pipeline-for-a-flask-api-25aj</guid>
      <description>&lt;p&gt;This article provides a detailed technical explanation of a CI/CD pipeline built for a JavaScript API using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitLab CI/CD&lt;/li&gt;
&lt;li&gt;Node.js and Express&lt;/li&gt;
&lt;li&gt;Docker&lt;/li&gt;
&lt;li&gt;Docker Compose&lt;/li&gt;
&lt;li&gt;Jest for testing&lt;/li&gt;
&lt;li&gt;npm audit for dependency security checks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of only showing the pipeline, this guide explains the role of each file and how every part contributes to the delivery workflow.&lt;/p&gt;




&lt;h1&gt;
  
  
  1. Project Architecture Overview
&lt;/h1&gt;

&lt;p&gt;The project follows this structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;project/
├── index.js
├── package.json
├── package-lock.json
├── tests/
│   └── app.test.js
├── Dockerfile
├── compose.yml
└── .gitlab-ci.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each file plays a specific role in development, validation, security, and deployment.&lt;/p&gt;




&lt;h1&gt;
  
  
  2. Application Layer — &lt;code&gt;index.js&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;The application is a small Express API.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&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="nf"&gt;express&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;PORT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;;&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;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;/&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;UrbanHub API&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ok&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="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;/health&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;up&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="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;/config&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;APP_ENV&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dev&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&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="nx"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0.0.0.0&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Server is running on port &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;express()&lt;/code&gt; creates the web application instance.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PORT&lt;/code&gt; is taken from an environment variable or defaults to &lt;code&gt;3000&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/&lt;/code&gt; returns a basic JSON response.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/health&lt;/code&gt; is a health check endpoint.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/config&lt;/code&gt; exposes the current runtime environment.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;0.0.0.0&lt;/code&gt; makes the service reachable from outside the container.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;module.exports = app&lt;/code&gt; allows the application to be imported in test files.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This structure is common in Node.js APIs because it separates the app definition from the server execution logic.&lt;/p&gt;




&lt;h1&gt;
  
  
  3. Dependencies — &lt;code&gt;package.json&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;This file describes the project metadata, scripts, and dependencies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"urbanhub-api"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Simple Express API with CI/CD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"index.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jest"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"express"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^5.0.0"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"devDependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"jest"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^29.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"supertest"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^7.0.0"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;main&lt;/code&gt; defines the application entry point.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;scripts.test&lt;/code&gt; standardizes how tests are run.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;express&lt;/code&gt; is used for the API.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;jest&lt;/code&gt; is the test framework.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;supertest&lt;/code&gt; is used to test HTTP endpoints without starting a real external server.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This file is central to both local development and CI execution.&lt;/p&gt;




&lt;h1&gt;
  
  
  4. Test Layer — &lt;code&gt;tests/app.test.js&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;The tests validate the behavior of the API.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;supertest&lt;/span&gt;&lt;span class="dl"&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="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../index&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;describe&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 tests&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET / should return 200&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="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&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;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;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ok&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;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET /health should return 200&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="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&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;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;/health&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&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;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;supertest&lt;/code&gt; sends requests directly to the Express app.&lt;/li&gt;
&lt;li&gt;The first test validates the root endpoint.&lt;/li&gt;
&lt;li&gt;The second test validates the health endpoint.&lt;/li&gt;
&lt;li&gt;These tests ensure that the service responds correctly before deployment.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In CI/CD, this stage prevents a broken application from reaching deployment.&lt;/p&gt;




&lt;h1&gt;
  
  
  5. Dockerfile — Containerization Explained
&lt;/h1&gt;

&lt;p&gt;The Dockerfile defines how the application image is built.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:20-alpine&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; APP_ENV=production&lt;/span&gt;

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "index.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Line-by-line explanation
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;FROM node:20-alpine&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;Uses a lightweight Node.js image.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;node:20&lt;/code&gt; provides the runtime&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;alpine&lt;/code&gt; keeps the image small and efficient&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;WORKDIR /app&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;Defines &lt;code&gt;/app&lt;/code&gt; as the working directory inside the container.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;COPY package*.json ./&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;Copies &lt;code&gt;package.json&lt;/code&gt; and &lt;code&gt;package-lock.json&lt;/code&gt; first.&lt;/p&gt;

&lt;p&gt;This improves Docker layer caching because dependencies do not need to be reinstalled on every code change.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;RUN npm ci&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;Installs dependencies in a reproducible way.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm ci&lt;/code&gt; is preferred in CI/CD because it strictly follows the lock file and is more deterministic than &lt;code&gt;npm install&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;COPY . .&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;Copies the source code into the image.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;ENV APP_ENV=production&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;Defines the runtime environment variable.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;EXPOSE 3000&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;Documents that the container listens on port 3000.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;CMD ["node", "index.js"]&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;Starts the application when the container runs.&lt;/p&gt;




&lt;h1&gt;
  
  
  6. Docker Compose — &lt;code&gt;compose.yml&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;Docker Compose is used to run the API in a structured way.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;urbanhub-api:latest&lt;/span&gt;
    &lt;span class="na"&gt;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.env&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3000:3000"&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;services&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;Defines the list of services managed by Compose.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;api&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;The service name for the application container.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;image: urbanhub-api:latest&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;Uses the Docker image produced during the build phase.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;env_file&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;Loads environment variables from a dedicated file.&lt;/p&gt;

&lt;p&gt;This helps separate configuration from source code.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;ports&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;Maps port 3000 on the host to port 3000 in the container.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;restart: unless-stopped&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;Restarts the service automatically unless it is manually stopped.&lt;/p&gt;

&lt;p&gt;For a simple API, this is often enough. If the app used a database, more services could be added here.&lt;/p&gt;




&lt;h1&gt;
  
  
  7. CI/CD Pipeline — &lt;code&gt;.gitlab-ci.yml&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;The pipeline is organized into four stages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;stages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;build&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;security&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;deploy&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The execution flow is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Build → Test → Security → Deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives a clear delivery order:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;prepare the app&lt;/li&gt;
&lt;li&gt;validate behavior&lt;/li&gt;
&lt;li&gt;check security&lt;/li&gt;
&lt;li&gt;deploy only if previous stages succeed&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  8. Global Variables
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;APP_DIR&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/home/gitlab-runner/node_api"&lt;/span&gt;
  &lt;span class="na"&gt;NPM_CONFIG_CACHE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$CI_PROJECT_DIR/.npm"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;APP_DIR&lt;/code&gt; defines the target deployment directory on the runner host.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;NPM_CONFIG_CACHE&lt;/code&gt; speeds up package installation by caching dependencies.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These variables make the pipeline cleaner and easier to maintain.&lt;/p&gt;




&lt;h1&gt;
  
  
  9. Build Stage
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;npm ci&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;node -c index.js&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;$CI_COMMIT_BRANCH&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;==&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"develop"'&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;$CI_COMMIT_BRANCH&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;==&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"main"'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;npm ci&lt;/code&gt; installs dependencies.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;node -c index.js&lt;/code&gt; checks JavaScript syntax.&lt;/li&gt;
&lt;li&gt;The stage runs on both &lt;code&gt;develop&lt;/code&gt; and &lt;code&gt;main&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This stage catches basic syntax and installation issues early.&lt;/p&gt;




&lt;h1&gt;
  
  
  10. Test Stage
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;unit_tests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;npm ci&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;npm test&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;$CI_COMMIT_BRANCH&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;==&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"develop"'&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;$CI_COMMIT_BRANCH&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;==&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"main"'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;npm ci&lt;/code&gt; ensures the environment is clean and reproducible.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;npm test&lt;/code&gt; runs the Jest suite.&lt;/li&gt;
&lt;li&gt;The pipeline stops here if tests fail.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the most important validation gate before deployment.&lt;/p&gt;




&lt;h1&gt;
  
  
  11. Security Stage
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;security_scan&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;security&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;npm ci&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;npm audit --audit-level=high&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;$CI_COMMIT_BRANCH&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;==&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"develop"'&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;$CI_COMMIT_BRANCH&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;==&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"main"'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;npm audit&lt;/code&gt; checks project dependencies for known vulnerabilities.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--audit-level=high&lt;/code&gt; focuses on serious issues.&lt;/li&gt;
&lt;li&gt;This introduces a DevSecOps mindset directly into the pipeline.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a more advanced pipeline, this stage could be complemented by container scanning tools such as Trivy.&lt;/p&gt;




&lt;h1&gt;
  
  
  12. Deployment Stage
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;deploy&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo "Deploying into $APP_DIR"&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;if [ ! -d "$APP_DIR/.git" ]; then&lt;/span&gt;
        &lt;span class="s"&gt;git clone "$CI_REPOSITORY_URL" "$APP_DIR"&lt;/span&gt;
      &lt;span class="s"&gt;fi&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cd "$APP_DIR"&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;git checkout main&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;git pull origin main&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;if [ ! -f .env ]; then&lt;/span&gt;
        &lt;span class="s"&gt;cp .env.example .env&lt;/span&gt;
      &lt;span class="s"&gt;fi&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker compose down || &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker build --network=host -t urbanhub-api:latest .&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker compose up -d&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;sleep &lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker ps&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker logs $(docker ps -q --filter "name=api") || &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;curl --fail http://localhost:3000/ || (echo "App not reachable" &amp;amp;&amp;amp; exit 1)&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;$CI_COMMIT_BRANCH&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;==&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"main"'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This stage performs several actions:&lt;/p&gt;

&lt;h4&gt;
  
  
  Clone the project if missing
&lt;/h4&gt;

&lt;p&gt;If the deployment directory does not already contain the Git repository, it is cloned.&lt;/p&gt;

&lt;h4&gt;
  
  
  Pull the latest code
&lt;/h4&gt;

&lt;p&gt;The deployment always runs against the latest &lt;code&gt;main&lt;/code&gt; branch state.&lt;/p&gt;

&lt;h4&gt;
  
  
  Ensure environment configuration exists
&lt;/h4&gt;

&lt;p&gt;If &lt;code&gt;.env&lt;/code&gt; does not exist, it is generated from &lt;code&gt;.env.example&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Stop old containers
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;docker compose down || true&lt;/code&gt; ensures the previous version is stopped cleanly.&lt;/p&gt;

&lt;h4&gt;
  
  
  Rebuild the image
&lt;/h4&gt;

&lt;p&gt;The latest source code is rebuilt into a fresh Docker image.&lt;/p&gt;

&lt;h4&gt;
  
  
  Start the new container
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;docker compose up -d&lt;/code&gt; launches the service in detached mode.&lt;/p&gt;

&lt;h4&gt;
  
  
  Verify deployment
&lt;/h4&gt;

&lt;p&gt;The pipeline checks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;running containers&lt;/li&gt;
&lt;li&gt;container logs&lt;/li&gt;
&lt;li&gt;HTTP accessibility via &lt;code&gt;curl&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This makes deployment not only automatic, but also self-validated.&lt;/p&gt;




&lt;h1&gt;
  
  
  13. Branch-Based Execution Strategy
&lt;/h1&gt;

&lt;p&gt;The pipeline uses branch rules.&lt;/p&gt;

&lt;h3&gt;
  
  
  Build, test, and security
&lt;/h3&gt;

&lt;p&gt;Run on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;develop&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;main&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This allows validation both in integration and stable branches.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deploy
&lt;/h3&gt;

&lt;p&gt;Runs only on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;main&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This protects deployment from accidental pushes on development branches.&lt;/p&gt;

&lt;p&gt;It creates a simple but effective promotion model:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;develop&lt;/code&gt; for validation&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;main&lt;/code&gt; for delivery&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  14. DevSecOps Integration
&lt;/h1&gt;

&lt;p&gt;Security is not treated as an afterthought.&lt;/p&gt;

&lt;p&gt;This pipeline integrates security by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;testing the application before deployment&lt;/li&gt;
&lt;li&gt;auditing dependencies for known vulnerabilities&lt;/li&gt;
&lt;li&gt;isolating the runtime inside containers&lt;/li&gt;
&lt;li&gt;verifying the deployed service automatically&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach reduces the chance of shipping broken or unsafe code.&lt;/p&gt;




&lt;h1&gt;
  
  
  15. Strengths of This Pipeline
&lt;/h1&gt;

&lt;p&gt;This CI/CD implementation provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;clear stage separation&lt;/li&gt;
&lt;li&gt;reproducible dependency installation with &lt;code&gt;npm ci&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;automated syntax validation&lt;/li&gt;
&lt;li&gt;automated API testing&lt;/li&gt;
&lt;li&gt;built-in dependency security audit&lt;/li&gt;
&lt;li&gt;controlled deployment on the main branch&lt;/li&gt;
&lt;li&gt;runtime verification after deployment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is simple, but complete enough to represent a real DevOps workflow.&lt;/p&gt;




&lt;h1&gt;
  
  
  16. Limitations
&lt;/h1&gt;

&lt;p&gt;Like any basic pipeline, this one has some limitations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;no staging environment&lt;/li&gt;
&lt;li&gt;no rollback mechanism&lt;/li&gt;
&lt;li&gt;no container vulnerability scan&lt;/li&gt;
&lt;li&gt;no performance testing&lt;/li&gt;
&lt;li&gt;single-node deployment model&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are acceptable for a lightweight project, but could be extended in a production context.&lt;/p&gt;




&lt;h1&gt;
  
  
  17. Possible Improvements
&lt;/h1&gt;

&lt;p&gt;This pipeline could be improved with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ESLint integration for code quality&lt;/li&gt;
&lt;li&gt;SonarQube or CodeQL for advanced static analysis&lt;/li&gt;
&lt;li&gt;Trivy for Docker image scanning&lt;/li&gt;
&lt;li&gt;staging and production environments&lt;/li&gt;
&lt;li&gt;monitoring and alerting&lt;/li&gt;
&lt;li&gt;rollback support in case of failed deployment&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;This JavaScript CI/CD pipeline demonstrates a complete delivery chain for a Node.js API.&lt;/p&gt;

&lt;p&gt;It combines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;source validation&lt;/li&gt;
&lt;li&gt;automated testing&lt;/li&gt;
&lt;li&gt;dependency security checks&lt;/li&gt;
&lt;li&gt;automated deployment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key strength of this setup is not complexity, but reliability.&lt;/p&gt;

&lt;p&gt;A good pipeline is not the one with the most tools.&lt;/p&gt;

&lt;p&gt;It is the one that delivers software in a predictable, controlled, and trustworthy way.&lt;/p&gt;

</description>
      <category>cicd</category>
      <category>devops</category>
      <category>docker</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
