330 lines
7.0 KiB
Markdown
330 lines
7.0 KiB
Markdown
|
|
# Quick Start Guide
|
||
|
|
|
||
|
|
Get up and running with Mux in under 5 minutes!
|
||
|
|
|
||
|
|
## Installation
|
||
|
|
|
||
|
|
```bash
|
||
|
|
go get code.patial.tech/go/mux
|
||
|
|
```
|
||
|
|
|
||
|
|
## Requirements
|
||
|
|
|
||
|
|
- Go 1.25 or higher
|
||
|
|
|
||
|
|
## Your First Route
|
||
|
|
|
||
|
|
Create `main.go`:
|
||
|
|
|
||
|
|
```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:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
go run main.go
|
||
|
|
```
|
||
|
|
|
||
|
|
Visit [http://localhost:8080](http://localhost:8080) 🎉
|
||
|
|
|
||
|
|
## Common Patterns
|
||
|
|
|
||
|
|
### 1. Multiple Routes
|
||
|
|
|
||
|
|
```go
|
||
|
|
m := mux.New()
|
||
|
|
|
||
|
|
m.GET("/", homeHandler)
|
||
|
|
m.GET("/about", aboutHandler)
|
||
|
|
m.GET("/users/{id}", showUser)
|
||
|
|
m.POST("/users", createUser)
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2. Using Middleware
|
||
|
|
|
||
|
|
```go
|
||
|
|
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
|
||
|
|
|
||
|
|
```go
|
||
|
|
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
|
||
|
|
|
||
|
|
```go
|
||
|
|
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
|
||
|
|
|
||
|
|
```go
|
||
|
|
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:
|
||
|
|
|
||
|
|
```go
|
||
|
|
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](README.md) for detailed documentation
|
||
|
|
- Check [CONTRIBUTING.md](CONTRIBUTING.md) for code quality standards
|
||
|
|
- Look at the [example directory](example/) for more examples
|
||
|
|
- Review the [middleware package](middleware/) for built-in middleware
|
||
|
|
|
||
|
|
## Testing Your Routes
|
||
|
|
|
||
|
|
```go
|
||
|
|
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:
|
||
|
|
|
||
|
|
```go
|
||
|
|
srv.Addr = ":8081" // Use a different port
|
||
|
|
```
|
||
|
|
|
||
|
|
### 404 Not Found
|
||
|
|
|
||
|
|
Make sure your route patterns start with `/`:
|
||
|
|
|
||
|
|
```go
|
||
|
|
m.GET("/users", handler) // ✅ Correct
|
||
|
|
m.GET("users", handler) // ❌ Wrong
|
||
|
|
```
|
||
|
|
|
||
|
|
### Middleware Not Working
|
||
|
|
|
||
|
|
Ensure middleware is registered before routes:
|
||
|
|
|
||
|
|
```go
|
||
|
|
m.Use(middleware1) // ✅ Register first
|
||
|
|
m.GET("/route", handler) // Then add routes
|
||
|
|
```
|
||
|
|
|
||
|
|
## Getting Help
|
||
|
|
|
||
|
|
- Check the [README](README.md) for detailed documentation
|
||
|
|
- Look at [examples](example/) for working code
|
||
|
|
- Open an issue on GitHub for bugs or questions
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
Happy routing! 🚀
|