Skip to main content

On This Page

Draft / Scheduled Content

This article is a draft or scheduled for future publication. The content is subject to change.

Why You Probably Don't Need a Backend

8 min read
Share

The Default Reflex

Here is how almost every modern software project starts:

  1. You get a cool idea.
  2. You run npx create-next-app.
  3. You immediately run mkdir server (or backend), initiate a Docker Compose file with PostgreSQL, spin up an Express/FastAPI/Go app, and figure out how to configure CORS, JWT authentication, and database migrations.

We do this because it’s what we know. We are trained to think in terms of client-server architecture. We assume that if an application needs data, it must fetch that data from an active server database over HTTP on every single page load.

But as the project goes live, reality sets in.

Suddenly, you are responsible for maintaining a database. You have to monitor connection pools, manage database migrations, run security updates on the OS, and set up Prometheus alerts to tell you if the server runs out of memory. If your project gets featured on Hacker News, your backend crashes under the sudden spike of 500 concurrent connections. If it gets no traffic, you are still paying $15/month for idle compute and DB instances just to keep the lights on.

It doesn’t have to be this way.

For a massive category of web applications, you do not need an active backend. A static site hosted on a global CDN, combined with a background builder job running in GitHub Actions or GitLab CI, is not only cheaper and easier to maintain—it is objectively better.


The “Static + CI” Architecture (The Git-as-a-Database Pattern)

Think about the data your app actually uses. Does it change every millisecond? Or does it change once an hour, once a day, or only when you publish a new article?

If you are building a job board, a price tracker, a portfolio, a documentation site, a news aggregator, a dashboard showing public metrics, or even a local business directory, the data changes slowly. Fetching that data from a database on every page load is a waste of compute resources.

Instead, let the build pipeline do the heavy lifting:

+---------------------+
| Scheduled Cron Job  |  <--- Triggered every hour (GitHub Actions / GitLab CI)
+---------------------+
           |
           v
+---------------------+
| Python/Node Script  |  <--- Scrapes APIs, pulls DB records, queries RSS feeds
+---------------------+
           |
           v
+---------------------+
| Commit Data to Git  |  <--- Updates jobs.json or content/posts/
+---------------------+
           |
           v
+---------------------+
| Static Site Build   |  <--- Rebuilds HTML & JS (Astro, Hugo, Next.js)
+---------------------+
           |
           v
+---------------------+
| Edge CDN (Pages)    |  <--- Served globally at 0ms latency, zero database calls
+---------------------+

The Worker Script

Instead of writing a REST API, you write a simple script (Node, Python, or Go) that fetches your data, formats it, and writes it directly to a static file in your repository (like src/data/jobs.json).

Here is a simple example in Node.js (scripts/update-jobs.js):

import fs from 'fs';
import fetch from 'node-fetch';

async function syncData() {
  console.log("Fetching latest job listings...");
  const response = await fetch("https://api.externalpartner.com/jobs?status=active");
  const jobs = await response.json();

  // Save directly to the frontend's source folder
  fs.writeFileSync(
    './src/data/jobs.json',
    JSON.stringify(jobs, null, 2)
  );
  console.log(`Saved ${jobs.length} jobs to static repository.`);
}

syncData();

The CI Pipeline

You schedule this script to run periodically using GitHub Actions or GitLab CI. The runner executes the script, checks if the data changed, commits the changes back to the repository, and triggers a redeployment.

Here is a complete GitHub Actions workflow (.github/workflows/sync-data.yml):

name: Hourly Data Sync

on:
  schedule:
    - cron: '0 * * * *' # Run every hour
  workflow_dispatch: # Allow manual trigger

jobs:
  build-and-sync:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Code
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 20

      - name: Install Dependencies
        run: npm ci

      - name: Fetch and Sync Data
        run: node scripts/update-jobs.js

      - name: Commit and Push Changes
        run: |
          git config --global user.name "github-actions[bot]"
          git config --global user.email "github-actions[bot]@users.noreply.github.com"
          git add src/data/jobs.json
          if ! git diff-index --quiet HEAD; then
            git commit -m "chore: auto-update jobs database [skip ci]"
            git push
          else
            echo "No data changes detected. Skipping commit."
          fi

