ClaudeGate: wrapping Claude Code CLI as an async REST API in Go
How I built ClaudeGate: an HTTP gateway in Go that wraps Claude Code CLI with an async job queue, SSE streaming, webhooks, and SQLite persistence — single static binary, no CGO.
Lessons learned, problems and solutions
How I built ClaudeGate: an HTTP gateway in Go that wraps Claude Code CLI with an async job queue, SSE streaming, webhooks, and SQLite persistence — single static binary, no CGO.
EventSource doesn't support custom headers — no way to send X-API-Key. How to replace it with fetch + ReadableStream for authenticated SSE streaming, with a manual parser and AbortController.
Goroutine leaks don't crash the program — they silently degrade it. The 4 patterns that systematically leak, detection with pprof and goleak, fixes with context and channels.
The Go proverb "accept interfaces, return structs" explained with production code. When it applies, when it hurts, and the concrete cases most tutorials skip.
Three Go error patterns explained through a concrete ClaudeGate code review case. When to create a sentinel error, an error struct, or just use fmt.Errorf.
Propagating errors from goroutines, collecting all failures from a batch, and surviving panics without crashing the program. The essential patterns for robust concurrent code.
Mastering Go channels: buffered vs unbuffered, select, fan-out, fan-in and worker pool. The essential patterns for making your goroutines communicate without race conditions.
Learning Go parallelism from scratch: goroutines, sync.WaitGroup, the classic loop closure trap and the race detector. With a concrete example — 10 URLs in 1s instead of 10s.
The concurrency vs parallelism distinction explained with a simple analogy, and how Go + Event Sourcing / CQRS leverage both: aggregate single-writer, fan-out projections, pipeline with backpressure.
Four words everyone mixes up. What they precisely mean, how Go expresses them, and how synchronous/asynchronous and concurrent/parallel work together.