本教程面向零基础开发者,手把手教你用 Claude Code + Go 语言从空白目录开始,构建一个完整的 REST API 微服务项目。你将掌握 Claude Code 的核心工作流——安装认证、CLAUDE.md 配置、对话式开发、斜杠命令——同时用 Go 标准库和 Chi 路由器实现一个可运行的书库管理 API。学完即可将同样的工作流复用到任何语言和项目。
适用人群:有基本编程概念但未用过 Claude Code 或 Go 的开发者;想体验 AI 配对编程全流程的团队负责人。
| 项目 | 最低要求 | 检查命令 |
|---|---|---|
| 操作系统 | macOS / Linux / WSL2 | uname -a |
| Node.js | v18+ | node -v |
| Go | 1.22+ | go version |
| Git | 2.30+ | git --version |
| 终端 | 任意现代终端 | — |
不需要事先熟悉 Go 语法——Claude Code 会帮你生成和解释代码。
npm install -g @anthropic-ai/claude-code
安装完成后验证:
claude --version
# 预期输出: Claude Code v2.x.x
claude
首次运行会进入交互式认证流程,支持两种方式:
ANTHROPIC_API_KEY=sk-ant-...认证成功后终端底部会出现 > 提示符,表示已进入对话模式。
flowchart TD
A[npm install -g] --> B[运行 claude]
B --> C{首次启动?}
C -->|是| D[OAuth 浏览器登录]
C -->|否| E[直接进入对话]
D --> F[授权成功]
F --> E
E --> G["> 提示符就绪"]
| 问题 | 原因 | 解决 |
|---|---|---|
command not found: claude |
npm 全局路径未加入 PATH | npm config get prefix 检查路径,加入 ~/.bashrc |
EACCES permission denied |
npm 全局目录权限不足 | 使用 npx @anthropic-ai/claude-code 代替,或修复 npm 全局目录权限 |
| 浏览器打不开 OAuth 页面 | 无图形环境(SSH/容器) | 使用 API Key 模式:export ANTHROPIC_API_KEY=sk-ant-... |
mkdir bookshelf-api && cd bookshelf-api
git init
claude
在 Claude Code 对话中输入:
/init
Claude 会分析当前目录结构并生成一份 CLAUDE.md 草案。因为是空项目,我们需要手动补充项目规范。告诉 Claude:
这是一个 Go 语言 REST API 项目,使用 Chi 路由器和 PostgreSQL。
请生成 CLAUDE.md,包含:
- 项目结构说明
- Go 代码风格:gofmt、golint、错误处理用 fmt.Errorf 包装
- 测试要求:每个 handler 都要有 table-driven test
- 禁止使用 ORM,直接用 database/sql + pgx 驱动
Claude 生成的 CLAUDE.md 示例:
# Bookshelf API
Go REST API 服务,管理图书的 CRUD 操作。
## 技术栈
- Go 1.22+ / Chi v5 路由器 / PostgreSQL 16 / pgx 驱动
## 项目结构
bookshelf-api/ ├── cmd/server/main.go # 入口 ├── internal/ │ ├── handler/ # HTTP handlers │ ├── model/ # 数据模型 │ ├── repository/ # 数据库操作 │ └── service/ # 业务逻辑 ├── migrations/ # SQL 迁移文件 ├── go.mod └── CLAUDE.md
## 代码规范
- 始终运行 gofmt,用 goimports 管理导入
- 错误包装:fmt.Errorf("repository.CreateBook: %w", err)
- Handler 返回 JSON,状态码语义清晰
- 表驱动测试,覆盖正常+边界+错误路径
## 测试
- 运行: go test ./...
- 竞态检测: go test -race ./...
Claude Code 的配置文件遵循就近优先原则:
flowchart BT
A["~/.claude/CLAUDE.md<br/>全局:所有项目共享"] --> B["项目根/CLAUDE.md<br/>项目级:团队共享"]
B --> C["项目根/.claude/CLAUDE.md<br/>增强级:git 忽略的本地配置"]
C --> D["子目录/CLAUDE.md<br/>目录级:模块专属规则"]
style A fill:#e8f5e9
style B fill:#fff3e0
style C fill:#e3f2fd
style D fill:#fce4ec
理解这个层级很重要:全局 CLAUDE.md 放通用偏好(如"用中文回复"),项目级放团队规范,目录级放模块特化指令。
在 Claude Code 对话中输入:
帮我初始化这个 Go 项目:
1. 运行 go mod init github.com/example/bookshelf-api
2. 创建 CLAUDE.md 中定义的目录结构
3. 安装依赖:github.com/go-chi/chi/v5 和 github.com/jackc/pgx/v5
4. 创建 cmd/server/main.go,监听 :8080,包含健康检查端点
Claude 会依次执行:
# Step 1: 初始化 module
go mod init github.com/example/bookshelf-api
# Step 2: 创建目录
mkdir -p cmd/server internal/{handler,model,repository,service} migrations
# Step 3: 安装依赖
go get github.com/go-chi/chi/v5
go get github.com/jackc/pgx/v5/pgxpool
然后生成 cmd/server/main.go:
package main
import (
"fmt"
"log"
"net/http"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
)
func main() {
r := chi.NewRouter()
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)
r.Get("/health", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
fmt.Fprintf(w, `{"status":"ok"}`)
})
fmt.Println("Server starting on :8080")
log.Fatal(http.ListenAndServe(":8080", r))
}
请运行这个项目并用 curl 测试 /health 端点
Claude 会执行:
go run cmd/server/main.go &
# 等待启动
curl http://localhost:8080/health
# 预期输出: {"status":"ok"}
预期结果:终端显示 {"status":"ok"},确认骨架运行正常。
| 问题 | 解决 |
|---|---|
go: module not found |
运行 go mod tidy 修复依赖 |
:8080 already in use |
先 kill $(lsof -t -i:8080) 再重试 |
| import 路径红色波浪线 | 确保 go mod init 路径与 import 一致 |
在 internal/model/book.go 中定义 Book 结构体:
- ID: int64
- Title: string
- Author: string
- ISBN: string
- CreatedAt: time.Time
- UpdatedAt: time.Time
同时生成 migrations/001_create_books.sql 建表语句
Claude 生成的 internal/model/book.go:
package model
import "time"
type Book struct {
ID int64 `json:"id"`
Title string `json:"title"`
Author string `json:"author"`
ISBN string `json:"isbn"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type CreateBookRequest struct {
Title string `json:"title"`
Author string `json:"author"`
ISBN string `json:"isbn"`
}
type UpdateBookRequest struct {
Title *string `json:"title,omitempty"`
Author *string `json:"author,omitempty"`
ISBN *string `json:"isbn,omitempty"`
}
migrations/001_create_books.sql:
CREATE TABLE IF NOT EXISTS books (
id BIGSERIAL PRIMARY KEY,
title VARCHAR(255) NOT NULL,
author VARCHAR(255) NOT NULL,
isbn VARCHAR(13) UNIQUE NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_books_isbn ON books(isbn);
CREATE INDEX idx_books_author ON books(author);
实现 internal/repository/book_repo.go:
- 使用 database/sql 接口(方便测试时 mock)
- 方法:Create, GetByID, List, Update, Delete
- List 支持分页(limit/offset)和按作者过滤
- 错误用 fmt.Errorf 包装,包含方法名上下文
Claude 生成的关键片段:
package repository
import (
"context"
"database/sql"
"fmt"
"github.com/example/bookshelf-api/internal/model"
)
type BookRepository struct {
db *sql.DB
}
func NewBookRepository(db *sql.DB) *BookRepository {
return &BookRepository{db: db}
}
func (r *BookRepository) Create(ctx context.Context, book *model.CreateBookRequest) (*model.Book, error) {
query := `INSERT INTO books (title, author, isbn)
VALUES ($1, $2, $3)
RETURNING id, created_at, updated_at`
var b model.Book
b.Title = book.Title
b.Author = book.Author
b.ISBN = book.ISBN
err := r.db.QueryRowContext(ctx, query, book.Title, book.Author, book.ISBN).
Scan(&b.ID, &b.CreatedAt, &b.UpdatedAt)
if err != nil {
return nil, fmt.Errorf("repository.Create: %w", err)
}
return &b, nil
}
func (r *BookRepository) List(ctx context.Context, author string, limit, offset int) ([]model.Book, error) {
query := `SELECT id, title, author, isbn, created_at, updated_at
FROM books
WHERE ($1 = '' OR author ILIKE '%' || $1 || '%')
ORDER BY id
LIMIT $2 OFFSET $3`
rows, err := r.db.QueryContext(ctx, query, author, limit, offset)
if err != nil {
return nil, fmt.Errorf("repository.List: %w", err)
}
defer rows.Close()
var books []model.Book
for rows.Next() {
var b model.Book
if err := rows.Scan(&b.ID, &b.Title, &b.Author, &b.ISBN, &b.CreatedAt, &b.UpdatedAt); err != nil {
return nil, fmt.Errorf("repository.List scan: %w", err)
}
books = append(books, b)
}
return books, nil
}
当你在对话中持续提出需求,Claude Code 会自动维护上下文:
sequenceDiagram
participant U as 开发者
participant CC as Claude Code
participant FS as 文件系统
U->>CC: 创建 Book 模型
CC->>FS: 写入 model/book.go
CC->>FS: 写入 migrations/001_create_books.sql
U->>CC: 实现 Repository 层
Note over CC: 自动读取 model/book.go<br/>理解 Book 结构体定义
CC->>FS: 写入 repository/book_repo.go
U->>CC: 写测试
Note over CC: 读取 repo 代码 + model 定义<br/>+ CLAUDE.md 中的测试规范
CC->>FS: 写入 repository/book_repo_test.go
CC->>U: 运行 go test -race ./...
关键洞察:Claude Code 在每轮对话前会自动读取相关文件,你不需要重复粘贴代码。这就是 CLAUDE.md 的价值——它确保 Claude 始终遵循你的项目规范。
实现 internal/handler/book_handler.go:
- 注入 service 层(如果逻辑简单可省略 service,直接用 repo)
- POST /books → 创建
- GET /books → 列表(?author=&limit=20&offset=0)
- GET /books/{id} → 详情
- PUT /books/{id} → 更新
- DELETE /books/{id} → 删除
- 所有响应返回 JSON,错误格式 {"error":"message"}
- 请求体用 json.NewDecoder 解析
Claude 生成的 Handler 关键代码:
package handler
import (
"encoding/json"
"net/http"
"strconv"
"github.com/go-chi/chi/v5"
"github.com/example/bookshelf-api/internal/model"
"github.com/example/bookshelf-api/internal/repository"
)
type BookHandler struct {
repo *repository.BookRepository
}
func NewBookHandler(repo *repository.BookRepository) *BookHandler {
return &BookHandler{repo: repo}
}
func (h *BookHandler) Routes() chi.Router {
r := chi.NewRouter()
r.Post("/", h.Create)
r.Get("/", h.List)
r.Get("/{id}", h.GetByID)
r.Put("/{id}", h.Update)
r.Delete("/{id}", h.Delete)
return r
}
func (h *BookHandler) Create(w http.ResponseWriter, r *http.Request) {
var req model.CreateBookRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
writeError(w, http.StatusBadRequest, "invalid request body")
return
}
book, err := h.repo.Create(r.Context(), &req)
if err != nil {
writeError(w, http.StatusInternalServerError, "failed to create book")
return
}
writeJSON(w, http.StatusCreated, book)
}
func (h *BookHandler) List(w http.ResponseWriter, r *http.Request) {
author := r.URL.Query().Get("author")
limit, _ := strconv.Atoi(r.URL.Query().Get("limit"))
offset, _ := strconv.Atoi(r.URL.Query().Get("offset"))
if limit <= 0 || limit > 100 {
limit = 20
}
books, err := h.repo.List(r.Context(), author, limit, offset)
if err != nil {
writeError(w, http.StatusInternalServerError, "failed to list books")
return
}
writeJSON(w, http.StatusOK, books)
}
func writeJSON(w http.ResponseWriter, status int, v interface{}) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
json.NewEncoder(w).Encode(v)
}
func writeError(w http.ResponseWriter, status int, msg string) {
writeJSON(w, status, map[string]string{"error": msg})
}
更新 cmd/server/main.go,把 BookHandler 的路由挂载到 /api/v1/books 路径下
更新后的 main.go:
package main
import (
"database/sql"
"fmt"
"log"
"net/http"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
_ "github.com/jackc/pgx/v5/stdlib"
"github.com/example/bookshelf-api/internal/handler"
"github.com/example/bookshelf-api/internal/repository"
)
func main() {
connStr := "postgres://user:pass@localhost:5432/bookshelf?sslmode=disable"
db, err := sql.Open("pgx", connStr)
if err != nil {
log.Fatalf("failed to connect to database: %v", err)
}
defer db.Close()
if err := db.Ping(); err != nil {
log.Fatalf("failed to ping database: %v", err)
}
bookRepo := repository.NewBookRepository(db)
bookHandler := handler.NewBookHandler(bookRepo)
r := chi.NewRouter()
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)
r.Use(middleware.AllowContentType("application/json"))
r.Get("/health", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
fmt.Fprintf(w, `{"status":"ok"}`)
})
r.Mount("/api/v1/books", bookHandler.Routes())
fmt.Println("Server starting on :8080")
log.Fatal(http.ListenAndServe(":8080", r))
}
为 internal/handler/book_handler.go 编写表驱动测试:
- 使用 httptest 包模拟请求
- Repository 用接口 mock,不依赖真实数据库
- 测试用例覆盖:正常创建、空标题、重复 ISBN、列表分页、获取不存在的 ID
- 按 CLAUDE.md 规范,用 table-driven 风格
Claude 生成的测试代码:
package handler
import (
"bytes"
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/example/bookshelf-api/internal/model"
)
// MockRepository 满足 handler 需要的 repo 接口
type MockRepository struct {
books map[int64]*model.Book
nextID int64
}
func NewMockRepository() *MockRepository {
return &MockRepository{
books: make(map[int64]*model.Book),
nextID: 1,
}
}
func (m *MockRepository) Create(ctx context.Context, req *model.CreateBookRequest) (*model.Book, error) {
book := &model.Book{
ID: m.nextID,
Title: req.Title,
Author: req.Author,
ISBN: req.ISBN,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
m.books[m.nextID] = book
m.nextID++
return book, nil
}
func (m *MockRepository) GetByID(ctx context.Context, id int64) (*model.Book, error) {
book, ok := m.books[id]
if !ok {
return nil, fmt.Errorf("not found")
}
return book, nil
}
func TestCreateBook(t *testing.T) {
tests := []struct {
name string
body interface{}
wantStatus int
}{
{
name: "正常创建",
body: model.CreateBookRequest{
Title: "Go 程序设计",
Author: "张三",
ISBN: "9787111111111",
},
wantStatus: http.StatusCreated,
},
{
name: "空标题应拒绝",
body: model.CreateBookRequest{
Title: "",
Author: "张三",
ISBN: "9787111111111",
},
wantStatus: http.StatusBadRequest,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
body, _ := json.Marshal(tt.body)
req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
repo := NewMockRepository()
h := NewBookHandler(repo)
h.Create(w, req)
if w.Code != tt.wantStatus {
t.Errorf("status = %d, want %d", w.Code, tt.wantStatus)
}
})
}
}
运行所有测试,包括竞态检测
Claude 执行:
go test -race -v ./...
预期输出:
=== RUN TestCreateBook
=== RUN TestCreateBook/正常创建
=== RUN TestCreateBook/空标题应拒绝
--- PASS: TestCreateBook (0.00s)
--- PASS: TestCreateBook/正常创建 (0.00s)
--- PASS: TestCreateBook/空标题应拒绝 (0.00s)
PASS
ok github.com/example/bookshelf-api/internal/handler 0.003s
开发过程中,掌握以下核心斜杠命令能大幅提升效率:
| 命令 | 功能 | 典型场景 |
|---|---|---|
/init |
生成 CLAUDE.md | 新项目首次配置 |
/compact |
压缩上下文窗口 | 对话过长时释放 token |
/plan |
进入规划模式 | 大型重构前先制定方案 |
/review |
代码审查 | 提交前检查代码质量 |
/mcp |
管理 MCP 服务器 | 添加数据库工具等 |
/cost |
查看当前会话费用 | 成本控制 |
/clear |
清空对话历史 | 切换任务时重置上下文 |
/help |
查看所有命令 | 忘记用法时 |
flowchart LR
A["/plan<br/>规划任务"] --> B["自然语言<br/>描述需求"]
B --> C["Claude 生成<br/>+ 自动写文件"]
C --> D["go test<br/>自动验证"]
D --> E{测试通过?}
E -->|是| F["/review<br/>代码审查"]
E -->|否| B
F --> G["git commit<br/>提交"]
G --> H{还有需求?}
H -->|是| A
H -->|否| I["/compact<br/>总结"]
| 快捷键 | 功能 |
|---|---|
Escape |
中断 Claude 当前操作 |
Shift+Tab |
切换 Plan/Act 模式 |
Ctrl+C |
退出对话 |
↑ / ↓ |
浏览历史输入 |
Tab |
自动补全文件名 |
将前面所有步骤整合,执行一次完整的端到端测试:
# 启动 PostgreSQL 容器
docker run -d --name bookshelf-db \
-e POSTGRES_USER=user \
-e POSTGRES_PASSWORD=pass \
-e POSTGRES_DB=bookshelf \
-p 5432:5432 \
postgres:16-alpine
# 执行迁移
docker exec -i bookshelf-db psql -U user -d bookshelf < migrations/001_create_books.sql
# 启动 API 服务
go run cmd/server/main.go
在 Claude Code 对话中:
帮我对 http://localhost:8080/api/v1/books 执行完整的 CRUD 测试:
1. POST 创建一本 Go 书
2. GET 列表确认存在
3. GET /{id} 获取详情
4. PUT 更新标题
5. DELETE 删除
6. GET 确认已删除,应返回 404
Claude 会自动生成并运行 curl 命令:
# 创建
curl -s -X POST http://localhost:8080/api/v1/books \
-H "Content-Type: application/json" \
-d '{"title":"Go程序设计","author":"张三","isbn":"9787111111111"}'
# {"id":1,"title":"Go程序设计","author":"张三","isbn":"9787111111111",...}
# 列表
curl -s http://localhost:8080/api/v1/books
# [{"id":1,"title":"Go程序设计",...}]
# 详情
curl -s http://localhost:8080/api/v1/books/1
# {"id":1,"title":"Go程序设计",...}
# 更新
curl -s -X PUT http://localhost:8080/api/v1/books/1 \
-H "Content-Type: application/json" \
-d '{"title":"Go程序设计(第二版)"}'
# {"id":1,"title":"Go程序设计(第二版)",...}
# 删除
curl -s -X DELETE http://localhost:8080/api/v1/books/1
# {"message":"deleted"}
# 确认删除
curl -s -w "\nHTTP %{http_code}" http://localhost:8080/api/v1/books/1
# {"error":"book not found"}
# HTTP 404
bookshelf-api/
├── CLAUDE.md # 项目规范
├── cmd/
│ └── server/
│ └── main.go # 入口 + 路由挂载
├── internal/
│ ├── handler/
│ │ ├── book_handler.go # HTTP 处理器
│ │ └── book_handler_test.go # 表驱动测试
│ ├── model/
│ │ └── book.go # 数据模型
│ └── repository/
│ ├── book_repo.go # 数据库操作
│ └── book_repo_test.go # Repo 测试
├── migrations/
│ └── 001_create_books.sql # DDL
├── go.mod
└── go.sum
graph TB
subgraph "开发者终端"
CC["Claude Code CLI<br/>对话式 AI 编码"]
end
subgraph "Claude Code 内部"
CTX["上下文管理器<br/>自动读取项目文件"]
CL["CLAUDE.md<br/>项目规范注入"]
TOOLS["工具集<br/>Read/Write/Bash/Grep"]
end
subgraph "项目文件系统"
SRC["Go 源码<br/>cmd/ internal/"]
TEST["测试文件<br/>*_test.go"]
MIG["数据库迁移<br/>migrations/"]
CONF["配置文件<br/>CLAUDE.md go.mod"]
end
subgraph "运行环境"
GO["Go Runtime<br/>go run / go test"]
PG["PostgreSQL<br/>Docker 容器"]
CURL["curl<br/>API 验证"]
end
CC --> CTX
CTX --> CL
CTX --> TOOLS
TOOLS --> SRC
TOOLS --> TEST
TOOLS --> MIG
TOOLS --> CONF
TOOLS --> GO
TOOLS --> CURL
GO --> PG
style CC fill:#6366f1,color:#fff
style CTX fill:#8b5cf6,color:#fff
style TOOLS fill:#a78bfa,color:#fff
| 方向 | 下一步 | 推荐 Skill |
|---|---|---|
| 数据库集成 | 添加事务、连接池配置 | pgx 官方文档 |
| 认证鉴权 | JWT / API Key 中间件 | security-best-practices |
| API 文档 | OpenAPI 3.0 / Swagger | — |
| 容器化 | Dockerfile + docker-compose | — |
| CI/CD | GitHub Actions + go test | gh-cli |
| 性能优化 | pprof + 基准测试 | — |
| 微服务拆分 | gRPC 服务间通信 | — |
| 维度 | 说明 |
|---|---|
| 适用训练营 | AI 辅助开发入门营、Go 后端实战营、全栈工程师训练营 |
| 教学时长 | 4-6 小时(含动手实践) |
| 学员基础 | 了解任一编程语言即可,无需 Go 或 Claude Code 经验 |
| 硬件要求 | 8GB+ RAM 笔记本,能运行 Docker |
| 产出物 | 一个可运行的 REST API 项目 + CLAUDE.md 配置体系 |
品牌信息:本教程由 Claude Code 驱动生成,基于 Anthropic Claude 模型。技术栈:Claude Code CLI + Go 1.22 + Chi v5 + PostgreSQL 16。
[!tip] 51学通信知识星球 星主:爱卫生(微信:gprshome201101)——公众号「51学通信」创办者,19 年通信行业运维与讲师经验,著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书(各大电商平台有售)。 星球定位:深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域,主打运维岗位与信令分析。 当前规模:5200 篇主题 / 990 个回答,198 元/年。 加入福利:加入星球后请加爱卫生微信,拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题,另有群内分享。 AI 加持:51学通信知识星球已作为 skill 接入 WorkBuddy,使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例,做总结、找文档和 PPT 等场景全覆盖。
