Standard Library Compatibility
Okapi integrates seamlessly with Go’s net/http standard library, enabling you to:
- Use existing
http.Handlermiddleware - Register standard
http.HandlerFunchandlers - Combine Okapi-style routes with standard library handlers
This makes Okapi ideal for gradual adoption or hybrid use in existing Go projects.
Middleware Compatibility
Okapi’s UseMiddleware bridges standard http.Handler middleware into Okapi’s middleware system. This lets you reuse the wide ecosystem of community-built middleware—such as logging, metrics, tracing, compression, and more.
Signature
func (o *Okapi) UseMiddleware(middleware func(http.Handler) http.Handler)
Example: Injecting a Custom Header
o := okapi.Default()
// Add a custom version header to all responses
o.UseMiddleware(func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("X-Version", "v1.2.0")
next.ServeHTTP(w, r)
})
})
Example: Using Third-Party Middleware
import (
"github.com/gorilla/handlers"
"github.com/jkaninda/okapi"
)
o := okapi.Default()
// Use Gorilla's CORS middleware
o.UseMiddleware(handlers.CORS(
handlers.AllowedOrigins([]string{"*"}),
handlers.AllowedMethods([]string{"GET", "POST", "PUT", "DELETE"}),
))
Handler Compatibility
You can register any http.HandlerFunc using HandleStd, or use full http.Handler instances via HandleHTTP. These retain Okapi’s routing and middleware features while supporting familiar handler signatures.
HandleStd Signature
func (o *Okapi) HandleStd(method, path string, handler http.HandlerFunc, opts ...RouteOption)
Example: Basic Standard Library Handler
o := okapi.Default()
o.HandleStd("GET", "/greeting", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello from Okapi!"))
})
Example: Using http.Handler
type MyHandler struct{}
func (h *MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello from custom handler!"))
}
o.HandleHTTP("GET", "/custom", &MyHandler{})
Migration Tips
Migrating an existing net/http application? Okapi makes it painless.
Mixed Routing Support
You can mix Okapi and standard handlers in the same application:
// Okapi-style route
o.Handle("GET", "/okapi", func(c *okapi.Context) error {
return c.OK(okapi.M{"status": "ok"})
})
// Standard library handler
o.HandleStd("GET", "/standard", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("standard response"))
})
Gradual Migration
Start by wrapping your existing routes:
// Existing handler
func oldHandler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("legacy code"))
}
// Register with Okapi
o.HandleStd("GET", "/legacy", oldHandler)
// New Okapi-style handler
o.Get("/new", func(c *okapi.Context) error {
return c.OK(okapi.M{"message": "new code"})
})
Converting Handlers
Convert standard handlers to Okapi style when ready:
// Before (standard library)
func handler(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(map[string]string{"message": "hello"})
}
// After (Okapi)
func handler(c *okapi.Context) error {
return c.JSON(200, okapi.M{"message": "hello"})
}
Error Handling Differences
| Aspect | http.HandlerFunc | okapi.HandlerFunc |
|---|---|---|
| Response Writing | Must manually call w.WriteHeader(...) | Return an error or use helper methods |
| Error Handling | Handle errors inline within the handler | Return errors; Okapi handles them |
| Status Codes | Set explicitly via w.WriteHeader(code) | Use helpers like c.OK(), c.JSON() |
| Content Type | Set manually via w.Header().Set(...) | Automatically set based on method used |
Best Practices
- Start with Standard Library: Use
HandleStdfor existing code - Migrate Gradually: Convert one route at a time to Okapi style
- Use Middleware: Leverage Okapi’s middleware for cross-cutting concerns
- Consistent Error Handling: Adopt Okapi’s error handling patterns for new code
- Document Both Styles: Keep documentation clear when mixing handler types
Example: Complete Migration
Before (Pure net/http)
func main() {
http.HandleFunc("/users", func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
users := []string{"Alice", "Bob"}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(users)
})
http.ListenAndServe(":8080", nil)
}
After (Okapi)
func main() {
o := okapi.Default()
o.Get("/users", func(c *okapi.Context) error {
users := []string{"Alice", "Bob"}
return c.JSON(http.StatusOK, users)
})
o.Start()
}
Accessing Underlying Objects
When needed, you can access the underlying http.Request and http.ResponseWriter:
o.Get("/raw", func(c *okapi.Context) error {
req := c.Request() // *http.Request
w := c.Response() // http.ResponseWriter
// Use standard library directly
w.Header().Set("X-Custom", "value")
w.WriteHeader(http.StatusOK)
w.Write([]byte("direct access"))
return nil
})