Trackly: A Production-Ready Full-Stack Application with Go, React, PostgreSQL, Docker, and JWT Authentication

#. Introduction

Trackly is a real-world style full-stack application that I developed to manage and monitor projects in real time.

  • A learning platform for me to master backend, frontend, and DevOps fundamentals.

  • A reference implementation for clean architecture, containerization, and secure authentication.

  • A revision tool I can use for internships, technical interviews, and future projects.

In this first blog of the series, I’ll cover:

  • The architecture and why it’s designed this way.

  • Every major technology used and its role in the system.

  • Deep dive into backend entry point (main.go) .

  • Deployment strategy for a production-ready environment.

#. Technology Stack

LayerTechnologyWhy It’s Used
FrontendReact, Bootstrap, React Router DOMComponent-driven UI, responsive design, SPA routing
BackendGo (Golang), Gorilla MuxFast, concurrent, minimal memory footprint, mature HTTP router
DatabasePostgreSQLACID compliance, strong relational data modeling
DeploymentDocker, Render (backend), GitHub Pages (frontend)Same environment locally and in production
AuthenticationJWT (JSON Web Tokens)Stateless, secure, widely used for APIs

#. System Architecture

Key Characteristics:

  • Decoupled frontend & backend — allows independent deployment and scaling.

  • Stateless backend — JWT makes user sessions independent of server storage.

  • Dockerized environment — ensures consistency between local dev and production.

  • Background monitoring — separate goroutine handles project status checks without blocking API requests.

#. Backend Overview — main.go

func main() {
    //1. Load .env only if running locally
    if os.Getenv("RENDER") == "" {
        if err := godotenv.Load(); err != nil {
            log.Println("⚠️ No .env file found, using system environment variables")
        }
    }

    //2. Check if critical env vars exist
    if os.Getenv("DB_URL") == "" {
        log.Fatal("❌ DB_URL environment variable not set")
    }

    db.ConnectDB()

    //3. THIS LINE IS ADDED TO START THE BACKGROUND MONITOR
    go project.MonitorProjects()

    r := mux.NewRouter()

    //4.  middleware
    r.Use(middleware.CORSMiddleware)

    //5. Healthcheck
    r.HandleFunc("/healthcheck", func(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(http.StatusOK)
        fmt.Fprint(w, "OpenAnalytics backend is running 🚀")
    }).Methods("GET")

    //6. Public routes
    r.HandleFunc("/user/register", user.Register).Methods("POST", "OPTIONS")
    r.HandleFunc("/user/login", user.HandleLogin).Methods("POST", "OPTIONS")

    //7. Project routes (JWT protected)
    projectRouter := r.PathPrefix("/project").Subrouter()
    projectRouter.Use(user.JwtMiddleware)
    projectRouter.HandleFunc("", project.CreateProject).Methods("POST", "OPTIONS")
    projectRouter.HandleFunc("", project.GetProjects).Methods("GET", "OPTIONS")
    projectRouter.HandleFunc("/dashboard", project.GetDashboard).Methods("GET", "OPTIONS")
    projectRouter.HandleFunc("/status", project.CheckProjectStatus).Methods("GET", "OPTIONS")
    projectRouter.HandleFunc("/{id}", project.UpdateProject).Methods("PUT", "OPTIONS")
    projectRouter.HandleFunc("/{id}", project.DeleteProject).Methods("DELETE", "OPTIONS")
    projectRouter.HandleFunc("/{id}", project.GetProjectByID).Methods("GET", "OPTIONS")

    //8. Use PORT from Render or default to 8080
    port := os.Getenv("PORT")
    if port == "" {
        port = "8080"
    }

    fmt.Printf("🚀 Trackly backend is running on port %s\n", port)
    log.Fatal(http.ListenAndServe(":"+port, r))
}

1. Loading Environment Variables

  • What: Loads .env file in local development, skips in production (Render sets env vars automatically).

  • Why: Keeps credentials (DB_URL, JWT_SECRET) out of source code for security.

