Skip to content

Software Engineering

Go for backend beginners: a practical path from zero to a working API

A learning path for Go aimed at backend work: setup, the language features you need, a small HTTP API with PostgreSQL, tests, and the mistakes beginners make.

By · Published · 3 min read

Short answer: install Go, do the official Tour of Go in an evening, then build one small HTTP API that stores data in PostgreSQL, using only the standard library plus a database driver. Write tests as you go. That path covers the language features you will use daily and teaches the habits Go expects.

Why learn Go for backend work?

  • It compiles to a single static binary, which makes deployment simple.
  • The standard library includes a production-grade HTTP server and JSON handling.
  • Concurrency is built in.
  • The language is small, and codebases look alike, so reading other people's code is easier.
  • Startup time and memory use are low.

How do you set up?

# download from go.dev/dl, then
go version
mkdir hello && cd hello
go mod init example.com/hello

go mod init creates go.mod, which tracks your module and dependencies. There is no separate package manager to learn.

Which language features matter first?

  • Types and structs, instead of classes. Methods attach to types.
  • Interfaces, satisfied implicitly. If a type has the methods, it implements the interface. Keep them small.
  • Errors as values. Functions return (result, error), and you check if err != nil. It is repetitive, and it makes failure paths explicit.
  • Slices and maps.
  • `defer`, to run cleanup when a function returns.
  • Packages and exported names: capitalised means public.
  • Goroutines and channels, later. See concurrency patterns.

What does a small API look like?

package main

import (
    "encoding/json"
    "log"
    "net/http"
)

type Health struct {
    Status string `json:"status"`
}

func main() {
    mux := http.NewServeMux()

    mux.HandleFunc("GET /healthz", func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type", "application/json")
        json.NewEncoder(w).Encode(Health{Status: "ok"})
    })

    mux.HandleFunc("GET /items/{id}", func(w http.ResponseWriter, r *http.Request) {
        id := r.PathValue("id")
        w.Write([]byte("item " + id))
    })

    log.Println("listening on :8080")
    log.Fatal(http.ListenAndServe(":8080", mux))
}

Since Go 1.22 the standard router supports methods and path parameters, so you can build a real API without a framework.

How do you talk to PostgreSQL?

Use pgx, the most widely used PostgreSQL driver for Go, with its connection pool.

pool, err := pgxpool.New(ctx, os.Getenv("DATABASE_URL"))
if err != nil { log.Fatal(err) }
defer pool.Close()

var name string
err = pool.QueryRow(ctx, "SELECT name FROM customers WHERE id = $1", id).Scan(&name)
if errors.Is(err, pgx.ErrNoRows) {
    http.NotFound(w, r)
    return
}

Always use placeholders for values. See how to prevent SQL injection. Keep an eye on pool sizing too, covered in connection pooling.

How do you test?

func TestHealth(t *testing.T) {
    req := httptest.NewRequest("GET", "/healthz", nil)
    rec := httptest.NewRecorder()
    handler.ServeHTTP(rec, req)
    if rec.Code != 200 {
        t.Fatalf("got %d", rec.Code)
    }
}

Run go test ./.... Table-driven tests, where a slice of cases is looped over, are the usual style.

Which tools come with Go?

  • go fmt formats code in one standard way. No style arguments.
  • go vet finds suspicious constructs.
  • go test -race finds data races.
  • go build produces the binary, with cross-compilation through GOOS and GOARCH.

What mistakes do beginners make?

  • Ignoring returned errors, or using _ for them.
  • Using a global database handle and global state everywhere.
  • Overusing channels where a mutex or plain function call would be clearer.
  • Creating interfaces before there are two implementations.
  • Writing Java-style layers of abstraction. Prefer small packages and plain functions.
  • Forgetting that a nil map panics on write.

When your API works, package it: deploying a Go app with Docker.

References

Author

Raktim Ranjit is a software engineer and the founder of NodeDR Infotech. He builds and maintains the software described here.

Have something in mind?

Let’s build something useful.

Tell me about the idea, product, or workflow you’re working through.

Tap to say hello