docs: add backend onboarding guide
This commit is contained in:
281
docs/GO_FOR_BEGINNERS.md
Normal file
281
docs/GO_FOR_BEGINNERS.md
Normal file
@@ -0,0 +1,281 @@
|
||||
# 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:
|
||||
|
||||
```go
|
||||
module github.com/arkie/ark-database
|
||||
```
|
||||
|
||||
That module name is used in imports:
|
||||
|
||||
```go
|
||||
import "github.com/arkie/ark-database/internal/config"
|
||||
```
|
||||
|
||||
The executable starts at:
|
||||
|
||||
```text
|
||||
cmd/server/main.go
|
||||
```
|
||||
|
||||
In Go, a runnable program has:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
func main() {
|
||||
// program starts here
|
||||
}
|
||||
```
|
||||
|
||||
## 2. Packages
|
||||
|
||||
Every `.go` file begins with a package name:
|
||||
|
||||
```go
|
||||
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:
|
||||
|
||||
```go
|
||||
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`:
|
||||
|
||||
```go
|
||||
type Config struct {
|
||||
Addr string
|
||||
DatabaseURL string
|
||||
JWTSecret string
|
||||
}
|
||||
```
|
||||
|
||||
JSON field names are controlled by tags:
|
||||
|
||||
```go
|
||||
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)`:
|
||||
|
||||
```go
|
||||
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:
|
||||
|
||||
```go
|
||||
cfg := config.Load()
|
||||
```
|
||||
|
||||
This assigns to an existing variable:
|
||||
|
||||
```go
|
||||
cfg = config.Load()
|
||||
```
|
||||
|
||||
Most code here uses `:=` inside functions.
|
||||
|
||||
## 7. HTTP handlers
|
||||
|
||||
A Go HTTP handler usually looks like:
|
||||
|
||||
```go
|
||||
func ListCategories(w http.ResponseWriter, r *http.Request) {
|
||||
// w writes response
|
||||
// r reads request
|
||||
}
|
||||
```
|
||||
|
||||
- `w` = response writer.
|
||||
- `r` = request.
|
||||
|
||||
Route registration in `main.go`:
|
||||
|
||||
```go
|
||||
r.Get("/categories", handlers.ListCategories)
|
||||
```
|
||||
|
||||
Because this is inside `r.Route("/api", ...)`, the full URL is:
|
||||
|
||||
```text
|
||||
GET /api/categories
|
||||
```
|
||||
|
||||
## 8. Middleware
|
||||
|
||||
Middleware wraps a request before it reaches the final handler.
|
||||
|
||||
Example in `main.go`:
|
||||
|
||||
```go
|
||||
r.Use(middleware.Logger)
|
||||
```
|
||||
|
||||
This logs requests.
|
||||
|
||||
Admin auth is also middleware:
|
||||
|
||||
```go
|
||||
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:
|
||||
|
||||
```go
|
||||
r.Use(func(next http.Handler) http.Handler {
|
||||
return handlers.WithPool(next, pool)
|
||||
})
|
||||
```
|
||||
|
||||
Handlers retrieve it:
|
||||
|
||||
```go
|
||||
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:
|
||||
|
||||
```go
|
||||
err := pool.QueryRow(ctx, `SELECT id FROM admins WHERE email = $1`, email).Scan(&id)
|
||||
```
|
||||
|
||||
Many rows:
|
||||
|
||||
```go
|
||||
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`:
|
||||
|
||||
```go
|
||||
var pubAt *time.Time
|
||||
```
|
||||
|
||||
This allows SQL `NULL` to become Go `nil`.
|
||||
|
||||
Code checks before using it:
|
||||
|
||||
```go
|
||||
if pubAt != nil {
|
||||
s := pubAt.UTC().Format(time.RFC3339)
|
||||
dto.PublishedAt = &s
|
||||
}
|
||||
```
|
||||
|
||||
## 12. `defer`
|
||||
|
||||
`defer` runs later when the current function returns.
|
||||
|
||||
Examples:
|
||||
|
||||
```go
|
||||
defer pool.Close()
|
||||
defer rows.Close()
|
||||
defer r.Body.Close()
|
||||
```
|
||||
|
||||
Use it for cleanup.
|
||||
|
||||
## 13. JSON helpers in this project
|
||||
|
||||
Write JSON response:
|
||||
|
||||
```go
|
||||
writeJSON(w, map[string]any{"ok": true})
|
||||
```
|
||||
|
||||
Read JSON request body:
|
||||
|
||||
```go
|
||||
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:
|
||||
|
||||
```bash
|
||||
gofmt -w .
|
||||
go test ./...
|
||||
```
|
||||
|
||||
`go test ./...` also compiles all packages, even if there are no test files.
|
||||
|
||||
## 15. Recommended reading order
|
||||
|
||||
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:
|
||||
|
||||
```bash
|
||||
rg "func FunctionName" .
|
||||
```
|
||||
Reference in New Issue
Block a user