<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Umair Mirza - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Umair Mirza - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Fri, 23 Oct 2026 15:43:15 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/author/umairmirza/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Catch Security Vulnerabilities in Code Before They Reach Your Pull Requests ]]>
                </title>
                <description>
                    <![CDATA[ Security reviews are most effective when developers receive feedback while the code is still fresh in their minds. Waiting until a pull request, CI build, or penetration test to find exposed credentia ]]>
                </description>
                <link>https://www.freecodecamp.org/news/catch-security-vulnerabilities-code-pull-requests/</link>
                <guid isPermaLink="false">6aa873c5c1f02b411d54db2f</guid>
                
                    <category>
                        <![CDATA[ Security ]]>
                    </category>
                
                    <category>
                        <![CDATA[ DevSecOps ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Git ]]>
                    </category>
                
                    <category>
                        <![CDATA[ SAST ]]>
                    </category>
                
                    <category>
                        <![CDATA[ software development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Umair Mirza ]]>
                </dc:creator>
                <pubDate>Mon, 14 Sep 2026 22:23:01 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/1443bfb4-581b-457b-afc3-7a85dd5fb3be.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Security reviews are most effective when developers receive feedback while the code is still fresh in their minds. Waiting until a pull request, CI build, or penetration test to find exposed credentials and insecure patterns creates unnecessary rework.</p>
<p>A lightweight way to shift security left is to run static application security testing (SAST) locally through Git pre-commit hooks.</p>
<p>In this guide, you'll install the DevSkim CLI, wire it into <code>pre-commit</code> so it lints staged files, add a dedicated secret scanner alongside it, verify the setup with a deliberate failure, and enforce the same checks in CI.</p>
<h3 id="heading-what-well-cover">What We'll Cover:</h3>
<ul>
<li><p><a href="#heading-why-pre-commit-security-scanning">Why Pre-Commit Security Scanning?</a></p>
</li>
<li><p><a href="#heading-what-is-devskim">What Is DevSkim?</a></p>
</li>
<li><p><a href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a href="#heading-create-a-pre-commit-configuration">Create a Pre-Commit Configuration</a></p>
</li>
<li><p><a href="#heading-install-the-git-hook">Install the Git Hook</a></p>
</li>
<li><p><a href="#heading-verify-the-setup-with-a-deliberate-failure">Verify the Setup With a Deliberate Failure</a></p>
</li>
<li><p><a href="#heading-example-catching-a-hardcoded-secret">Example: Catching a Hardcoded Secret</a></p>
</li>
<li><p><a href="#heading-keep-hooks-fast-and-focused">Keep Hooks Fast and Focused</a></p>
</li>
<li><p><a href="#heading-handling-false-positives">Handling False Positives</a></p>
</li>
<li><p><a href="#heading-add-security-checks-to-ci-too">Add Security Checks to CI Too</a></p>
</li>
<li><p><a href="#heading-practical-rollout-tips">Practical Rollout Tips</a></p>
</li>
<li><p><a href="#heading-final-thoughts">Final Thoughts</a></p>
</li>
</ul>
<h2 id="heading-why-pre-commit-security-scanning">Why Pre-Commit Security Scanning?</h2>
<p>Pre-commit hooks run automatically before Git creates a commit. They're useful for catching hardcoded passwords, tokens, and connection strings, insecure cryptographic patterns, weak input validation, risky file or process handling, and other language-specific security anti-patterns.</p>
<p>The goal isn't to replace CI security scanning or manual reviews. It's to give developers fast, actionable feedback at the earliest practical point.</p>
<p>A healthy model looks like this:</p>
<pre><code class="language-text">Developer workstation
  -&gt; Pre-commit security checks
  -&gt; Pull request review
  -&gt; CI/CD SAST and dependency scanning
  -&gt; Runtime monitoring and vulnerability management
