今天我们用 OpenCode 写一个基于 HTTP 的 Key-Value 存储服务。它提供 RESTful API,支持 GET/PUT/DELETE 操作,使用内存存储,线程安全,约 100 行代码。
技术栈:Go 1.21+,仅用标准库(net/http + sync),无需任何第三方依赖。最终效果:一个可通过 curl 交互的轻量 KV 服务器。
npm install -g @anthropic/opencode(或参考 opencode.ai 文档)我对 OpenCode 说:
> 帮我创建一个 Go 项目,初始化 go mod,创建 main.go,main 函数里启动一个 HTTP 服务器监听 :8080,并打印启动日志。
OpenCode 自动执行 go mod init kvstore,生成 main.go:
package main
import (
"log"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello"))
})
log.Println("KV Store running on :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
运行 go run main.go,服务直接跑起来了。关键要点:OpenCode 不仅是代码补全——它会帮你完成从模块初始化到代码生成的全流程。
我对 OpenCode 说:
> 添加一个 Store 结构体,内部用 map[string]string 存数据,带 sync.RWMutex 保证并发安全。实现 Get、Set、Delete、List 四个方法。
OpenCode 在 main() 上方插入代码:
type Store struct {
mu sync.RWMutex
data map[string]string
}
func NewStore() *Store {
return &Store{data: make(map[string]string)}
}
func (s *Store) Get(key string) (string, bool) {
s.mu.RLock()
defer s.mu.RUnlock()
v, ok := s.data[key]
return v, ok
}
func (s *Store) Set(key, value string) {
s.mu.Lock()
defer s.mu.Unlock()
s.data[key] = value
}
func (s *Store) Delete(key string) bool {
s.mu.Lock()
defer s.mu.Unlock()
_, ok := s.data[key]
if ok {
delete(s.data, key)
}
return ok
}
func (s *Store) List() map[string]string {
s.mu.RLock()
defer s.mu.RUnlock()
result := make(map[string]string)
for k, v := range s.data {
result[k] = v
}
return result
}
关键要点:注意 OpenCode 自动处理了并发安全问题——Get/List 用读锁,Set/Delete 用写锁,Go 程序员常犯的 map 并发写 panic 被直接规避了。
我对 OpenCode 说:
> 把 /api/kv/ 路径注册为 KV 操作的 handler:GET /api/kv/{key} 返回 JSON;PUT /api/kv/{key} 从请求体 {"value":"..."} 读取并写入;DELETE /api/kv/{key} 删除。再加一个 GET /api/kv 列出所有键值对。
OpenCode 替换了原来的 handler,生成路由分发逻辑:
http.HandleFunc("/api/kv/", func(w http.ResponseWriter, r *http.Request) {
key := strings.TrimPrefix(r.URL.Path, "/api/kv/")
if key == "" {
// 列出全部 - 但 /api/kv/ 末尾有斜线
// 实际全部列表用 /api/kv
http.Error(w, "key required", http.StatusBadRequest)
return
}
switch r.Method {
case http.MethodGet:
v, ok := store.Get(key)
if !ok {
http.Error(w, "not found", http.StatusNotFound)
return
}
json.NewEncoder(w).Encode(map[string]string{"key": key, "value": v})
case http.MethodPut:
var body map[string]string
json.NewDecoder(r.Body).Decode(&body)
store.Set(key, body["value"])
fmt.Fprintf(w, `{"status":"ok"}`)
case http.MethodDelete:
store.Delete(key)
fmt.Fprintf(w, `{"status":"ok"}`)
}
})
关键要点:我只描述了 API 行为,OpenCode 自行处理了路由解析、错误返回、JSON 编解码。强调"干什么"而非"怎么干",是高效使用 OpenCode 的核心技巧。
我对 OpenCode 说:
> 给所有端点加上 Content-Type: application/json 响应头,PUT 时 body.value 为空返回 400,DELETE 不存在的 key 返回 404,不支持的方法返回 405。最后帮我写出 curl 测试命令。
OpenCode 完善了所有错误分支,并输出测试用例:
curl -X PUT http://localhost:8080/api/kv/name -d '{"value":"Alice"}'
# {"status":"ok"}
curl http://localhost:8080/api/kv/name
# {"key":"name","value":"Alice"}
curl -X DELETE http://localhost:8080/api/kv/name
# {"status":"ok"}
curl http://localhost:8080/api/kv
# {}
关键要点:这一轮体现了 OpenCode 的"迭代精化"能力——第一版能用就行,第二轮补齐边界条件。实际开发中这非常省时间,你只需指出缺陷,AI 负责修复。
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"strings"
"sync"
)
type Store struct {
mu sync.RWMutex
data map[string]string
}
func NewStore() *Store {
return &Store{data: make(map[string]string)}
}
func (s *Store) Get(key string) (string, bool) {
s.mu.RLock()
defer s.mu.RUnlock()
v, ok := s.data[key]
return v, ok
}
func (s *Store) Set(key, value string) {
s.mu.Lock()
defer s.mu.Unlock()
s.data[key] = value
}
func (s *Store) Delete(key string) bool {
s.mu.Lock()
defer s.mu.Unlock()
_, ok := s.data[key]
if ok {
delete(s.data, key)
}
return ok
}
func (s *Store) List() map[string]string {
s.mu.RLock()
defer s.mu.RUnlock()
m := make(map[string]string)
for k, v := range s.data {
m[k] = v
}
return m
}
func main() {
store := NewStore()
http.HandleFunc("/api/kv/", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
key := strings.TrimPrefix(r.URL.Path, "/api/kv/")
if key == "" {
http.Error(w, `{"error":"key required"}`, http.StatusBadRequest)
return
}
switch r.Method {
case http.MethodGet:
v, ok := store.Get(key)
if !ok {
http.Error(w, `{"error":"not found"}`, http.StatusNotFound)
return
}
json.NewEncoder(w).Encode(map[string]string{"key": key, "value": v})
case http.MethodPut:
var body map[string]string
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
http.Error(w, `{"error":"invalid JSON"}`, http.StatusBadRequest)
return
}
val, ok := body["value"]
if !ok || val == "" {
http.Error(w, `{"error":"value field required"}`, http.StatusBadRequest)
return
}
store.Set(key, val)
fmt.Fprint(w, `{"status":"ok"}`)
case http.MethodDelete:
if !store.Delete(key) {
http.Error(w, `{"error":"not found"}`, http.StatusNotFound)
return
}
fmt.Fprint(w, `{"status":"ok"}`)
default:
http.Error(w, `{"error":"method not allowed"}`, http.StatusMethodNotAllowed)
}
})
http.HandleFunc("/api/kv", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
if r.Method != http.MethodGet {
http.Error(w, `{"error":"method not allowed"}`, http.StatusMethodNotAllowed)
return
}
json.NewEncoder(w).Encode(store.List())
})
addr := ":8080"
log.Printf("KV Store running on %s", addr)
log.Fatal(http.ListenAndServe(addr, nil))
}
整个开发过程约 4 分钟,四轮对话,每轮一句话描述需求即可。
传统写一个带并发安全的 REST 服务,你需要手动查 API 文档、处理锁的读写分离、写 JSON 编解码模板、补齐一堆错误分支——不说半小时,十分钟总要的。用 OpenCode 基本就是"描述业务 → 验证运行 → 补充边界 → 收工"。
最让我印象深刻的是 OpenCode 对项目上下文的感知能力:第三步加路由时它知道 store 变量已经存在,第四步补边界时知道所有端点的上下文。不是那种"一次性 prompt 生成",而是真正在陪你迭代开发。
如果这篇文章对你有帮助,欢迎在评论区交流你用 OpenCode 的体验。