Bookmarks API
REST handlers for bookmarks with JSON helpers.
package main
import (
"encoding/json"
"fmt"
"log"
"log/slog"
"net/http"
"time"
)
type Bookmark struct {
ID string `json:"id"`
URL string `json:"url"`
Title string `json:"title"`
}
var bookmarks = []Bookmark{
{ID: "1", URL: "https://go.dev", Title: "Go"},
{ID: "2", URL: "https://pkg.go.dev", Title: "Go Packages"},
}
func writeJSON(w http.ResponseWriter, status int, data any) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
json.NewEncoder(w).Encode(data)
}
func writeError(w http.ResponseWriter, status int, message string) {
writeJSON(w, status, map[string]string{"error": message})
}
func listBookmarks(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusOK, bookmarks)
}
func getBookmark(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
for _, b := range bookmarks {
if b.ID == id {
writeJSON(w, http.StatusOK, b)
return
}
}
writeError(w, http.StatusNotFound, "bookmark not found")
}
func createBookmark(w http.ResponseWriter, r *http.Request) {
var input struct {
URL string `json:"url"`
Title string `json:"title"`
}
if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
writeError(w, http.StatusBadRequest, "invalid JSON")
return
}
if input.URL == "" {
writeError(w, http.StatusBadRequest, "url is required")
return
}
b := Bookmark{
ID: fmt.Sprintf("%d", time.Now().UnixNano()),
URL: input.URL,
Title: input.Title,
}
bookmarks = append(bookmarks, b)
slog.Info("bookmark created", "id", b.ID, "url", b.URL)
writeJSON(w, http.StatusCreated, b)
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("GET /bookmarks", listBookmarks)
mux.HandleFunc("GET /bookmarks/{id}", getBookmark)
mux.HandleFunc("POST /bookmarks", createBookmark)
slog.Info("listening", "addr", ":8080")
log.Fatal(http.ListenAndServe(":8080", mux))
}