go-project-overview
Overview of Go project structure, technology stack (Echo, Connect RPC, Driver pattern), and essential commands for building, testing, and development
SKILL.md
Full skill instructions
Go Project Overview
Technology Stack
HTTP Framework
- Echo v4 (
github.com/labstack/echo/v4) - Middleware: Recover, CORS, Logger, RequestID
- Pattern:
func(c echo.Context) error
RPC Framework
- Connect RPC + gRPC-Gateway
- Tool:
buffor proto management - Dual Protocol: Single impl serves both
Database
- Driver Interface Pattern (
store/driver.go) - Drivers: SQLite (
modernc.org/sqlite), MySQL (go-sql-driver/mysql), PostgreSQL (lib/pq) - Caching: TTL 10min, max 1000 items
Authentication
- JWT access token (stateless, 15min)
- Refresh token (long-lived, db-validated)
- PAT (personal access token, SHA-256 hash)
- Header:
Authorization: Bearer <token>
Essential Commands
Backend
# Run dev server on port 8081
go run ./cmd/server --port 8081
# All tests
go test ./...
# Single test
go test -run TestName ./path/...
# Test with coverage
go test -cover ./...
# Lint
golangci-lint run
# Format imports
goimports -w .
Protocol Buffers
# Install dependencies
cd proto && buf dep update
# Generate code
cd proto && buf generate
# Lint proto files
cd proto && buf lint
# Breaking changes
cd proto && buf breaking --against .git#main
Multi-Database Testing
# SQLite (default)
DRIVER=sqlite go test ./...
# MySQL
DRIVER=mysql DSN="user:pass@tcp(localhost:3306)/db" go test ./...
# PostgreSQL
DRIVER=postgres DSN="postgres://user:pass@localhost:5432/db?sslmode=disable" go test ./...
Project Structure
cmd/ # Entry point (main.go with Cobra CLI)
server/ # HTTP/gRPC server
auth/ # Authentication logic
router/ # API routing
api/v1/ # Connect + gRPC-Gateway handlers
fileserver/ # Static file serving
frontend/ # SPA serving
rss/ # RSS feeds
runner/ # Background tasks
store/ # Data layer
db/{driver}/ # Database implementations
cache/ # In-memory cache
migration/ # Schema migrations
seed/ # Demo data
test/ # Test utilities
proto/ # Protocol buffers
api/v1/ # Service definitions
google/ # Google API protos
plugin/ # Extensible features
internal/ # Internal utilities
Key Files
| File | Purpose |
|---|---|
cmd/server/main.go | CLI entry point |
internal/profile/profile.go | Configuration struct |
server/server.go | Server initialization, cmux |
server/router/api/v1/v1.go | Service registration |
store/driver.go | Database interface |
store/store.go | Store wrapper with caching |
proto/buf.yaml | Proto lint config |
proto/buf.gen.yaml | Code generation config |
Skill Map
| Task | Skill |
|---|---|
| CLI, Config, Shutdown | go-project-main |
| Echo, Connect, cmux | go-project-server |
| Proto definitions | go-project-proto |
| Database, Migrations | go-project-store |
| Code style | go-project-conventions |
Workflows
⚠️ CRITICAL: Development Order
You MUST create files in this exact order:
1. proto/api/v1/*_service.proto ← DEFINE API FIRST
2. proto/buf.yaml
3. proto/buf.gen.yaml
4. cd proto && buf generate ← GENERATE CODE
5. server/router/api/v1/*_service.go ← IMPLEMENT SERVICE
6. store/driver.go
7. store/{model}.go
8. server/router/api/v1/v1.go ← REGISTER SERVICE
9. server/server.go
10. cmd/server/main.go
If you skip step 1-4, the server code will not compile!
Add API Endpoint (Detailed Steps)
Step 1: Create proto definition (see go-project-proto)
mkdir -p proto/api/v1
Create proto/api/v1/auth_service.proto:
syntax = "proto3";
package myapp.api.v1;
import "google/api/annotations.proto";
import "google/api/client.proto";
import "google/api/field_behavior.proto";
import "google/protobuf/empty.proto";
option go_package = "gen/api/v1";
service AuthService {
rpc SignIn(SignInRequest) returns (SignInResponse) {
option (google.api.http) = {
post: "/api/v1/auth/signin"
body: "*"
};
}
rpc SignUp(SignUpRequest) returns (SignUpResponse) {
option (google.api.http) = {
post: "/api/v1/auth/signup"
body: "*"
};
}
}
message SignInRequest {
string username = 1 [(google.api.field_behavior) = REQUIRED];
string password = 2 [(google.api.field_behavior) = REQUIRED];
}
message SignInResponse {
string access_token = 1;
string refresh_token = 2;
}
message SignUpRequest {
string username = 1 [(google.api.field_behavior) = REQUIRED];
string email = 2 [(google.api.field_behavior) = REQUIRED];
string password = 3 [(google.api.field_behavior) = REQUIRED];
}
message SignUpResponse {
User user = 1;
}
message User {
string name = 1 [(google.api.field_behavior) = IDENTIFIER];
string username = 2 [(google.api.field_behavior) = REQUIRED];
string email = 3 [(google.api.field_behavior) = REQUIRED];
}
Step 2: Create buf config
# proto/buf.yaml
version: v2
deps:
- buf.build/googleapis/googleapis
lint:
use: [BASIC]
breaking:
use: [FILE]
# proto/buf.gen.yaml
version: v2
plugins:
- remote: buf.build/protocolbuffers/go
out: gen
opt: paths=source_relative
- remote: buf.build/grpc/go
out: gen
opt: paths=source_relative
- remote: buf.build/connectrpc/go
out: gen
opt: paths=source_relative
- remote: buf.build/grpc-ecosystem/gateway
out: gen
opt: paths=source_relative
Step 3: Generate code
cd proto && buf generate
Step 4: Implement service (see go-project-server)
Create server/router/api/v1/auth_service.go:
package v1
type AuthService struct {
Store *store.Store
Profile *profile.Profile
}
func (s *AuthService) SignIn(ctx context.Context, req *connect.Request[v1pb.SignInRequest]) (*connect.Response[v1pb.SignInResponse], error) {
// Implementation here
}
Step 5: Register service (see go-project-server)
Add to server/router/api/v1/v1.go:
func (s *APIV1Service) RegisterGateway(ctx context.Context, echoServer *echo.Echo) error {
// ... existing code
v1pb.RegisterAuthServiceHandlerServer(ctx, gwMux, s)
return nil
}
Add Database Model (Detailed Steps)
Step 1: Create migration (see go-project-store)
mkdir -p store/migration/sqlite/0.1
Create store/migration/sqlite/0.1/00__create_user.sql:
CREATE TABLE user (
id INTEGER PRIMARY KEY AUTOINCREMENT,
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
username TEXT NOT NULL UNIQUE,
email TEXT NOT NULL UNIQUE,
password_hash TEXT NOT NULL
);
Step 2: Update LATEST.sql
Step 3: Update store/driver.go interface
Step 4: Implement CRUD in store/db/sqlite/.go*
AI Configuration Files
Create these files for AI assistant context:
AGENTS.md (通用)
# Project Guide for AI Agents
## Technology Stack
- **Echo v4** - HTTP framework
- **Connect RPC + gRPC-Gateway** - Dual protocol API
- **buf** - Proto management
- **Driver Pattern** - SQLite/MySQL/PostgreSQL
## Essential Commands
\`\`\`bash
# Dev server
go run ./cmd/server --port 8081
# Tests
go test ./...
go test -run TestName ./path/...
# Lint
golangci-lint run
# Proto
cd proto && buf generate && buf lint
\`\`\`
## Code Conventions
### Error Handling
- Use `errors.Wrap(err, "context")` from `github.com/pkg/errors`
- gRPC errors: `status.Errorf(codes.NotFound, "message")`
- Never use `fmt.Errorf`
### Imports
- Group: stdlib, third-party, local
- Sort alphabetically within groups
- Use `goimports -w .`
.cursor/rules/go-project.rule (Cursor)
# Go Project Rules
## Architecture
- Echo v4 for HTTP
- Connect RPC + gRPC-Gateway
- Driver interface pattern for databases
## Key Patterns
- Store wraps Driver, adds caching
- Service implementations serve both Connect and gRPC-Gateway
- Background runners with context cancellation
## Commands
- Dev: `go run ./cmd/server --port 8081`
- Test: `go test ./...`
- Lint: `golangci-lint run`
.github/copilot-instructions.md (Copilot)
# Go Project Guidelines
This project uses:
- Echo v4 web framework
- Connect RPC + gRPC-Gateway for APIs
- buf for protocol buffers
- Driver pattern for multi-database support (SQLite/MySQL/PostgreSQL)
When generating code:
- Use `errors.Wrap(err, "context")` for error handling
- Group imports: stdlib, third-party, local
- Implement services once, serve both Connect and gRPC-Gateway
File Templates
.gitignore
# Generated by go mod init
go.mod
go.sum
# Binary
cmd/server/server
cmd/server/server.exe
# IDE
.idea/
.vscode/
*.swp
*.swo
# Data
*.db
*.sqlite
data/
# Proto generated
proto/gen/
web/src/types/proto/
.golangci.yaml
run:
timeout: 5m
issues-exit-code: 1
linters:
enable:
- revive
- govet
- staticcheck
- misspell
- gocritic
- sqlclosecheck
- rowserrcheck
- godot
issues:
exclude-rules:
- path: _test\.go
linters:
- dupl
