High-Performance Video API Server with Go net/http

go dev.to

High-Performance Video API Server with Go net/http

Go is compelling for a video platform API: compiled binaries, zero-overhead goroutines, and a production-ready HTTP server in the standard library. Here's how I'd build the API backend for TopVideoHub in Go.

Why Go for This Use Case

  • net/http handles ~100k requests/second on modest hardware
  • Goroutines cost ~2KB memory vs ~1MB for OS threads
  • Single binary deployment — no runtime, no dependencies
  • Built-in race detector during development

For TopVideoHub handling 9 Asia-Pacific regions with concurrent trending requests, Go's concurrency model is a natural fit.

Core Types

package models

import "time"

type Region string

const (
    JP Region = "JP"
    KR Region = "KR"
    TW Region = "TW"
    SG Region = "SG"
    VN Region = "VN"
    TH Region = "TH"
    HK Region = "HK"
    US Region = "US"
    GB Region = "GB"
)

var ValidRegions = map[Region]bool{
    JP: true, KR: true, TW: true, SG: true,
    VN: true, TH: true, HK: true, US: true, GB: true,
}

// Video holds all metadata for a single video entry.
// Title, ChannelTitle may contain CJK characters — Go strings are UTF-8 natively.
type Video struct {
    VideoID      string    `json:"video_id"`
    Title        string    `json:"title"`
    ChannelTitle string    `json:"channel_title"`
    ThumbnailURL string    `json:"thumbnail_url"`
    ViewCount    int64     `json:"view_count"`
    Region       Region    `json:"region"`
    CategoryID   int       `json:"category_id"`
    PublishedAt  time.Time `json:"published_at"`
}

type TrendingResponse struct {
    Region    Region    `json:"region"`
    Videos    []Video   `json:"videos"`
    Total     int       `json:"total"`
    FetchedAt time.Time `json:"fetched_at"`
}
Enter fullscreen mode Exit fullscreen mode

HTTP Handler

package handlers

import (
    "encoding/json"
    "net/http"
    "strings"
    "time"

    "github.com/jackc/pgx/v5/pgxpool"
    "topvideohub/internal/models"
)

type TrendingHandler struct {
    Pool  *pgxpool.Pool
    Cache *Cache
}

func (h *TrendingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    // Extract region: /trending/JP
    parts := strings.Split(strings.Trim(r.URL.Path, "/"), "/")
    if len(parts) < 2 || parts[1] == "" {
        http.Error(w, `{"error":"region required"}`, http.StatusBadRequest)
        return
    }

    region := models.Region(strings.ToUpper(parts[1]))
    if !models.ValidRegions[region] {
        http.Error(w, `{"error":"invalid region"}`, http.StatusNotFound)
        return
    }

    cacheKey := "trending:" + string(region)
    if cached, ok := h.Cache.Get(cacheKey); ok {
        w.Header().Set("Content-Type", "application/json")
        w.Header().Set("X-Cache", "HIT")
        w.Write(cached)
        return
    }

    rows, err := h.Pool.Query(r.Context(), `
        SELECT v.video_id, v.title, v.channel_title, v.thumbnail_url,
               v.view_count, v.category_id, v.published_at, vr.region
        FROM videos v
        JOIN video_regions vr ON vr.video_id = v.id
        WHERE vr.region = $1
        ORDER BY vr.trending_rank ASC NULLS LAST, v.view_count DESC
        LIMIT 50
    `, string(region))
    if err != nil {
        http.Error(w, `{"error":"database error"}`, http.StatusInternalServerError)
        return
    }
    defer rows.Close()

    var videos []models.Video
    for rows.Next() {
        var v models.Video
        if err := rows.Scan(
            &v.VideoID, &v.Title, &v.ChannelTitle, &v.ThumbnailURL,
            &v.ViewCount, &v.CategoryID, &v.PublishedAt, &v.Region,
        ); err != nil {
            continue
        }
        videos = append(videos, v)
    }

    resp := models.TrendingResponse{
        Region:    region,
        Videos:    videos,
        Total:     len(videos),
        FetchedAt: time.Now().UTC(),
    }

    data, _ := json.Marshal(resp)
    h.Cache.Set(cacheKey, data, time.Hour)

    w.Header().Set("Content-Type", "application/json")
    w.Header().Set("Cache-Control", "public, max-age=3600")
    w.Header().Set("X-Cache", "MISS")
    w.Write(data)
}
Enter fullscreen mode Exit fullscreen mode

Middleware Chaining

package middleware

import (
    "log"
    "net/http"
    "time"
)

// Logger logs each request with method, path, status, and duration.
func Logger(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        rw := &responseWriter{ResponseWriter: w, status: http.StatusOK}
        next.ServeHTTP(rw, r)
        log.Printf("%s %s %d %v", r.Method, r.URL.Path, rw.status, time.Since(start))
    })
}

// CORS adds permissive CORS headers for API access.
func CORS(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Access-Control-Allow-Origin", "*")
        w.Header().Set("Access-Control-Allow-Methods", "GET, OPTIONS")
        if r.Method == http.MethodOptions {
            w.WriteHeader(http.StatusNoContent)
            return
        }
        next.ServeHTTP(w, r)
    })
}
Enter fullscreen mode Exit fullscreen mode

Main Server

func main() {
    ctx := context.Background()
    pool, _ := pgxpool.New(ctx, os.Getenv("DATABASE_URL"))
    cache := handlers.NewCache()

    mux := http.NewServeMux()
    mux.Handle("/trending/", &handlers.TrendingHandler{Pool: pool, Cache: cache})
    mux.Handle("/search", &handlers.SearchHandler{Pool: pool, Cache: cache})
    mux.Handle("/categories", &handlers.CategoriesHandler{Pool: pool, Cache: cache})

    handler := middleware.Logger(middleware.CORS(mux))

    srv := &http.Server{
        Addr:         ":8080",
        Handler:      handler,
        ReadTimeout:  5 * time.Second,
        WriteTimeout: 10 * time.Second,
        IdleTimeout:  60 * time.Second,
    }
    log.Printf("TopVideoHub Go API serving on :8080")
    log.Fatal(srv.ListenAndServe())
}
Enter fullscreen mode Exit fullscreen mode

This Go API for TopVideoHub handles concurrent requests for all 9 Asia-Pacific regions efficiently. The in-memory cache layer prevents repeated database hits for trending data that changes only hourly. Go strings are UTF-8 natively, so Korean, Japanese, and Chinese video titles are serialized correctly to JSON without any special handling.


This article is part of the Building TopVideoHub series. Check out TopVideoHub to see these techniques in action.

Source: dev.to

arrow_back Back to Tutorials