golang-web
Modern Go Web application architecture guide. Use when creating new Go web projects, APIs, or microservices. Covers project structure, tech stack selection, and best practices based on Go standards.
What this skill does
# Go Web Architecture
## Core Principles
- **Standard layout** — Follow cmd/internal/pkg convention
- **Explicit dependencies** — Wire dependencies in main.go, no globals
- **Interface-driven** — Define interfaces where you use them, not where you implement
- **Error wrapping** — Wrap errors with context, use error codes
- **No backwards compatibility** — Delete, don't deprecate. Change directly
- **LiteLLM for LLM APIs** — Use LiteLLM proxy for all LLM integrations
---
## No Backwards Compatibility
> **Delete unused code. Change directly. No compatibility layers.**
```go
// ❌ BAD: Deprecated function kept around
// Deprecated: Use NewUserService instead
func CreateUserService() *UserService { ... }
// ❌ BAD: Alias for renamed types
type OldName = NewName // "for backwards compatibility"
// ❌ BAD: Unused parameters
func Process(_ context.Context, data Data) { ... }
// ✅ GOOD: Just delete and update all usages
func NewUserService(repo UserRepository) *UserService { ... }
```
---
## LiteLLM for LLM APIs
> **Use LiteLLM proxy. Don't call provider APIs directly.**
```go
// adapters/llm/client.go
package llm
import (
"github.com/sashabaranov/go-openai"
)
// Connect to LiteLLM proxy using OpenAI-compatible SDK
func NewClient(cfg Config) *openai.Client {
config := openai.DefaultConfig(cfg.APIKey)
config.BaseURL = cfg.BaseURL // LiteLLM proxy URL
return openai.NewClientWithConfig(config)
}
```
---
## Quick Start
### 1. Initialize Project
```bash
mkdir myapp && cd myapp
go mod init github.com/yourname/myapp
# Install core dependencies
go get github.com/gin-gonic/gin
go get github.com/spf13/viper
go get github.com/sirupsen/logrus
go get gorm.io/gorm
```
### 2. Apply Tech Stack
| Layer | Recommendation |
|-------|----------------|
| HTTP Framework | Gin / Chi / Echo |
| Configuration | Viper |
| Logging | Logrus / Zap / Slog |
| Database ORM | GORM / sqlx / sqlc |
| Validation | go-playground/validator |
| Testing | testify / go test |
### Version Strategy
> **Always get latest. Never pin in templates.**
```bash
# Always fetch latest
go get -u github.com/gin-gonic/gin
go get -u ./...
# go.mod handles version locking
# go.sum ensures reproducible builds
```
### 3. Use Standard Structure
```
myapp/
├── cmd/
│ └── myapp/
│ └── main.go # Entry point, dependency wiring
├── configs/
│ └── config.go # Configuration struct + loader
├── internal/ # Private application code
│ ├── handlers/ # HTTP handlers
│ ├── services/ # Business logic
│ ├── repositories/ # Data access
│ ├── models/ # Domain models
│ ├── middleware/ # HTTP middleware
│ └── router/ # Route definitions
├── pkg/ # Public reusable packages
│ ├── errors/ # Error types
│ ├── logger/ # Logging setup
│ ├── response/ # Unified response format
│ └── database/ # Database connection
├── config.yaml # Configuration file
├── Makefile # Build automation
├── Dockerfile
└── go.mod
```
---
## Architecture Layers
### cmd/ — Entry Point
Wire all dependencies here. No business logic.
```go
// cmd/myapp/main.go
func main() {
// Load config
cfg := configs.Load()
// Initialize infrastructure
db := database.New(cfg.Database)
cache := cache.New(cfg.Redis)
logger := logger.New(cfg.Log)
// Initialize repositories
userRepo := repositories.NewUserRepository(db)
// Initialize services
userService := services.NewUserService(userRepo)
// Initialize handlers
userHandler := handlers.NewUserHandler(userService)
// Setup router
r := router.Setup(cfg, userHandler)
// Start server with graceful shutdown
server.Run(r, cfg.Server)
}
```
### internal/ — Private Business Code
#### handlers/ — HTTP Layer
```go
// internal/handlers/user.go
type UserHandler struct {
service services.UserService
}
func NewUserHandler(s services.UserService) *UserHandler {
return &UserHandler{service: s}
}
func (h *UserHandler) Create(c *gin.Context) {
var input CreateUserInput
if err := c.ShouldBindJSON(&input); err != nil {
response.Error(c, errors.ErrInvalidParams)
return
}
user, err := h.service.Create(c.Request.Context(), input)
if err != nil {
response.Error(c, err)
return
}
response.Success(c, user)
}
```
#### services/ — Business Logic
```go
// internal/services/user.go
type UserService interface {
Create(ctx context.Context, input CreateUserInput) (*models.User, error)
GetByID(ctx context.Context, id string) (*models.User, error)
}
type userService struct {
repo repositories.UserRepository
}
func NewUserService(repo repositories.UserRepository) UserService {
return &userService{repo: repo}
}
func (s *userService) Create(ctx context.Context, input CreateUserInput) (*models.User, error) {
existing, _ := s.repo.FindByEmail(ctx, input.Email)
if existing != nil {
return nil, errors.ErrUserExists
}
user := &models.User{
ID: uuid.New().String(),
Email: input.Email,
Name: input.Name,
}
return s.repo.Save(ctx, user)
}
```
#### repositories/ — Data Access
```go
// internal/repositories/user.go
type UserRepository interface {
FindByID(ctx context.Context, id string) (*models.User, error)
FindByEmail(ctx context.Context, email string) (*models.User, error)
Save(ctx context.Context, user *models.User) (*models.User, error)
Delete(ctx context.Context, id string) error
}
type userRepository struct {
db *gorm.DB
}
func NewUserRepository(db *gorm.DB) UserRepository {
return &userRepository{db: db}
}
func (r *userRepository) FindByID(ctx context.Context, id string) (*models.User, error) {
var user models.User
if err := r.db.WithContext(ctx).First(&user, "id = ?", id).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, err
}
return &user, nil
}
```
### pkg/ — Reusable Packages
#### errors/ — Error Handling
```go
// pkg/errors/errors.go
type AppError struct {
Code int `json:"code"`
Message string `json:"message"`
Cause error `json:"-"`
}
func (e *AppError) Error() string { return e.Message }
func (e *AppError) Unwrap() error { return e.Cause }
func New(code int, message string) *AppError {
return &AppError{Code: code, Message: message}
}
func Wrap(err error, code int, message string) *AppError {
return &AppError{Code: code, Message: message, Cause: err}
}
// Predefined errors
var (
ErrInternal = New(500, "internal server error")
ErrInvalidParams = New(400, "invalid parameters")
ErrNotFound = New(404, "resource not found")
ErrUnauthorized = New(401, "unauthorized")
ErrUserExists = New(409, "user already exists")
)
```
#### response/ — Unified Response
```go
// pkg/response/response.go
type Response struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data,omitempty"`
}
func Success(c *gin.Context, data interface{}) {
c.JSON(http.StatusOK, Response{
Code: 0,
Message: "success",
Data: data,
})
}
func Error(c *gin.Context, err error) {
var appErr *errors.AppError
if errors.As(err, &appErr) {
c.JSON(appErr.Code/100, Response{
Code: appErr.Code,
Message: appErr.Message,
})
return
}
c.JSON(http.StatusInternalServerError, Response{
Code: 500,
Message: "internal server error",
})
}
```
---
## Configuration
### Viper + YAML + Environment Variables
```go
// configs/config.go
type Config struct {
Server ServerConfig `mapstructure:"server"`
Database DatabaseConfigRelated in General
modeling-omnistudio-epc-catalog
IncludedSalesforce Industries CME EPC product-modeling skill for Product2-based catalog creation. Use when creating EPC products, configuring product attributes, building offer bundles with Product Child Items, or reviewing EPC DataPack JSON metadata for product catalog changes. TRIGGER when: user creates or updates Product2 EPC records, AttributeAssignment payloads, AttributeMetadata/AttributeDefaultValues, Offer bundles, or ProductChildItem relationships. DO NOT TRIGGER when: designing OmniScripts/FlexCards/Integration Procedures (use building-omnistudio-omniscript, building-omnistudio-flexcard, or building-omnistudio-integration-procedure), implementing Apex business logic (use generating-apex), or troubleshooting deployment pipelines (use deploying-metadata).
relationship-science-coach
IncludedUse this skill for direct, practical adult relationship coaching: couples conflict, repair, trust, marriage, dating, flirting, attachment patterns, emotional connection, sex, desire differences, eroticism, kink negotiation, affection, love languages, breakups, and long-term passion. Draw on Gottman, EFT and Hold Me Tight, attachment science, modern sex research, Perel, Nagoski, Kerner, Schnarch, Love and Stosny, and flexible love-language tools. Be concrete and low-hedge. Redirect only for imminent danger, abuse, coercive control, minors, non-consent, self-harm, stalking, or medical/legal/psychiatric decisions.
building-sf-integrations
IncludedSalesforce integration architecture and runtime plumbing with 120-point scoring. Use this skill to set up Named Credentials, External Credentials, External Services, REST/SOAP callout patterns, Platform Events, and Change Data Capture. TRIGGER when: user sets up Named Credentials, External Services, REST/SOAP callouts, Platform Events, CDC, or touches .namedCredential-meta.xml files. DO NOT TRIGGER when: Connected App/OAuth config (use configuring-connected-apps), Apex-only logic (use generating-apex), or data import/export (use handling-sf-data).
venue-templates
IncludedAccess comprehensive LaTeX templates, formatting requirements, and submission guidelines for major scientific publication venues (Nature, Science, PLOS, IEEE, ACM), academic conferences (NeurIPS, ICML, CVPR, CHI), research posters, and grant proposals (NSF, NIH, DOE, DARPA). This skill should be used when preparing manuscripts for journal submission, conference papers, research posters, or grant proposals and need venue-specific formatting requirements and templates.
let-fate-decide
IncludedDraws the 12 Houses of the Zodiac Tarot spread to inject entropy into planning when prompts are vague, ambiguous, or casually delegated. Interprets the spread to guide next steps. Use when the user says 'let fate decide', 'YOLO', 'whatever', 'idk', or other nonchalant phrases, makes Yu-Gi-Oh references, or when you are about to arbitrarily pick between multiple reasonable approaches. Prefer over ask-questions-if-underspecified when the user's tone is casual or playful rather than precision-seeking.
net-ops
IncludedCross-platform network troubleshooting (Windows, macOS, Linux) via local or remote shell. Use for: DNS broken, can't resolve hostnames, nslookup/dig works but apps fail, NRPT, WFP, scutil, /etc/resolver, systemd-resolved, /etc/resolv.conf, NetworkManager, VPN DNS leak residue (ProtonVPN/Mullvad/WireGuard/AnyConnect), AV/firewall blocking DNS or DoH, Tailscale DNS interaction, intermittent connectivity, remote diagnostics over SSH.