Files
mux/QUICKSTART.md
Ankit Patial 26bb9bf5ee feat: improve resource routing API and add comprehensive quality standards
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
2025-11-15 14:05:11 +05:30

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

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! 🚀