</code></pre>
<h2 id="heading-what-is-devskim">What Is DevSkim?</h2>
<p>DevSkim is a security linter created by Microsoft. It ships as IDE extensions and a cross-platform CLI, and it flags insecure coding patterns using a configurable rule set.</p>
<p>It works well as a developer-facing guardrail because it scans files quickly and explains why a pattern may be risky. Its rules cover dangerous API usage, weak cryptography, insecure deserialization, weak TLS or certificate validation, and potential command injection risks.</p>
<p>One distinction matters before you rely on it: DevSkim is a security linter, not a secret scanner. It bundles a few generic credential rules, but those are regex patterns aimed at long, token-like values.</p>
<p>Teams typically pair DevSkim with a dedicated secret-scanning tool such as <a href="https://github.com/gitleaks/gitleaks">Gitleaks</a>, <a href="https://github.com/Yelp/detect-secrets">detect-secrets</a>, or <a href="https://github.com/trufflesecurity/trufflehog">TruffleHog</a>. Gitleaks and detect-secrets both publish official <code>pre-commit</code> hooks, so either slots into this workflow easily. This guide uses Gitleaks.</p>
<p>As with any security scanner, findings need context. A warning isn't automatically a vulnerability, but it's worth reviewing before the code moves downstream.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>You'll need Git, Python 3, and the .NET SDK, which DevSkim's CLI is distributed through.</p>
<p>Install <code>pre-commit</code>:</p>
<pre><code class="language-bash">pip install pre-commit
</code></pre>
<p>Install the DevSkim CLI as a .NET global tool:</p>
<pre><code class="language-bash">dotnet tool install --global Microsoft.CST.DevSkim.CLI
</code></pre>
<p>If you prefer not to install the .NET SDK, platform-specific binaries are available on the <a href="https://github.com/microsoft/DevSkim/releases">DevSkim releases page</a>.</p>
<p>Confirm both installations in a new terminal, so it picks up the updated <code>PATH</code>:</p>
<pre><code class="language-bash">pre-commit --version
devskim --version
</code></pre>
<p>For inline feedback while you type, also install the <a href="https://marketplace.visualstudio.com/items?itemName=MS-CST-E.vscode-devskim">DevSkim VS Code extension</a>. The CLI is what the Git hook uses.</p>
<h2 id="heading-create-a-pre-commit-configuration">Create a Pre-Commit Configuration</h2>
<p>DevSkim's CLI takes a single source path after <code>-I</code>, while <code>pre-commit</code> appends every staged filename to the command it runs. Passing <code>devskim analyze</code> directly therefore breaks as soon as a commit touches more than one file.</p>
<p>The simplest fix is a small wrapper that invokes DevSkim once per filename. Create <code>scripts/run-devskim.py</code> in your repository:</p>
<pre><code class="language-python">import subprocess
import sys

exit_code = 0

for filename in sys.argv[1:]:
    result = subprocess.run(["devskim", "analyze", "-I", filename])
    exit_code = exit_code or result.returncode

sys.exit(exit_code)
</code></pre>
<p>Now create <code>.pre-commit-config.yaml</code> at the root of your repository:</p>
<pre><code class="language-yaml">repos:
  - repo: local
    hooks:
      - id: devskim
        name: DevSkim security lint
        entry: python scripts/run-devskim.py
        language: system
        types_or: [python, javascript, typescript, json, yaml]
        pass_filenames: true

  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.30.1
    hooks:
      - id: gitleaks
</code></pre>
<p>The first hook runs DevSkim against the staged files that match the listed types. The second adds Gitleaks for the secret detection DevSkim isn't designed to handle.</p>
<p>If you would rather keep things simpler and scan the whole repository on every commit, drop the wrapper and use a single fixed path instead:</p>
<pre><code class="language-yaml">      - id: devskim
        name: DevSkim security lint
        entry: devskim analyze -I .
        language: system
        pass_filenames: false
</code></pre>
<p>That version is easier to reason about, but it gets slower as the repository grows. Staged-file scanning is the better default once a project has real history.</p>
<h2 id="heading-install-the-git-hook">Install the Git Hook</h2>
<p>Run this once from the repository root:</p>
<pre><code class="language-bash">pre-commit install
</code></pre>
<p>Now, every <code>git commit</code> triggers the configured checks.</p>
<p>You can run the hooks manually against the entire codebase:</p>
<pre><code class="language-bash">pre-commit run --all-files
</code></pre>
<p>That's particularly useful when introducing the tool to an existing project. Expect initial findings. Older repositories often contain patterns that predate current security standards.</p>
<h2 id="heading-verify-the-setup-with-a-deliberate-failure">Verify the Setup With a Deliberate Failure</h2>
<p>Before trusting the hook, you'll want to confirm that it actually blocks something. Create a file called <code>insecure-example.js</code> with a weak hashing call:</p>
<pre><code class="language-javascript">const crypto = require("crypto");

