How to Write a REST API in Go in 30 Minutes: A Practical Guide

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction: Why Go is the Ideal Choice for REST API

Go (Golang) is a compiled programming language created at Google for high-load systems. It combines the speed of C, the simplicity of Python, and built-in concurrency support. For creating REST APIs, Go offers a powerful standard library net/http and popular frameworks such as gorilla/mux and gin.

In this guide, we will create a full-fledged REST API for managing a Todo List in pure Go without third-party frameworks in 30 minutes. You will learn:

  • How to set up routing and handlers
  • How to work with JSON (serialization/deserialization)
  • How to implement CRUD operations (Create, Read, Update, Delete)
  • How to add middleware for logging and CORS

All the code will be written in a single file main.go for simplicity. Let's get started!



Step 1: Basic Structure and Starting the HTTP Server

Create a file main.go and add the following code. We will declare a data structure for a task and start a simple server.

package main

import ( "encoding/json" "log" "net/http" "strconv" "strings" "sync" "time")

// Task — task structuretype Task struct { ID int `json:"id"` Title string `json:"title"` Completed bool `json:"completed"` CreatedAt time.Time `json:"created_at"`}

// Task storage (in-memory)var ( tasks = make(map[int]Task) nextID = 1 mu sync.Mutex)

func main() { // Routes http.HandleFunc("/api/tasks", tasksHandler) http.HandleFunc("/api/tasks/", taskByIDHandler)

log.Println("Server started on :8080") log.Fatal(http.ListenAndServe(":8080", nil))}

Explanation:

  • Task — a structure with JSON tags for correct serialization.
  • tasks — a map for storing tasks in memory (no database).
  • mu — a mutex for safe access from goroutines.
  • Two routes: /api/tasks (list and create) and /api/tasks/ (work with a single task by ID).


Step 2: Handlers for the Task List (GET and POST)

Let's implement the tasksHandler function, which handles GET (retrieving all tasks) and POST (creating a new task).

func tasksHandler(w http.ResponseWriter, r *http.Request) {    w.Header().Set("Content-Type", "application/json")

switch r.Method { case http.MethodGet: mu.Lock() taskList := make([]Task, 0, len(tasks)) for _, task := range tasks { taskList = append(taskList, task) } mu.Unlock() json.NewEncoder(w).Encode(taskList)

case http.MethodPost: var newTask Task if err := json.NewDecoder(r.Body).Decode(&newTask); err != nil { http.Error(w, `{"error":"Invalid JSON"}`, http.StatusBadRequest) return } mu.Lock() newTask.ID = nextID nextID++ newTask.CreatedAt = time.Now() tasks[newTask.ID] = newTask mu.Unlock()

w.WriteHeader(http.StatusCreated) json.NewEncoder(w).Encode(newTask)

default: http.Error(w, `{"error":"Method not supported"}`, http.StatusMethodNotAllowed) }}

Key points:

  • We use json.NewEncoder and json.NewDecoder for working with JSON.
  • On POST, we check the JSON validity, assign an ID, and return 201 Created.
  • The mutex protects data during concurrent access.


Step 3: Handlers for a Single Task (GET, PUT, DELETE)

Now let's implement taskByIDHandler. We extract the ID from the URL using strings.TrimPrefix

Blogs

Book Recommendations