Introduction: Why Go for REST API?
\nGo (Golang) is a modern compiled language with built-in concurrency support and high performance. A REST API in Go runs fast, consumes little memory, and scales easily. In 30 minutes, you will create a full-fledged REST service with GET, POST, PUT, and DELETE requests, using only the standard library.
\nIn this guide, we will write an API for managing a task list (Todo app). You will learn how to set up routing, handle JSON, add middleware for logging, and correctly return errors. No third-party frameworks — just pure Go!
\n\n1. Project Setup and Structure
\nCreate a new directory for the project and initialize the Go module:
\nmkdir go-todo-api\ncd go-todo-api\ngo mod init todo-api\nThe project structure will be simple:
\n- \n
main.go— entry point and server startup \nhandlers.go— request handlers \nmodels.go— data structures \nmiddleware.go— middleware \n
Create the models.go file with the task structure:
package main\n\ntype Task struct {\n ID int `json:"id"`\n Title string `json:"title"`\n Completed bool `json:"completed"`\n}\nWe will store tasks in memory (a tasks slice) for simplicity. In a real project, use a database.
\n\n2. Routing and Handlers
\nGo uses the built-in net/http package. Let's create the handlers.go file with the main handlers.
2.1. GET /tasks — Get all tasks
\npackage main\n\nimport (\n "encoding/json"\n "net/http"\n "strconv"\n)\n\nvar tasks = []Task{}\nvar nextID = 1\n\nfunc getTasks(w http.ResponseWriter, r *http.Request) {\n w.Header().Set("Content-Type", "application/json")\n json.NewEncoder(w).Encode(tasks)\n}\n2.2. POST /tasks — Create a new task
\nfunc createTask(w http.ResponseWriter, r *http.Request) {\n var newTask Task\n if err := json.NewDecoder(r.Body).Decode(&newTask); err != nil {\n http.Error(w, "Invalid JSON", http.StatusBadRequest)\n return\n }\n newTask.ID = nextID\n nextID++\n tasks = append(tasks, newTask)\n w.Header().Set("Content-Type", "application/json")\n w.WriteHeader(http.StatusCreated)\n json.NewEncoder(w).Encode(newTask)\n}\n2.3. PUT /tasks/{id} — Update a task
\nfunc updateTask(w http.ResponseWriter, r *http.Request) {\n idStr := r.PathValue("id")\n id, err := strconv.Atoi(idStr)\n if err != nil {\n http.Error(w, "Invalid task ID", http.StatusBadRequest)\n return\n }\n \n var updatedTask Task\n if err := json.NewDecoder(r.Body).Decode(&updatedTask); err != nil {\n http.Error(w, "Invalid JSON", http.StatusBadRequest)\n return\n }\n \n for i, t := range tasks {\n if t.ID == id {\n tasks[i].Title = updatedTask.Title\n tasks[i].Completed = updatedTask.Completed\n w.Header().Set("Content-Type", "application/json")\n json.NewEncoder(w).Encode(tasks[i])\n return\n }\n }\n http.Error(w, "Task not found", http.StatusNotFound)\n}\n2.4. DELETE /tasks/{id} — Delete a task
\nfunc deleteTask(w http.ResponseWriter, r *http.Request) {\n idStr := r.PathValue("id")\n id, err := strconv.Atoi(idStr)\n if err != nil {\n http.Error(w, "Invalid task ID", http.StatusBadRequest)\n return\n }\n \n for i, t := range tasks {\n if t.ID == id {\n tasks = append(tasks[:i], tasks[i+1:]...)\n w.WriteHeader(http.StatusNoContent)\n return\n }\n }\n http.Error(w, "Task not found", http.StatusNotFound)\n}\nPlease note