2. Database Connection

  • What: Reads DB connection string from DB_URL env var.

  • Why: Centralizes DB connection logic in a single function (db.ConnectDB()).

3. go project.MonitorProjects()

  • What: Launches a goroutine to periodically check project statuses.

  • Why: Go’s concurrency model is lightweight, allowing tasks to run without blocking HTTP requests.

4. HTTP Router Setup

  • What: Creates an HTTP router with Gorilla Mux.

  • Why: Gorilla Mux provides named parameters, subrouters, and middleware support — features missing in Go’s standard http package.

5. Healthcheck Endpoint

6. Public vs Protected Routes

  • Public Routes: No authentication needed (register, login).

  • Protected Routes: Require JWT token in Authorization header.

  • Why: Separating public and private endpoints improves API clarity and security.

7. Server Startup :- log.Fatal(http.ListenAndServe(":8080", r))

#. Database (Docker Compose — PostgreSQL Service)

services:
  db:
    image: postgres:15
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: yourpassword
      POSTGRES_DB: openanalytics
    ports:
      - "5432:5432"
    volumes:
      - postgres-data:/var/lib/postgresql/data
  • Why Docker?

    • Eliminates “works on my machine” issues.

    • Makes it easy to reset and share dev environments.

  • Volume: postgres-data ensures data persistence.

  • Port Mapping: Exposes DB on localhost:5432 for local development.

#.Frontend

Key Points

  • Built with React + Vite for fast development and small production bundle size.

  • Bootstrap ensures quick, responsive UI design.

  • React Router DOM provides client-side routing for SPA (Single Page Application) behavior.

export default defineConfig({
  plugins: [react()],
  base: "/Trackly/"
})
  • Base Path: Necessary when hosting under /Trackly/ instead of root.

  • Why Vite?: Faster build times compared to Create React App.

#. Deployment

Local Development

docker compose up --build

Runs:

  • PostgreSQL (db)

  • Go backend (backend)

  • React frontend (frontend)

Production

  • DB(database): Render .

  • Backend: Render, with env vars configured in dashboard.

  • Frontend: Built (npm run build) and deployed to GitHub Pages via gh-pages package.

#. Why JWT?

When building Trackly, I needed an authentication system that was secure, scalable, and easy to integrate with a stateless backend. JSON Web Tokens (JWT) checked all the boxes:

  • Stateless Authentication – No server-side session storage; all required user data is embedded in the token.

  • Scalability – Works seamlessly across multiple backend instances.

  • Security – Signed with a secret (JWT_SECRET), preventing tampering.

  • Cross-Platform – Any client (web, mobile) can authenticate with the same token.

The flow is simple:

  1. User logs in → backend validates credentials.

  2. Backend issues JWT → frontend stores it securely.

  3. For protected routes, frontend sends token in Authorization: Bearer <token> header.

  4. Middleware verifies token before processing the request.

#. System Structure

Trackly is split into three main services:

  1. React Frontend (GitHub Pages) – Handles UI and API calls.

  2. Go Backend API (Render) – Processes requests, applies business logic, verifies JWTs, and communicates with the database.

  3. PostgreSQL Database – Stores users, projects, and related data.

The backend also issues JWTs and runs a background monitoring goroutine to check project statuses without blocking requests.

0
Subscribe to my newsletter

Read articles from Sidharth chauhan directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Sidharth chauhan
Sidharth chauhan

🌟 Hello! I'm Sidharth Chauhan, a passionate DevOps Engineer dedicated to automating and optimizing processes to enhance software development and deployment. With expertise in Docker, Kubernetes, CI/CD, AWS, and more, I thrive in environments that leverage cutting-edge technologies and tools.I love sharing my knowledge and experiences through blogging, and I'm always eager to connect with like-minded professionals. Whether you're looking to collaborate on a project, need help with DevOps, or just want to chat about the latest tech trends, feel free to reach out!🔗 Connect with Me: