handler_html.go — HTML Handlers

Server-rendered handlers for the browser UI. Parses templates at startup, renders the list page, and handles form submissions with Post/Redirect/Get.

package main

import (
	"html/template"
	"log/slog"
	"net/http"
	"strconv"
	"time"
)

type PageData struct {
	Title     string
	Bookmarks []Bookmark
	Count     int
}

func parseTemplates() *template.Template {
	funcMap := template.FuncMap{
		"formatTime": func(t time.Time) string {
			return t.Format("Jan 02, 2006")
		},
	}
	return template.Must(
		template.New("layout.html").Funcs(funcMap).ParseFiles(
			"templates/layout.html",
			"templates/list.html",
		),
	)
}

func handleBookmarksList(tmpl *template.Template, store *BookmarkStore) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		bookmarks, err := store.List(r.Context())
		if err != nil {
			slog.Error("list bookmarks", "err", err)
			http.Error(w, "internal error", http.StatusInternalServerError)
			return
		}
		data := PageData{
			Title:     "Bookmarks",
			Bookmarks: bookmarks,
			Count:     len(bookmarks),
		}
		w.Header().Set("Content-Type", "text/html; charset=utf-8")
		if err := tmpl.Execute(w, data); err != nil {
			slog.Error("template execute", "err", err)
		}
	}
}

func handleBookmarksCreate(store *BookmarkStore) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		title := r.FormValue("title")
		url := r.FormValue("url")
		if title == "" || url == "" {
			http.Error(w, "title and url are required", http.StatusBadRequest)
			return
		}
		if _, err := store.Create(r.Context(), url, title); err != nil {
			slog.Error("create bookmark", "err", err)
			http.Error(w, "internal error", http.StatusInternalServerError)
			return
		}
		http.Redirect(w, r, "/", http.StatusSeeOther)
	}
}

func handleBookmarksDelete(store *BookmarkStore) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		id, err := strconv.Atoi(r.PathValue("id"))
		if err != nil {
			http.Error(w, "invalid id", http.StatusBadRequest)
			return
		}
		if err := store.Delete(r.Context(), id); err != nil {
			slog.Error("delete bookmark", "err", err)
			http.Error(w, "internal error", http.StatusInternalServerError)
			return
		}
		http.Redirect(w, r, "/", http.StatusSeeOther)
	}
}

💻 Run locally

Copy the code above and run it on your machine

© 2026 ByteLearn.dev. Free courses for developers. · Privacy