GoGo · Lesson 7 of 8
Mini Project: HTTP Server
Go's standard library includes a production-quality HTTP server. No frameworks needed for the basics. Let's build one.
Go
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"strconv"
"sync"
"time"
)
type Task struct {
ID int `json:"id"`
Text string `json:"text"`
Done bool `json:"done"`
CreatedAt time.Time `json:"created_at"`
}
type Store struct {
mu sync.Mutex
tasks []Task
nextID int
}
func (s *Store) Add(text string) Task {
s.mu.Lock()
defer s.mu.Unlock()
s.nextID++
t := Task{ID: s.nextID, Text: text, Done: false, CreatedAt: time.Now()}
s.tasks = append(s.tasks, t)
return t
}
func (s *Store) List() []Task {
s.mu.Lock()
defer s.mu.Unlock()
result := make([]Task, len(s.tasks))
copy(result, s.tasks)
return result
}
func (s *Store) Complete(id int) bool {
s.mu.Lock()
defer s.mu.Unlock()
for i := range s.tasks {
if s.tasks[i].ID == id {
s.tasks[i].Done = true
return true
}
}
return false
}
func writeJSON(w http.ResponseWriter, status int, v any) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
json.NewEncoder(w).Encode(v)
}
func main() {
store := &Store{nextID: 0}
http.HandleFunc("GET /tasks", func(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusOK, store.List())
})
http.HandleFunc("POST /tasks", func(w http.ResponseWriter, r *http.Request) {
var body struct{ Text string `json:"text"` }
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
http.Error(w, "Bad request", http.StatusBadRequest)
return
}
task := store.Add(body.Text)
writeJSON(w, http.StatusCreated, task)
})
http.HandleFunc("PUT /tasks/{id}/done", 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 !store.Complete(id) {
http.Error(w, "Not found", http.StatusNotFound)
return
}
writeJSON(w, http.StatusOK, map[string]string{"status": "ok"})
})
addr := ":8080"
fmt.Printf("Server running on http://localhost%s\n", addr)
log.Fatal(http.ListenAndServe(addr, nil))
}Bash
go run main.go
# In another terminal:
curl -X POST http://localhost:8080/tasks -H "Content-Type: application/json" -d '{"text":"Learn Go"}'
curl http://localhost:8080/tasks
curl -X PUT http://localhost:8080/tasks/1/done◆ Note
sync.Mutex protects the task list from concurrent access. Go servers handle each request in its own goroutine, so shared state must be protected. This is a simple example — production code would use a database.
sync.Mutex protects the task list from concurrent access. Go servers handle each request in its own goroutine, so shared state must be protected. This is a simple example — production code would use a database.