backend gogolangrestapihttpbackendserver

go-rest-api

Build production REST APIs in Go with the standard library or Gin, including routing, middleware, JSON handling, and testing. Use for performant backends.

Quality 100% Code blocks 6 Sections 8 Lines 136 RU translation yes
view source
Models: sonnetopusgpt-6gemini-3glm-5

Go REST API

Building fast, concurrent REST APIs in Go.

Quick Start

go mod init example.com/myapi
go run .  # http://localhost:8080

When to Use

  • High-concurrency services and backends
  • Microservices needing small, static binaries
  • Teams wanting simple concurrency primitives
  • CLIs and tools that double as libraries

Best Practices

Structure

  • Organize by feature: `cmd/`, `internal/`, `pkg/`
  • Keep handlers thin; move logic to services
  • Use `net/http` with Go 1.22+ route patterns or a router
  • Define request/response types explicitly

Concurrency

  • Use goroutines for independent work; channel for coordination
  • Use `context.Context` for cancellation and deadlines
  • Bound concurrency with semaphores or worker pools
  • Protect shared state with `sync.Mutex` or atomics

Errors & Logging

  • Wrap errors with `fmt.Errorf("%w", err)` and `errors.Is`
  • Log structured with `slog`; include request ID
  • Return proper HTTP status codes and JSON errors

Dependencies

go mod init example.com/myapi
go get github.com/gin-gonic/gin   # optional router

Examples

package main

import (
	"fmt"
	"net/http"
)

func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("GET /health", healthHandler)
	mux.HandleFunc("POST /items", createItem)
	http.ListenAndServe(":8080", logMiddleware(mux))
}

func healthHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	fmt.Fprint(w, `{"status":"ok"}`)
}
// Gin-style handler with JSON binding
package main

import "github.com/gin-gonic/gin"

type Item struct {
	Name  string `json:"name" binding:"required"`
	Price float64 `json:"price" binding:"required"`
}

func createItem(c *gin.Context) {
	var item Item
	if err := c.ShouldBindJSON(&item); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusCreated, gin.H{"ok": true, "item": item})
}
// Context-aware handler with timeout
func slowHandler(ctx context.Context) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		select {
		case <-time.After(2 * time.Second):
			fmt.Fprint(w, "done")
		case <-ctx.Done():
			w.WriteHeader(http.StatusGatewayTimeout)
		}
	}
}
// Middleware pattern
func logMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		slog.Info("request", "method", r.Method, "path", r.URL.Path)
		next.ServeHTTP(w, r)
	})
}

Step-by-Step

1. Initialize the module and pick a routing approach (stdlib patterns or Gin).

2. Sketch the API contract: routes, methods, request/response JSON.

3. Define explicit request/response structs with `json` tags.

4. Implement handlers; keep them thin and delegate to services.

5. Add middleware for logging, recovery, CORS, and auth.

6. Use `context.Context` everywhere; set server timeouts.

7. Write table-driven tests with `httptest`.

8. Add linters (golangci-lint), staticcheck, and `go vet` to CI.

Validation

1. `go build ./...` succeeds

2. `go test ./...` passes

3. `go vet ./...` clean

4. Endpoints return correct status codes and JSON shape

5. `go test -race ./...` shows no data races

Troubleshooting

  • Data races: guard shared state with mutexes or use channels.
  • Context canceled: honor `ctx.Done()` in long operations.
  • High latency: add timeouts on the server and on outbound clients.