diff --git a/services/api/.golangci-lint.yml b/services/api/.golangci-lint.yml new file mode 100644 index 00000000..2d6c7d6e --- /dev/null +++ b/services/api/.golangci-lint.yml @@ -0,0 +1,13 @@ +run: + timeout: 5m + +linters: + enable: + - govet + - revive + - staticcheck + - errcheck + - ineffassign + +issues: + exclude-use-default: false diff --git a/services/api/Dockerfile b/services/api/Dockerfile new file mode 100644 index 00000000..610e3c3a --- /dev/null +++ b/services/api/Dockerfile @@ -0,0 +1,12 @@ +# Builder +FROM golang:1.22-alpine AS builder +WORKDIR /app +COPY . . +RUN go mod tidy +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o server ./cmd/server + +# Runtime +FROM gcr.io/distroless/base-debian12 +COPY --from=builder /app/server /server +EXPOSE 8080 +ENTRYPOINT ["/server"] diff --git a/services/api/Makefile b/services/api/Makefile new file mode 100644 index 00000000..d95dd021 --- /dev/null +++ b/services/api/Makefile @@ -0,0 +1,16 @@ +BINARY=server + +build: + go build -o bin/$(BINARY) ./cmd/server + +run: + go run ./cmd/server + +test: + go test ./... + +fmt: + go fmt ./... + +lint: + golangci-lint run diff --git a/services/api/README.md b/services/api/README.md new file mode 100644 index 00000000..61396ebc --- /dev/null +++ b/services/api/README.md @@ -0,0 +1,125 @@ +# Nester API + +Production-grade Go backend service with clean architecture foundations. + +## Overview + +The Nester backend is transitioning from Node.js/Express to Go to support long-term scalability and align with ecosystem standards. This service serves as the authoritative API layer for vault, user, and offramp domains. + +## Directory Structure + +### `cmd/` +**Entrypoints only.** Contains `server/main.go` — application bootstrap and port resolution. No domain logic here. + +### `internal/` +**Private domain logic.** Not importable by external packages. + +- `config/` — configuration loading and validation +- `domain/` — domain entities and business logic + - `vault/` — vault operations and state + - `user/` — user identity and auth + - `offramp/` — off-ramp transaction processing +- `handler/` — HTTP request handlers +- `middleware/` — request/response middleware +- `repository/` — data access layer +- `service/` — business logic orchestration + +### `pkg/` +**Reusable utilities.** Importable by external packages. + +- `response/` — standard HTTP response formatting +- `validator/` — input validation helpers + +### `migrations/` +**Database schema evolution.** Version-controlled SQL or Go migration scripts. + +## Local Development + +### Prerequisites +- Go 1.22+ +- golangci-lint (optional, for linting) + +### Running the server + +```bash +make run +``` + +Server starts on `http://localhost:8080` (or `$PORT` if set). + +### Health check + +```bash +curl http://localhost:8080/healthz +# Returns: ok (200) +``` + +### Building + +```bash +make build +``` + +Binary written to `bin/server`. + +### Testing + +```bash +make test +``` + +### Code quality + +Format code: +```bash +make fmt +``` + +Lint: +```bash +make lint +``` + +## Docker + +### Build + +```bash +docker build -t nester-api . +``` + +Multi-stage build ensures minimal runtime image (~15MB). + +### Run + +```bash +docker run -p 8080:8080 nester-api +``` + +Set port with environment variable: +```bash +docker run -p 9000:9000 -e PORT=9000 nester-api +``` + +## Architecture Philosophy + +**Clean Architecture.** Domain logic is isolated from HTTP, database, and framework concerns. Changes to the web layer do not cascade into domain code. + +**Domain Isolation.** `internal/domain/*` packages own their boundaries. Cross-domain dependencies flow only through service/handler layers. + +**Minimal Abstraction.** Uses `net/http` directly instead of frameworks. Avoids premature indirection. Repository pattern introduced only where data access is complex. + +**Simplicity First.** No dependency injection containers, no global singletons, no reflection-based magic. Code is explicit and traceable. + +## Future Phases + +1. **Database layer** — PostgreSQL integration via `internal/repository` +2. **Auth middleware** — JWT validation in `internal/middleware` +3. **Vault domain** — Core business logic in `internal/domain/vault` +4. **API v1** — Public endpoints in `internal/handler` +5. **Integration tests** — End-to-end validation + +--- + +*Go version: 1.22+* +*Module: github.com/Suncrest-Labs/nester* diff --git a/services/api/bin/server b/services/api/bin/server new file mode 100644 index 00000000..c9d5edb1 Binary files /dev/null and b/services/api/bin/server differ diff --git a/services/api/cmd/server/main.go b/services/api/cmd/server/main.go new file mode 100644 index 00000000..7662ba21 --- /dev/null +++ b/services/api/cmd/server/main.go @@ -0,0 +1,37 @@ +package main + +import ( + "log" + "net/http" + "os" +) + +func main() { + port := resolvePort() + + mux := http.NewServeMux() + mux.HandleFunc("/healthz", healthHandler) + + server := &http.Server{ + Addr: ":" + port, + Handler: mux, + } + + log.Printf("server starting on :%s", port) + + if err := server.ListenAndServe(); err != nil { + log.Fatalf("server failed: %v", err) + } +} + +func resolvePort() string { + if p := os.Getenv("PORT"); p != "" { + return p + } + return "8080" +} + +func healthHandler(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte("ok")) +} diff --git a/services/api/go.mod b/services/api/go.mod new file mode 100644 index 00000000..0a4c4491 --- /dev/null +++ b/services/api/go.mod @@ -0,0 +1,3 @@ +module github.com/Suncrest-Labs/nester + +go 1.22 diff --git a/services/api/server.exe b/services/api/server.exe new file mode 100644 index 00000000..c9d5edb1 Binary files /dev/null and b/services/api/server.exe differ