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
Layer | Technology | Why It’s Used |
Frontend | React, Bootstrap, React Router DOM | Component-driven UI, responsive design, SPA routing |
Backend | Go (Golang), Gorilla Mux | Fast, concurrent, minimal memory footprint, mature HTTP router |
Database | PostgreSQL | ACID compliance, strong relational data modeling |
Deployment | Docker, Render (backend), GitHub Pages (frontend) | Same environment locally and in production |
Authentication | JWT (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 viagh-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:
User logs in → backend validates credentials.
Backend issues JWT → frontend stores it securely.
For protected routes, frontend sends token in
Authorization: Bearer <token>
header.Middleware verifies token before processing the request.
#. System Structure
Trackly is split into three main services:
React Frontend (GitHub Pages) – Handles UI and API calls.
Go Backend API (Render) – Processes requests, applies business logic, verifies JWTs, and communicates with the database.
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.
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: