Files
Arkie-Library-Backend/docs/GO_FOR_BEGINNERS.md
2026-05-18 07:56:27 +08:00

4.5 KiB

Go Beginner Guide for This Backend

This is not a full Go course. It explains only the Go ideas you need to read this project.

1. How a Go project starts

This repo has a go.mod file:

module github.com/arkie/ark-database

That module name is used in imports:

import "github.com/arkie/ark-database/internal/config"

The executable starts at:

cmd/server/main.go

In Go, a runnable program has:

package main

func main() {
    // program starts here
}

2. Packages

Every .go file begins with a package name:

package handlers

Files in the same folder usually share the same package and can call each other directly.

Example: files in internal/handlers/ all say package handlers, so public.go can call writeJSON from util.go.

3. Imports

Go imports are explicit:

import (
    "net/http"
    "github.com/go-chi/chi/v5"
)

If an import is unused, Go compilation fails. Run gofmt -w . after edits.

4. Structs

A struct is an object/data shape.

Example from internal/config/config.go:

type Config struct {
    Addr        string
    DatabaseURL string
    JWTSecret   string
}

JSON field names are controlled by tags:

type ResourceDTO struct {
    CategoryID int `json:"categoryId"`
}

The Go field is CategoryID, but JSON output is categoryId.

5. Functions with errors

Go commonly returns (value, error):

pool, err := db.Connect(ctx, cfg.DatabaseURL)
if err != nil {
    log.Fatal(err)
}

Read this as:

  1. Try connecting to DB.
  2. If err is not nil, stop.
  3. Otherwise use pool.

6. Short variable declaration :=

This creates a new variable:

cfg := config.Load()

This assigns to an existing variable:

cfg = config.Load()

Most code here uses := inside functions.

7. HTTP handlers

A Go HTTP handler usually looks like:

func ListCategories(w http.ResponseWriter, r *http.Request) {
    // w writes response
    // r reads request
}
  • w = response writer.
  • r = request.

Route registration in main.go:

r.Get("/categories", handlers.ListCategories)

Because this is inside r.Route("/api", ...), the full URL is:

GET /api/categories

8. Middleware

Middleware wraps a request before it reaches the final handler.

Example in main.go:

r.Use(middleware.Logger)

This logs requests.

Admin auth is also middleware:

r.Use(handlers.AdminAuth(cfg.JWTSecret))

If token is bad, it returns 401 unauthorized before reaching the admin handler.

9. Context

Context carries request-scoped values and cancellation.

This project stores the DB pool in request context:

r.Use(func(next http.Handler) http.Handler {
    return handlers.WithPool(next, pool)
})

Handlers retrieve it:

pool := poolFrom(r)

So most handlers do not receive the DB pool as a direct function parameter.

10. Database queries

This project uses pgx.

One row:

err := pool.QueryRow(ctx, `SELECT id FROM admins WHERE email = $1`, email).Scan(&id)

Many rows:

rows, err := pool.Query(ctx, `SELECT id, title FROM resources`)
defer rows.Close()
for rows.Next() {
    rows.Scan(&id, &title)
}

PostgreSQL parameters use $1, $2, etc. This protects from SQL injection when used correctly.

11. Pointers and nil

You will see *string and *time.Time:

var pubAt *time.Time

This allows SQL NULL to become Go nil.

Code checks before using it:

if pubAt != nil {
    s := pubAt.UTC().Format(time.RFC3339)
    dto.PublishedAt = &s
}

12. defer

defer runs later when the current function returns.

Examples:

defer pool.Close()
defer rows.Close()
defer r.Body.Close()

Use it for cleanup.

13. JSON helpers in this project

Write JSON response:

writeJSON(w, map[string]any{"ok": true})

Read JSON request body:

var req loginReq
if err := jsonDecode(r, &req); err != nil {
    http.Error(w, "bad json", http.StatusBadRequest)
    return
}

14. Common edit workflow

After changing .go files:

gofmt -w .
go test ./...

go test ./... also compiles all packages, even if there are no test files.

  1. README.md
  2. cmd/server/main.go
  3. internal/config/config.go
  4. internal/handlers/public.go
  5. internal/handlers/admin.go
  6. internal/handlers/wallet_auth.go
  7. migrations/001_init.sql

When you see a function you do not understand, search for its definition:

rg "func FunctionName" .