BREAKING CHANGES:
- Renamed HandleGET -> MemberGET for member-level routes
- Renamed HandlePOST -> MemberPOST for member-level routes
- Renamed HandlePUT -> MemberPUT for member-level routes
- Renamed HandlePATCH -> MemberPATCH for member-level routes
- Renamed HandleDELETE -> MemberDELETE for member-level routes
New Features:
- Added collection-level route methods: GET, POST, PUT, PATCH, DELETE
- Clear distinction between collection (/pattern/action) and member (/pattern/{id}/action) routes
- Comprehensive documentation (README, CONTRIBUTING, QUICKSTART, DOCS)
- Development tooling (Makefile, check.sh script)
- AI coding assistant guidelines (.cursorrules)
- GitHub Actions CI/CD pipeline
- golangci-lint configuration
Code Quality:
- Optimized struct field alignment for better memory usage
- All code passes go vet, staticcheck, and fieldalignment
- All tests pass with race detector
- Go 1.25+ requirement enforced
Documentation:
- Complete README rewrite with examples
- CONTRIBUTING.md with development guidelines
- QUICKSTART.md for new users
- CHANGELOG.md with version history
- SUMMARY.md documenting all changes
- DOCS.md as documentation index
7.0 KiB
7.0 KiB
Quick Start Guide
Get up and running with Mux in under 5 minutes!
Installation
go get code.patial.tech/go/mux
Requirements
- Go 1.25 or higher
Your First Route
Create main.go:
package main
import (
"fmt"
"net/http"
"code.patial.tech/go/mux"
)
func main() {
m := mux.New()
m.GET("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello, Mux!")
})
m.Serve(func(srv *http.Server) error {
srv.Addr = ":8080"
fmt.Println("Server listening on http://localhost:8080")
return srv.ListenAndServe()
})
}
Run it:
go run main.go
Visit http://localhost:8080 🎉
Common Patterns
1. Multiple Routes
m := mux.New()
m.GET("/", homeHandler)
m.GET("/about", aboutHandler)
m.GET("/users/{id}", showUser)
m.POST("/users", createUser)
2. Using Middleware
m := mux.New()
// Add global middleware
m.Use(loggingMiddleware)
m.Use(authMiddleware)
// All routes will use both middleware
m.GET("/protected", protectedHandler)
3. Route Groups
m := mux.New()
// Public routes
m.GET("/", homeHandler)
// API routes with shared middleware
m.Group(func(api *mux.Mux) {
api.Use(jsonMiddleware)
api.Use(apiAuthMiddleware)
api.GET("/api/users", listUsers)
api.POST("/api/users", createUser)
})
4. RESTful Resources
m := mux.New()
m.Resource("/posts", func(res *mux.Resource) {
res.Index(listPosts) // GET /posts
res.Create(createPost) // POST /posts
res.View(showPost) // GET /posts/{id}
res.Update(updatePost) // PUT /posts/{id}
res.Delete(deletePost) // DELETE /posts/{id}
// Custom collection routes
res.POST("/search", searchPosts) // POST /posts/search
// Custom member routes
res.MemberPOST("/publish", publishPost) // POST /posts/{id}/publish
})
5. URL Parameters
m.GET("/users/{id}", func(w http.ResponseWriter, r *http.Request) {
userID := r.PathValue("id")
fmt.Fprintf(w, "User ID: %s", userID)
})
m.GET("/posts/{year}/{month}/{slug}", func(w http.ResponseWriter, r *http.Request) {
year := r.PathValue("year")
month := r.PathValue("month")
slug := r.PathValue("slug")
// Handle request...
})
Complete Example
Here's a more complete example:
package main
import (
"encoding/json"
"fmt"
"log/slog"
"net/http"
"time"
"code.patial.tech/go/mux"
)
func main() {
m := mux.New()
// Global middleware
m.Use(loggingMiddleware)
// Routes
m.GET("/", homeHandler)
m.GET("/health", healthHandler)
// API group
m.Group(func(api *mux.Mux) {
api.Use(jsonMiddleware)
// Users resource
api.Resource("/users", func(res *mux.Resource) {
res.Index(listUsers)
res.Create(createUser)
res.View(showUser)
res.Update(updateUser)
res.Delete(deleteUser)
// Custom routes
res.POST("/search", searchUsers)
res.MemberGET("/posts", getUserPosts)
})
})
// Debug routes
m.GET("/debug/routes", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain")
m.PrintRoutes(w)
})
// Start server
m.Serve(func(srv *http.Server) error {
srv.Addr = ":8080"
srv.ReadTimeout = 30 * time.Second
srv.WriteTimeout = 30 * time.Second
slog.Info("Server starting", "addr", srv.Addr)
return srv.ListenAndServe()
})
}
// Middleware
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
next.ServeHTTP(w, r)
slog.Info("request",
"method", r.Method,
"path", r.URL.Path,
"duration", time.Since(start))
})
}
func jsonMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
next.ServeHTTP(w, r)
})
}
// Handlers
func homeHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Welcome to Mux!")
}
func healthHandler(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(map[string]string{"status": "ok"})
}
func listUsers(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(map[string][]string{"users": {}})
}
func createUser(w http.ResponseWriter, r *http.Request) {
// Parse request body and create user
json.NewEncoder(w).Encode(map[string]string{"message": "User created"})
}
func showUser(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
json.NewEncoder(w).Encode(map[string]string{"id": id, "name": "John Doe"})
}
func updateUser(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
json.NewEncoder(w).Encode(map[string]string{"message": fmt.Sprintf("User %s updated", id)})
}
func deleteUser(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
json.NewEncoder(w).Encode(map[string]string{"message": fmt.Sprintf("User %s deleted", id)})
}
func searchUsers(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(map[string][]string{"results": {}})
}
func getUserPosts(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
json.NewEncoder(w).Encode(map[string]interface{}{
"user_id": id,
"posts": []string{},
})
}
Next Steps
- Read the full README for detailed documentation
- Check CONTRIBUTING.md for code quality standards
- Look at the example directory for more examples
- Review the middleware package for built-in middleware
Testing Your Routes
package main
import (
"net/http"
"net/http/httptest"
"testing"
"code.patial.tech/go/mux"
)
func TestHomeHandler(t *testing.T) {
m := mux.New()
m.GET("/", homeHandler)
req := httptest.NewRequest(http.MethodGet, "/", nil)
rec := httptest.NewRecorder()
m.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Errorf("expected status 200, got %d", rec.Code)
}
}
Common Issues
Port Already in Use
If you see "address already in use", change the port:
srv.Addr = ":8081" // Use a different port
404 Not Found
Make sure your route patterns start with /:
m.GET("/users", handler) // ✅ Correct
m.GET("users", handler) // ❌ Wrong
Middleware Not Working
Ensure middleware is registered before routes:
m.Use(middleware1) // ✅ Register first
m.GET("/route", handler) // Then add routes
Getting Help
- Check the README for detailed documentation
- Look at examples for working code
- Open an issue on GitHub for bugs or questions
Happy routing! 🚀