Why this is brilliant:

  • 0ms Database Latency: The user gets a pre-built JSON file or pure HTML. There are no database queries, no connection pools to overflow, and no backend routing.
  • Infinite Scaling: Since the site is 100% static, it is cached globally at the edge on Cloudflare, GitHub Pages, or Netlify. It can handle 10,000 requests per second without sweating, costing you exactly $0.
  • Instant Version History: Your database is your Git history. If something goes wrong or bad data gets imported, you don’t have to restore a database backup. You just run git revert to go back to the last clean state.

But What About…? (The “Dynamic” Excuses)

“But wait,” you say, “my app needs dynamic features! A static site can’t handle user input!”

Yes, it can. We no longer live in 2005. The modern web ecosystem has commoditized the generic parts of backends. Here is how you replace the most common backend dependencies:

1. Contact and Intake Forms

  • The Backend Way: Write an /api/contact route, validation logic, configure an SMTP server client, and handle spam prevention.
  • The Static Way: Use a static-friendly form provider like Web3Forms, Formspree, or SmartForms. You point your standard HTML form action directly to their endpoint, and they handle verification, spam filtering, and email forwarding.
<form action="https://api.web3forms.com/submit" method="POST">
  <input type="hidden" name="access_key" value="YOUR-KEY-HERE">
  <input type="email" name="email" required>
  <textarea name="message" required></textarea>
  <button type="submit">Submit</button>
</form>
  • The Backend Way: Set up Elasticsearch or Meilisearch, configure syncing cron jobs, and expose a search API.
  • The Static Way: Use Pagefind. Pagefind runs during your static site build. It parses your generated HTML files, extracts keywords, and outputs a highly optimized static search index (usually only a few hundred kilobytes). In the browser, a lightweight JS script queries this index locally. It is blazingly fast, runs offline, and requires zero servers.

3. User Comments

  • The Backend Way: Build a comments database table, design moderation panels, implement markdown sanitization to prevent XSS.
  • The Static Way: Use Giscus or Utterances. They use GitHub Discussions or Issues as the database. Users sign in with their GitHub account, and comments are stored directly in your repo’s discussions tab, completely managed by GitHub.

4. Interactive State and Authentication

If you need user bookmarks, local settings, or client-side folders, don’t rush to a DB.

  • Use the browser’s localStorage or IndexedDB.
  • If you absolutely need cross-device user sync, use a lightweight Backend-as-a-Service (BaaS) provider like Supabase or Firebase directly from the frontend. Let them handle the authentication and security rules, leaving your frontend codebase serverless.

When a Backend is Actually Mandatory

We shouldn’t be dogmatic. There are absolute limits to what static files and Git pipelines can do. You definitely need a custom backend if your application falls into these categories:

Use CaseWhy Static/CI Fails
Real-time CollaborationApps like Google Docs, Figma, or chat apps require WebSockets and immediate, sub-second state synchronization between multiple clients.
Sensitive Calculations / SecretsIf your application has proprietary pricing algorithms, custom machine learning logic, or handles sensitive API keys that must never be exposed to the client browser.
Instant Global WritesSocial networks or forums where users write content that must be immediately visible to other users globally, with strict moderation and relational index updates.
Transactional High-Write SystemsCore banking platforms, real-time airline ticket booking, or stock trading systems where race conditions (double spending/booking) must be avoided at all costs.

If your app isn’t doing these things, you are wasting time, energy, and money writing a backend.


The Operational Comparison

Let’s look at the maintenance reality of both approaches over a 12-month horizon:

Operational MetricStandard Backend Setup (VPS / Serverless)Static Site + Git CI Pipeline
Hosting Cost$10 - $100+/month (VM, Database, Redis, NAT)$0 (GitHub Pages, Cloudflare Pages, Netlify)
Security RisksServer exploits, SQL injection, database leakage, expired SSL certificatesPractically zero (Read-only static assets on CDN)
Scaling StrategyLoad balancers, horizontal scaling, replica DBsAuto-scaled by global CDN infrastructure
Backup StrategyWeekly DB dumps, point-in-time recovery setupsEvery data change is a Git commit. Pure history
Deployment ComplexityDocker builds, orchestration, environment variables managementGit push / Git merge

Conclusion: Keep it Simple

Every line of backend code you write is a line you have to maintain, secure, test, and debug. Every infrastructure piece you deploy is a potential point of failure.

If your web application is primarily displaying information, aggregating data from third-party sources, or performing calculations that can run in the user’s browser, do yourself a favor: Delete the backend folder.

Write a simple Node script, schedule it with a cron job on GitHub Actions or GitLab CI, check the data into Git, and let the CDN handle the rest. You will spend less time debugging servers and more time building features.

Related Content