Quote of the day GoLang App Tutorial

Quote of the day GoLang App Tutorial
Photo by Drew Beamer / Unsplash

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:

  1. Fetches a daily quote from a public API
  2. Caches the result for 24 hours
  3. Serves it via a fast local HTTP endpoint
  4. 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.