const hash = crypto.createHash("md5").update("password").digest("hex");
</code></pre>
<p>Stage it and attempt a commit:</p>
<pre><code class="language-bash">git add insecure-example.js
git commit -m "Test security hooks"
</code></pre>
<p>The commit should be rejected. <code>pre-commit</code> prints a status line per hook, followed by the failing hook's ID, its exit code, and the scanner's own findings:</p>
<pre><code class="language-text">DevSkim security lint....................................................Failed
- hook id: devskim
- exit code: 1

[DevSkim output listing the weak hash finding appears here]
</code></pre>
<p>DevSkim's finding text includes a rule ID, a severity label, and the file and line, and the exact formatting varies by version and ruleset. What matters is the non-zero exit code and the blocked commit.</p>
<p>If the hook passes unexpectedly, run the scan directly to isolate the problem:</p>
<pre><code class="language-bash">devskim analyze -I insecure-example.js
</code></pre>
<p>If that reports the finding but the hook does not, the issue is in your <code>.pre-commit-config.yaml</code> rather than in DevSkim.</p>
<h2 id="heading-example-catching-a-hardcoded-secret">Example: Catching a Hardcoded Secret</h2>
<p>Secrets are where the linter and the secret scanner divide. DevSkim's generic credential rules are regex-based and target long, token-like values, so a readable placeholder string will usually slip past them. A value shaped like a real key is a more realistic test:</p>
<pre><code class="language-javascript">const apiKey = "4f2a9c1e7b6d3a8f0c5e9b2d7a41c6";
</code></pre>
<p>Stage the change and run the hooks:</p>
<pre><code class="language-bash">git add config.js
pre-commit run
</code></pre>
<p>This is the case the secret scanner is built for. Gitleaks applies entropy and pattern checks tuned for credentials, so it's the hook you should expect to block a value like this, and its status line will report a failure and a non-zero exit code. Treat DevSkim's result here as a bonus rather than the control you rely on, which is exactly why the two tools sit side by side.</p>
<p>The fix is to move the value out of source control:</p>
<pre><code class="language-javascript">const apiKey = process.env.PAYMENT_API_KEY;
</code></pre>
<p>The real value should be stored in an approved secret manager, such as Azure Key Vault, GitHub Actions Secrets, or another managed platform.</p>
<p>This small shift matters. Removing a secret after it has been committed is harder, because Git history, clones, CI logs, and deployment systems may already contain it.</p>
<p>Once you have confirmed both hooks behave as expected, delete the test files. Never use a real credential to test a scanner.</p>
<h2 id="heading-keep-hooks-fast-and-focused">Keep Hooks Fast and Focused</h2>
<p>Developers will bypass slow hooks. A good local security scan should complete quickly and focus on high-confidence findings.</p>
<p>Start by scanning only staged or changed files locally, then reserve broader scans for CI. Block high-severity findings such as exposed secrets and critical insecure API usage first. Lower-confidence findings can be reported for review while the team tunes noisy rules and documents justified suppressions.</p>
<h2 id="heading-handling-false-positives">Handling False Positives</h2>
<p>False positives are expected in static analysis. The wrong response is disabling the scanner entirely. Verify whether a finding is exploitable and fix it when it represents real risk. When an exception is justified, use a narrowly scoped suppression, record the reason, and review it periodically.</p>
<p>Avoid broad exclusions such as skipping entire directories unless there's a clear technical reason. Broad exclusions tend to become blind spots.</p>
<h2 id="heading-add-security-checks-to-ci-too">Add Security Checks to CI Too</h2>
<p>Pre-commit hooks improve developer feedback, but they aren't enforcement. You can skip hooks with:</p>
<pre><code class="language-bash">git commit --no-verify
</code></pre>
<p>That's why the same or equivalent security checks should run in CI. Here's a GitHub Actions workflow that runs both tools on every pull request. Save it as <code>.github/workflows/security.yml</code>:</p>
<pre><code class="language-yaml">name: Security Checks

on:
  pull_request:
  push:
    branches: [main]

jobs:
  scan:
    runs-on: ubuntu-latest
    permissions:
      contents: read
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: actions/setup-dotnet@v4
        with:
          dotnet-version: "8.0.x"

      - name: Install DevSkim
        run: dotnet tool install --global Microsoft.CST.DevSkim.CLI

      - name: Run DevSkim
        run: devskim analyze -I . -f sarif -O devskim.sarif

      - name: Upload DevSkim results
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: devskim.sarif

      - name: Run Gitleaks
        uses: gitleaks/gitleaks-action@v2
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
</code></pre>
<p>A few details here are worth calling out. <code>fetch-depth: 0</code> gives Gitleaks the full history it needs to scan past commits rather than just the tip. The SARIF upload sends DevSkim findings to the repository's Security tab, so results are reviewable in GitHub instead of buried in build logs. Microsoft also publishes a <a href="https://github.com/microsoft/DevSkim-Action">DevSkim Action</a> if you prefer not to manage the CLI install yourself.</p>
<p>From there, use pre-commit for fast feedback on changed code, pull request pipelines for SAST, secret scanning, and dependency checks, main-branch builds for full scans and reporting, and release pipelines for gates on critical findings. This layered approach balances developer experience with governance.</p>
<h2 id="heading-practical-rollout-tips">Practical Rollout Tips</h2>
<p>Start with one or two repositories and baseline existing findings before enforcing new rules. Initially, block only high-confidence, high-impact issues, then share examples of findings and fixes with developers. Track recurring patterns to guide secure coding training, and measure adoption and time-to-remediation instead of focusing only on finding counts.</p>
<p>The aim is to help developers make secure choices by default, not create another tool that gets ignored.</p>
<h2 id="heading-final-thoughts">Final Thoughts</h2>
<p>Security tooling is most valuable when it appears at the moment a developer can act on it. Pre-commit hooks make secure coding feedback immediate, while CI provides the enforcement and broader coverage needed for production systems.</p>
<p>DevSkim is a useful lightweight entry point for teams building a shift-left security practice, and pairing it with a dedicated secret scanner covers the gap a linter is not built to fill. Start small, tune the rules, keep scans fast, and support the workflow with CI validation and secure coding guidance.</p>
<p>The best security control is often the one that prevents the issue from being committed in the first place.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Deploy a MERN Stack Notes App on AWS ]]>
                </title>
                <description>
                    <![CDATA[ Platforms like Vercel, Netlify, and Render simplify deployment by handling infrastructure for you. In this tutorial, we’ll step one layer deeper and work directly with AWS to understand the building blocks behind these platforms. You'll take a small ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-deploy-mern-stack-notes-app-aws/</link>
                <guid isPermaLink="false">696af32341a3a861f59ed367</guid>
                
                    <category>
                        <![CDATA[ deployment ]]>
                    </category>
                
                    <category>
                        <![CDATA[ AWS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ full stack ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Umair Mirza ]]>
                </dc:creator>
                <pubDate>Sat, 17 Jan 2026 02:25:39 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1768616328012/274a3de8-32bb-4b56-9f71-0f0723541c7d.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Platforms like Vercel, Netlify, and Render simplify deployment by handling infrastructure for you. In this tutorial, we’ll step one layer deeper and work directly with AWS to understand the building blocks behind these platforms.</p>
<p>You'll take a small React and Express notes app and ship it straight to AWS. We'll use EC2 for the API, RDS Postgres for the database, and S3 (optionally CloudFront) for the frontend. If you're new to AWS, you can turn on the Free Tier first: <a target="_blank" href="https://aws.amazon.com/free">https://aws.amazon.com/free</a>.</p>
<p>If you’ve mostly used one-click deployments before, this guide will help you understand what’s happening behind the scenes. You’ll work directly with the core AWS services involved, focusing only on the pieces that matter so you can see how everything fits together. This will also enable you to have more control over cost, security, and scaling.</p>
<p>If you just want to grab the finished code, it's all in this public repo: <a target="_blank" href="https://github.com/umair-mirza/mern-notes-aws">umair-mirza/mern-notes-aws</a>. You can clone or fork it and follow along without creating a new project from scratch.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-youll-build">What You’ll Build</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-mental-map">Mental Map</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-free-tier-basics">Free Tier Basics</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-environment-variables">Environment Variables</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-1-run-it-locally-first">Step #1 - Run It Locally First</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-2-push-to-github-so-ec2-can-pull">Step #2 - Push to GitHub (So EC2 Can Pull)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-3-create-aws-resources-quick-path">Step #3 - Create AWS Resources (Quick Path)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-4-configure-the-ec2-box">Step #4 - Configure the EC2 Box</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-5-build-and-upload-the-frontend">Step #5 - Build and Upload the Frontend</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-6-quick-troubleshooting">Step #6 - Quick Troubleshooting</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-7-secure-and-save">Step #7 - Secure and Save</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-8-verify-end-to-end">Step #8 - Verify End-to-End</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-next-steps">Next Steps</a></p>
</li>
</ul>
<h2 id="heading-what-youll-build"><strong>What You’ll Build</strong></h2>
<p>Before touching any buttons in AWS, it's helpful to know the exact pieces you're trying to build. At the end of this guide, you'll have a classic three-tier web app: a browser-based frontend, a backend API, and a database, all talking to each other over a network.</p>
<ul>
<li><p>API (Express/Node) on EC2</p>
</li>
<li><p>Postgres on RDS (Free Tier eligible)</p>
</li>
<li><p>React/Vite frontend on S3 (CloudFront optional for CDN/HTTPS)</p>
</li>
<li><p>Health check at <code>/api/health</code> and CRUD at <code>/api/notes</code></p>
</li>
</ul>
<h2 id="heading-prerequisites"><strong>Prerequisites</strong></h2>
<p>You don't need to be a DevOps expert to follow along, but you should be comfortable running basic commands in a terminal and editing some config files. If you've ever used <code>npm install</code> before, then you're in the right place.</p>
<ul>
<li><p>AWS account + AWS CLI configured (<code>aws configure</code>) – see <a target="_blank" href="https://docs.aws.amazon.com/accounts/latest/reference/manage-acct-creating.html">AWS account setup</a> and <a target="_blank" href="https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html">AWS CLI install</a>.</p>
</li>
<li><p>Node.js 18+ and npm – get it from <a target="_blank" href="http://nodejs.org">nodejs.org</a><a target="_blank" href="https://nodejs.org/">.</a></p>
</li>
<li><p>Git + GitHub repo – see <a target="_blank" href="https://docs.github.com/en/get-started">GitHub getting started</a>.</p>
</li>
<li><p>(Optional) Route 53 domain for a clean URL – <a target="_blank" href="https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/domain-register.html">Route 53 domains</a>.</p>
</li>
</ul>
<h2 id="heading-mental-map"><strong>Mental Map</strong></h2>
<p>AWS throws a lot of jargon at you (VPCs, security groups, subnets). This section is the story version of what happens when someone opens your app in the browser, without any buzzwords. If you can picture this flow, the later AWS screens will feel less scary.</p>
<ul>
<li><p>Browser loads the built React app from S3 (or CloudFront -&gt; S3)</p>
</li>
<li><p>Browser calls the API on EC2 over HTTP/HTTPS</p>
</li>
<li><p>EC2 talks to RDS Postgres on port 5432 inside your VPC</p>
</li>
<li><p>Security groups: allow 80/443 to EC2; allow 5432 only from the EC2 SG to RDS</p>
</li>
</ul>
<h2 id="heading-free-tier-basics"><strong>Free Tier Basics</strong></h2>
<p>AWS can be cheap if you use the free tier, but it can also surprise you with bills if you accidentally orprovision or leave things running. Here are the main knobs that affect cost for this tutorial and what to watch out for.</p>
<ul>
<li><p>EC2: <code>t2.micro</code> or <code>t3.micro</code> ~750 hours/month</p>
</li>
<li><p>RDS: <code>db.t3.micro</code> Postgres/MySQL with ~20 GB storage</p>
</li>
<li><p>S3/CloudFront: Small sites cost pennies - free tier includes some egress</p>
</li>
<li><p>Save money: Stop EC2 when idle. Delete unused buckets/DBs</p>
</li>
</ul>
<h2 id="heading-environment-variables"><strong>Environment Variables</strong></h2>
<p>Environment variables are just configuration values that live outside your code: ports, database URLs, and allowed origins. They keep secrets (like DB passwords) out of your Git repo and let the same code run in different places (local, staging, production) with different settings.</p>
<ul>
<li><p>Backend: <code>PORT</code>, <code>DATABASE_URL</code> (your RDS endpoint), <code>DATABASE_SSL</code> (<code>true</code> on RDS), <code>CORS_ORIGIN</code></p>
</li>
<li><p>Frontend: <code>VITE_API_URL</code> (API base, for example, <code>https://api.example.com/api</code>)</p>
</li>
</ul>
<h2 id="heading-step-1-run-it-locally-first"><strong>Step #1 - Run It Locally First</strong></h2>
<p>Before touching AWS, you want to prove the app actually works on your own machine. This removes a whole category of "Is it AWS or my code?" debugging later. In this step you just install dependencies and run both backend and frontend in dev mode.</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> mern-notes-aws

<span class="hljs-comment"># Backend</span>
<span class="hljs-built_in">cd</span> backend
npm install
cp .env.example .env   <span class="hljs-comment"># set DATABASE_URL to RDS (or local Postgres), DATABASE_SSL=true for RDS</span>
npm run dev            <span class="hljs-comment"># API on http://localhost:4000</span>

<span class="hljs-comment"># Frontend (new terminal)</span>
<span class="hljs-built_in">cd</span> frontend
npm install
cp .env.example .env   <span class="hljs-comment"># keep API URL at http://localhost:4000/api for local dev</span>
npm run dev            <span class="hljs-comment"># SPA on http://localhost:5173</span>
</code></pre>
<p>Open <code>http://localhost:5173</code>, add a note, and check if it persists. <code>/api/health</code> should return <code>{ status: 'ok' }</code>. If something is broken here, pause and fix it before moving on. AWS will only make debugging harder.</p>
<h2 id="heading-step-2-push-to-github-so-ec2-can-pull"><strong>Step #2 - Push to GitHub (So EC2 Can Pull)</strong></h2>
<p>Your EC2 server in AWS needs a place to pull your code from. Using GitHub is the simplest option: you push your code once, then the EC2 instance clones that repo. You can also reuse this repo later with CI/CD if you decide to automate deployments.</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> mern-notes-aws
git init
git add .
git commit -m <span class="hljs-string">"feat: mern notes app"</span>
git branch -M main
git remote add origin https://github.com/&lt;you&gt;/mern-notes-aws.git
git push -u origin main
</code></pre>
<p>If you're following along with my example repo instead of creating your own, you can simply fork <a target="_blank" href="https://github.com/umair-mirza/mern-notes-aws">umair-mirza/mern-notes-aws</a> and use that as your remote.</p>
<p>Before pushing, make sure your <code>.env</code> file is <strong>not committed to GitHub</strong>. Add it to your <code>.gitignore</code> so secrets like database passwords never end up in version control:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">echo</span> <span class="hljs-string">".env"</span> &gt;&gt; .gitignore
</code></pre>
<p>If you’ve already created a <code>.env</code> file locally, double-check it doesn’t appear in <code>git status</code> before committing.</p>
<h2 id="heading-step-3-create-aws-resources-quick-path"><strong>Step #3 - Create AWS Resources (Quick Path)</strong></h2>
<h3 id="heading-rds-postgres-free-tier-template"><strong>RDS (Postgres, Free Tier template)</strong></h3>
<p>RDS (Relational Database Service) is AWS's way of running managed databases for you. Instead of installing Postgres manually on a VM, you click a few options and AWS handles backups, patching, and high availability. For this app we only need a small, free tier–eligible Postgres instance.</p>
<p>For more background, you can skim the official <a target="_blank" href="https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_PostgreSQL.html">Amazon RDS for PostgreSQL docs</a>.</p>
<p>We’ll start by creating the database layer. The settings below are the minimum you need for a small, production-style Postgres setup that stays within the AWS Free Tier while still following basic best practices.</p>
<ul>
<li><p>RDS Create database Postgres Free Tier.</p>
</li>
<li><p>Class <code>db.t3.micro</code>, storage 20 GB gp2/gp3.</p>
</li>
<li><p>Set master user/pass. You'll need them for <code>DATABASE_URL</code>.</p>
</li>
<li><p>Public access: No.</p>
</li>
<li><p>Security group: allow 5432 only from the EC2 security group.</p>
</li>
<li><p>Enable backups and Require SSL. Download the RDS CA if you want strict cert validation.</p>
</li>
</ul>
<h3 id="heading-s3-bucket-for-the-frontend"><strong>S3 Bucket for the Frontend</strong></h3>
<p>S3 is AWS's "infinite hard drive" for files. A React/Vite app builds down to plain HTML, CSS, and JavaScript files, which are perfect to host from S3. Think of S3 as a very simple web server that just serves static files.</p>
<p>If you want to see more options, check the <a target="_blank" href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/WebsiteHosting.html">Hosting a static website on Amazon S3 guide</a>.</p>
<p>Now, we’ll create an S3 bucket to host the React frontend. These options configure the bucket for static website hosting while keeping it simple and inexpensive.</p>
<ul>
<li><p>Create bucket <code>mern-notes-aws-frontend-&lt;suffix&gt;</code>.</p>
</li>
<li><p>For simple hosting, enable static website hosting and allow public reads, or keep private and use CloudFront + OAC.</p>
</li>
<li><p>Turn on versioning if you want rollback safety.</p>
</li>
</ul>
<h3 id="heading-ec2-for-the-api"><strong>EC2 for the API</strong></h3>
<p>EC2 is "a computer in the cloud" that you control. You'll install Node.js on it, pull your code, and run <code>server.js</code> so that your backend API is always on. The security group attached to this instance works like a firewall.</p>
<p>If you've never launched an instance before, the <a target="_blank" href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EC2_GetStarted.html">Getting started with Amazon EC2</a> guide walks through the console screens you'll see.</p>
<p>Finally, we’ll provision a small EC2 instance to run the Express API. The configuration below focuses on a free tier–eligible setup that’s secure enough for learning and easy to extend later.</p>
<ul>
<li><p>Launch Amazon Linux 2023, size <code>t3.micro</code>.</p>
</li>
<li><p>Inbound SG: 22 (your IP), 80 (world), 443 if you add HTTPS on the instance/ALB.</p>
</li>
<li><p>Attach this SG as the allowed source to RDS.</p>
</li>
</ul>
<h3 id="heading-optional-cloudfront-route-53"><strong>Optional: CloudFront + Route 53</strong></h3>
<p>CloudFront is AWS's CDN (content delivery network), and Route 53 is their DNS service. You don't strictly need them to get your app working, but they make it faster and nicer: your app loads from edge locations close to users and can live behind a friendly domain like <code>app.example.com</code>.</p>
<p>For more details, see <a target="_blank" href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/GettingStarted.html">Getting started with Amazon CloudFront</a> and the <a target="_blank" href="https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/Welcome.html">Route 53 DNS developer guide</a>.</p>
<ul>
<li><p>Origin: the S3 bucket. Default root <code>index.html</code>. Add OAC if bucket is private.</p>
</li>
<li><p>Request an ACM cert in <code>us-east-1</code>, then create a Route 53 A/AAAA alias to the distribution.</p>
</li>
</ul>
<h2 id="heading-step-4-configure-the-ec2-box"><strong>Step #4 - Configure the EC2 Box</strong></h2>
<p>Once your EC2 instance is running, you treat it like a clean Linux machine. The commands below install the tools your API needs, pull your code from GitHub, configure environment variables, and run the server in a production-safe way.</p>
<p>Install basics:</p>
<pre><code class="lang-bash">sudo dnf update -y
</code></pre>
<p>This command updates all system packages to the latest versions. It's a good first step on any new Linux server.</p>
<pre><code class="lang-bash">sudo dnf install -y git
</code></pre>
<p>Installs Git so the EC2 instance can clone your repository from GitHub.</p>
<pre><code class="lang-bash">curl -fsSL https://rpm.nodesource.com/setup_20.x | sudo bash -
</code></pre>
<p>Adds the official NodeSource repository so you can install a modern version of Node.js (v20). Amazon Linux doesn’t ship with recent Node versions by default.</p>
<pre><code class="lang-bash">sudo dnf install -y nodejs
</code></pre>
<p>Installs Node.js and npm, which are required to run your Express API.</p>
<pre><code class="lang-bash">sudo npm install -g pm2
</code></pre>
<p>Installs PM2, a lightweight process manager that keeps your Node app running in the background and restarts it if it crashes or the server reboots.</p>
<p>Pull code and set environment variables:</p>
<pre><code class="lang-bash">git <span class="hljs-built_in">clone</span> https://github.com/&lt;you&gt;/mern-notes-aws.git
<span class="hljs-built_in">cd</span> mern-notes-aws/backend
npm install

cat &lt;&lt;<span class="hljs-string">'EOF'</span> &gt; .env
PORT=80
DATABASE_URL=postgres://&lt;user&gt;:&lt;password&gt;@&lt;rds-endpoint&gt;:5432/&lt;dbname&gt;
DATABASE_SSL=<span class="hljs-literal">true</span>
CORS_ORIGIN=https://&lt;your-frontend-domain&gt;
EOF
</code></pre>
<p>Start the API with PM2:</p>
<pre><code class="lang-bash">pm2 start server.js --name mern-notes-api
pm2 save
pm2 startup systemd -u ec2-user --hp /home/ec2-user
</code></pre>
<p>PM2 is a small process manager that makes sure your Node server keeps running if the machine reboots or the process crashes. Test on the box: <code>curl http://localhost/api/health</code>. From your laptop: <code>http://&lt;ec2-public-dns&gt;/api/health</code> (make sure SG allows 80/443).</p>
<h2 id="heading-step-5-build-and-upload-the-frontend"><strong>Step #5 - Build and Upload the Frontend</strong></h2>
<p>In development, Vite serves your React app from memory, but in production you want a set of static files that any web server (or S3) can host. <code>npm run build</code> creates an optimized <code>dist/</code> folder that you sync to S3 so the browser can load it.</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> frontend
setx VITE_API_URL <span class="hljs-string">"https://&lt;ec2-or-api-domain&gt;/api"</span>
npm run build
</code></pre>
<p>This sets an environment variable called <code>VITE_API_URL</code> on your local machine. Vite only exposes environment variables to the frontend if they start with the <code>VITE_</code> prefix.</p>
<p>Upload:</p>
<pre><code class="lang-bash">aws s3 sync dist/ s3://mern-notes-aws-frontend-&lt;suffix&gt;/ --delete
</code></pre>
<p>This uploads your compiled frontend (<code>dist/</code>) to S3 and removes old files that no longer exist locally, ensuring the bucket reflects the current version of the app</p>
<p>Open the S3 website URL or your CloudFront URL.</p>
<h2 id="heading-step-6-quick-troubleshooting"><strong>Step #6 - Quick Troubleshooting</strong></h2>
<p>If something doesn't work the first time, that's normal, especially with networking and AWS permissions. This section gives you a few quick places to look before you start randomly changing settings in the console.</p>
<ul>
<li><p>API 500s: <code>pm2 logs mern-notes-api</code>. This is often a bad <code>DATABASE_URL</code> or SSL flag.</p>
</li>
<li><p>DB connect issues: RDS SG must allow the EC2 SG - use the RDS endpoint.</p>
</li>
<li><p>CORS errors: <code>CORS_ORIGIN</code> must match your frontend origin exactly.</p>
</li>
<li><p>403 from S3: If you’re using static website hosting, allow public reads. With CloudFront, keep bucket private and use OAC.</p>
</li>
<li><p>Blank page: Confirm that you’ve uploaded <code>dist/</code> to the right bucket.</p>
</li>
</ul>
<h2 id="heading-step-7-secure-and-save"><strong>Step #7 - Secure and Save</strong></h2>
<p>Once everything works, you don't want to accidentally expose your database to the internet or burn through free tier hours. These are simple, beginner-friendly hardening steps that make your setup safer and cheaper without turning you into a full-time security engineer.</p>
<ul>
<li><p>Turn off SSH after setup or switch to SSM Session Manager.</p>
</li>
<li><p>Use HTTPS (CloudFront + ACM or ALB + ACM).</p>
</li>
<li><p>Keep RDS private and use SSM port forwarding if needed.</p>
</li>
<li><p>Ship PM2 logs with CloudWatch Agent and add alarms for CPU/status checks.</p>
</li>
<li><p>Snapshot RDS daily and stop EC2 when idle to save hours.</p>
</li>
</ul>
<h2 id="heading-step-8-verify-end-to-end"><strong>Step #8 - Verify End-to-End</strong></h2>
<p>Before you celebrate, run through the app like a real user: open it in the browser, create notes, refresh, and make sure everything behaves as expected. This confirms your frontend, API, and database are all wired together correctly.</p>
<ul>
<li><p>Load the frontend (S3 or CloudFront).</p>
</li>
<li><p>Create and delete notes. They should persist in RDS.</p>
</li>
<li><p>Hit <code>/api/health</code> for a quick liveness check.</p>
</li>
</ul>
<h2 id="heading-next-steps"><strong>Next Steps</strong></h2>
<p>Once you're comfortable with this manual setup, you can start layering on more advanced tools. The ideas are the same: frontend, API and database but you get more automation, safety, and scalability.</p>
<ul>
<li><p>Add Prisma + migrations for stronger schemas.</p>
</li>
<li><p>Add auth (Cognito/Auth0) and per-user notes.</p>
</li>
<li><p>Containerize and run on ECS/Fargate or add an ALB in front of EC2.</p>
</li>
<li><p>Use Terraform/CDK to recreate this stack with one command.</p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
