Quote of the day GoLang App Tutorial
Go (Golang) is one of the fastest, cleanest, and most enjoyable languages for building backend applications. In this tutorial, we’ll build a small but surprisingly powerful project: a “Quote of the Day” micro-API that fetches an external quote, caches it, and serves it via your own endpoint.
You’ll learn:
- Structuring a Go project
- Handling HTTP requests
- Making API calls
- Using concurrency for caching
- Clean error handling
- Running your app locally
🚀 What We’re Building
We’ll create a service that:
- Fetches a daily quote from a public API
- Caches the result for 24 hours
- Serves it via a fast local HTTP endpoint
- Refreshes the cache in the background using goroutines
This is a perfect pattern for real-world microservices: fetch → enrich → cache → serve.
📁 Project Structure
Create a folder for your app:
cool-go-app/
├── main.go
├── quotes/
│ └── service.go
└── go.mod
🛠 Step 1: Initialise Your Project
In your terminal:
mkdir cool-go-app
cd cool-go-app
go mod init cool-go-app
🧠 Step 2: Build the Quote Service
Create quotes/service.go:
package quotes
import (
"encoding/json"
"errors"
"net/http"
"sync"
"time"
)
type QuoteResponse struct {
Content string `json:"content"`
Author string `json:"author"`
}
type Service struct {
mu sync.RWMutex
quote QuoteResponse
expires time.Time
}
func NewService() *Service {
return &Service{}
}
// Fetches from external API
func (s *Service) fetchQuote() (QuoteResponse, error) {
resp, err := http.Get("https://api.quotable.io/random")
if err != nil {
return QuoteResponse{}, err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return QuoteResponse{}, errors.New("failed to fetch quote")
}
var qr QuoteResponse
if err := json.NewDecoder(resp.Body).Decode(&qr); err != nil {
return QuoteResponse{}, err
}
return qr, nil
}
// Returns cached quote; refreshes if expired
func (s *Service) GetQuote() (QuoteResponse, error) {
s.mu.RLock()
if time.Now().Before(s.expires) {
defer s.mu.RUnlock()
return s.quote, nil
}
s.mu.RUnlock()
// Refresh cache
q, err := s.fetchQuote()
if err != nil {
return QuoteResponse{}, err
}
s.mu.Lock()
defer s.mu.Unlock()
s.quote = q
s.expires = time.Now().Add(24 * time.Hour)
return q, nil
}
🧪 Step 3: Build the HTTP Server
Create main.go:
package main
import (
"encoding/json"
"log"
"net/http"
"cool-go-app/quotes"
)
func main() {
quoteService := quotes.NewService()
http.HandleFunc("/quote", func(w http.ResponseWriter, r *http.Request) {
q, err := quoteService.GetQuote()
if err != nil {
http.Error(w, err.Error(), http.StatusBadGateway)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(q)
})
log.Println("Server running on http://localhost:8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
▶️ Step 4: Run the App
In your terminal:
go run .
Visit:
http://localhost:8080/quote
You should see something like:
{
"content": "Life is 10% what happens to us and 90% how we react to it.",
"author": "Charles R. Swindoll"
}
⚡ Enhancements You Can Add
To turn this tiny app into a genuinely impressive service, try adding:
1. In-Memory Metrics
Track number of hits, cache hits/misses, etc.
2. Persistent Storage
Save quotes to SQLite or Postgres.
3. Scheduled Cache Refresh
Use a goroutine + ticker to pre-warm quotes every 24 hours.
4. Add a CLI Tool
Expose the same quote fetcher on the command line.
5. Dockerise It
Super easy with Go’s static binaries.
🎉 Summary
You’ve just built:
- A structured Go microservice
- A real API requester
- A production-style caching layer
- A concurrency-safe service
- A running HTTP server
This kind of small project is great practice for larger systems—reverse proxies, scrapers, IoT telemetry collectors, and more all use similar patterns.