A HTTP Mux web framework lightweight and high performance π₯ priority-based routing with Redix Tree which is based on fasthttp
To install Ming, use the go get command:
go get -u github.com/hypnguyen1209/ming/v2This will download and install the latest version of Ming and its dependencies in your Go module.
β
High Performance - Built on fasthttp for maximum speed
β
Named Parameters - /user/{name} with parameter extraction
β
Optional Parameters - /api/users/{id?} for flexible routing
β
Regex Validation - /product/{id:[0-9]+} for parameter validation
β
Catch-All Routes - /files/{path:*} for wildcard matching
β
Priority-Based Routing - Radix tree with optimized lookups for O(k) matching complexity
β
Route Conflict Resolution - Static routes take precedence over parameter routes
β
Method Support - GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS, TRACE, CONNECT
β
Static File Serving - Built-in static file handler
β
Middleware Support - Custom panic handlers and error handling
β
Thread-Safe - Concurrent request handling
β
Zero Memory Allocations - Built on fasthttp's zero-allocation philosophy
package main
import (
"fmt"
"github.com/hypnguyen1209/ming/v2"
"github.com/valyala/fasthttp"
)
func main() {
r := ming.New()
// Basic route
r.Get("/", func(ctx *fasthttp.RequestCtx) {
ctx.WriteString("Hello World!")
})
// Named parameters
r.Get("/user/{name}", func(ctx *fasthttp.RequestCtx) {
name := ming.Param(ctx, "name")
fmt.Fprintf(ctx, "Hello %s!", name)
})
// Multiple parameters
r.Get("/user/{id}/post/{postId}", func(ctx *fasthttp.RequestCtx) {
id := ming.Param(ctx, "id")
postId := ming.Param(ctx, "postId")
fmt.Fprintf(ctx, "User: %s, Post: %s", id, postId)
})
// Regex validation
r.Get("/product/{id:[0-9]+}", func(ctx *fasthttp.RequestCtx) {
id := ming.Param(ctx, "id")
fmt.Fprintf(ctx, "Product: %s", id)
})
// Catch-all routes
r.Get("/files/{path:*}", func(ctx *fasthttp.RequestCtx) {
path := ming.Param(ctx, "path")
fmt.Fprintf(ctx, "File: %s", path)
})
r.Run(":8080")
}r.Get("/user/{name}", handler) // Matches: /user/johnr.Get("/api/users/{id?}", handler) // Matches: /api/users/ and /api/users/123r.Get("/product/{id:[0-9]+}", handler) // Only numeric IDs
r.Get("/category/{name:[a-zA-Z]+}", handler) // Only alphabetic namesr.Get("/files/{filepath:*}", handler) // Matches: /files/docs/readme.txtMing supports multiple catch-all routes with different path prefixes in the same router instance:
// Each catch-all route captures everything after its prefix
r.Get("/files/{filepath:*}", fileHandler)
r.Get("/documents/{docpath:*}", documentHandler)
r.Get("/api/v1/proxy/{url:*}", apiProxyHandler)
r.Get("/media/{mediapath:*}", mediaHandler)This feature is useful for creating file servers, API proxies, or any application that needs to match multiple wildcard paths with different prefixes.
See the multiple catch-all example for a complete implementation.
r.Get("/api/{version:[v][0-9]+}/users/{userId:[0-9]+}/files/{filepath:*}", handler)
// Matches: /api/v1/users/123/files/documents/report.pdfr.Get(path, handler) // GET requests
r.Post(path, handler) // POST requests
r.Put(path, handler) // PUT requests
r.Delete(path, handler) // DELETE requests
r.Patch(path, handler) // PATCH requests
r.Head(path, handler) // HEAD requests
r.Options(path, handler) // OPTIONS requests
r.Trace(path, handler) // TRACE requests
r.Connect(path, handler) // CONNECT requests
r.All(path, handler) // All HTTP methods// Get parameter value as string
name := ming.Param(ctx, "name")
// Get query parameter
q := string(ming.Query(ctx, "search"))
// Get request body
body := ming.Body(ctx)Ming provides easy access to URL query parameters through the ming.Query() function:
// For URL: /search?term=ming&page=2
func SearchHandler(ctx *fasthttp.RequestCtx) {
// Get single query parameters
term := string(ming.Query(ctx, "term")) // Returns "ming"
page := string(ming.Query(ctx, "page")) // Returns "2"
// Check if parameter exists
if len(ming.Query(ctx, "term")) > 0 {
// Parameter exists
}
// Get all query parameters
queryArgs := ctx.QueryArgs()
// Iterate through all query parameters
queryArgs.VisitAll(func(key, value []byte) {
fmt.Printf("Parameter %s = %s\n", string(key), string(value))
})
// Respond with search results
fmt.Fprintf(ctx, "Search results for: %s (Page %s)", term, page)
}Ming makes it easy to handle various types of POST request data:
// Handle JSON POST request
func JsonHandler(ctx *fasthttp.RequestCtx) {
// Get full request body as []byte
body := ming.Body(ctx)
// Now you can unmarshal the JSON data
var data map[string]interface{}
if err := json.Unmarshal(body, &data); err != nil {
ctx.SetStatusCode(400)
fmt.Fprintf(ctx, "Invalid JSON: %s", err.Error())
return
}
// Process the data
fmt.Fprintf(ctx, "Received JSON with %d fields", len(data))
}
// Handle form POST request
func FormHandler(ctx *fasthttp.RequestCtx) {
// Access form values
username := string(ctx.FormValue("username"))
email := string(ctx.FormValue("email"))
// Check if a specific form field was provided
if len(ctx.FormValue("username")) == 0 {
ctx.SetStatusCode(400)
ctx.WriteString("Username is required")
return
}
// Get all form values
ctx.PostArgs().VisitAll(func(key, value []byte) {
fmt.Printf("Form field %s = %s\n", string(key), string(value))
})
// Access uploaded files (for multipart/form-data)
ctx.Request.SetBodyStream(ctx.RequestBodyStream(), int(ctx.Request.Header.ContentLength()))
form, err := ctx.MultipartForm()
if err == nil {
// Get uploaded files by form field name
if files, ok := form.File["upload"]; ok && len(files) > 0 {
filename := files[0].Filename
// Process the uploaded file
}
}
fmt.Fprintf(ctx, "Form processed for user: %s", username)
}Ming handles various content types transparently:
- application/json: Use
ming.Body(ctx)and then unmarshal the JSON - application/x-www-form-urlencoded: Use
ctx.FormValue(key)to access form fields - multipart/form-data: Use
ctx.MultipartForm()to access form fields and uploaded files - text/plain or other raw data: Use
ming.Body(ctx)to access the raw body
r.Static("./public", true) // Serve static files with directory listingr.NotFound = func(ctx *fasthttp.RequestCtx) {
ctx.SetStatusCode(404)
ctx.WriteString("Page not found!")
}
r.MethodNotAllowed = func(ctx *fasthttp.RequestCtx) {
ctx.SetStatusCode(405)
ctx.WriteString("Method not allowed!")
}package main
import (
"fmt"
"github.com/hypnguyen1209/ming/v2"
"github.com/valyala/fasthttp"
)
func Home(ctx *fasthttp.RequestCtx) {
ctx.WriteString("Home")
}
func AllHandler(ctx *fasthttp.RequestCtx) {
ctx.WriteString("123")
}
func SearchHandler(ctx *fasthttp.RequestCtx) {
q := string(ming.Query(ctx, "name"))
fmt.Fprintf(ctx, "Hello %s", q)
}
func PostHandler(ctx *fasthttp.RequestCtx) {
ctx.Write(ming.Body(ctx))
}
func main() {
r := ming.New()
r.Static("./", true)
r.Get("/", Home)
r.Post("/add", PostHandler)
r.All("/all", AllHandler)
r.Get("/search", SearchHandler)
r.Run("127.0.0.1:8000")
// r.Run(":8000")
}See more examples in the _examples directory:
- basic example - Basic routing and static files
- parameters example - Comprehensive parameter features
- simple example - Quick parameter demonstration
Ming is built on top of fasthttp, which provides exceptional performance compared to the standard net/http package. The router is designed to be:
- Ultra-fast: Up to 10x faster than standard Go net/http routing
- Memory-efficient: Minimizes allocations to reduce GC pressure
- Optimized routing: Uses a fast radix tree implementation for route matching
- Connection reuse: Takes advantage of fasthttp's connection pooling
Performance benchmarks show Ming handling thousands of requests per second with minimal resource consumption. For high-load applications, this translates to better response times and lower infrastructure costs.
Ming provides a simple way to extract variables from URL paths:
// Define a route with a named parameter
r.Get("/user/{name}", func(ctx *fasthttp.RequestCtx) {
// Extract the parameter value
name := ming.Param(ctx, "name")
fmt.Fprintf(ctx, "Hello, %s!", name)
})The parameter values are extracted at request time and can be accessed using ming.Param(ctx, "paramName"). Named parameters are perfect for RESTful APIs or any scenario where you need to capture parts of the URL path.
Ming supports optional parameters with the ? syntax, allowing a single route to handle multiple URL patterns:
// This route handles both /api/users and /api/users/42
r.Get("/api/users/{id?}", func(ctx *fasthttp.RequestCtx) {
id := ming.Param(ctx, "id")
if id == "" {
// Handle case without ID (list all users)
fmt.Fprintf(ctx, "List all users")
} else {
// Handle case with ID (get specific user)
fmt.Fprintf(ctx, "Get user with ID: %s", id)
}
})This feature helps reduce code duplication by handling related endpoints in a single handler.
Ming supports parameter validation using regex patterns, ensuring that parameters match specific formats:
// Only match numeric IDs
r.Get("/product/{id:[0-9]+}", func(ctx *fasthttp.RequestCtx) {
id := ming.Param(ctx, "id")
// ID is guaranteed to contain only digits
fmt.Fprintf(ctx, "Product ID: %s", id)
})
// Only match alphabetic categories
r.Get("/category/{name:[a-zA-Z]+}", func(ctx *fasthttp.RequestCtx) {
name := ming.Param(ctx, "name")
// Name is guaranteed to contain only letters
fmt.Fprintf(ctx, "Category: %s", name)
})
// Match specific date format
r.Get("/date/{date:[0-9]{4}-[0-9]{2}-[0-9]{2}}", func(ctx *fasthttp.RequestCtx) {
date := ming.Param(ctx, "date")
// Date is guaranteed to be in YYYY-MM-DD format
fmt.Fprintf(ctx, "Date: %s", date)
})Regex validation helps ensure data integrity and proper API usage, preventing malformed requests from reaching your handlers.
For situations where you need to capture an entire path segment, including slashes, Ming provides catch-all parameters:
// Match any path under /files/
r.Get("/files/{filepath:*}", func(ctx *fasthttp.RequestCtx) {
filepath := ming.Param(ctx, "filepath")
fmt.Fprintf(ctx, "Requested file: %s", filepath)
})Ming supports multiple catch-all routes with different path prefixes:
// Multiple catch-all routes for different areas of your application
r.Get("/files/{filepath:*}", filesHandler)
r.Get("/documents/{docpath:*}", documentsHandler)
r.Get("/api/v1/proxy/{url:*}", proxyHandler)This is particularly useful for:
- File servers
- Proxy routes
- Backend APIs that preserve URL structure
- Documentation systems
- APIs with multiple resource types
Ming uses a sophisticated radix tree (also known as a prefix tree or trie) to store and match routes. This data structure provides several key advantages:
- O(k) Matching Complexity: Where k is the length of the URL path, not the number of routes
- Memory Efficiency: Common prefixes are stored only once, reducing memory usage
- Predictable Performance: Lookup time remains consistent regardless of route count
- Path Compression: Optimized tree structure with compressed nodes for faster traversal
The routing algorithm follows specific priority rules:
- Static routes have the highest priority
- More specific routes take precedence over general ones
- Parameter routes have lower priority than static routes
For example:
r.Get("/user/profile", specificHandler) // Higher priority - matches exactly /user/profile
r.Get("/user/{name}", generalHandler) // Lower priority - matches /user/john but not /user/profileThis priority system ensures that the most appropriate handler is always selected for each request. The radix tree implementation automatically handles these priority rules during route registration and lookup.
Unlike hash map-based routers that require full string comparisons, Ming's radix tree performs character-by-character matching, allowing for early termination and more efficient handling of similar routes with different parameters.
When multiple routes could match the same URL pattern, Ming resolves conflicts using these rules:
- Static routes always win over parameter routes
- The most specific route wins (fewer parameters/wildcards)
- Routes defined earlier win when other rules don't apply
For example:
// This will match /api/v1/users exactly
r.Get("/api/v1/users", listUsersV1)
// This will match any other version (v2, v3, etc.)
r.Get("/api/{version}/users", listUsersGeneric)
// This won't match any of the above, because it's less specific
r.Get("/{service}/{version}/users", generalHandler)This conflict resolution system makes routing predictable and intuitive, while still providing flexibility.
Ming provides a simple way to serve static files from your filesystem. The Static method configures the router to serve files from a specified directory:
r.Static(rootPath string, isIndexPage bool)Parameters:
rootPath: The root directory path from which to serve filesisIndexPage: Boolean flag to enable/disable automatic generation of directory listing pages
r := ming.New()
// Serve files from the current directory with directory listing enabled
r.Static("./", true)
// Now run the server
r.Run(":8080")r := ming.New()
// Serve files from the "public" directory without directory listings
r.Static("./public", false)
r.Run(":8080")Ming's static file serving uses the fasthttp.FS functionality behind the scenes. When you call r.Static():
- It configures the
NotFoundhandler of the router to serve static files - Any request that doesn't match a defined route will attempt to serve a file from the specified directory
- If a matching file is found, it's served with the appropriate content-type
- If directory listings are enabled and a directory is requested, an index page is generated
- If no matching file exists, a 404 response is returned
r := ming.New()
// Define your API routes first
r.Get("/api/users", apiUsersHandler)
r.Post("/api/login", apiLoginHandler)
// Then configure static file serving
// This will handle any routes not matched by the API endpoints
r.Static("./public", true)
r.Run(":8080")-
Security: Be cautious about which directories you expose. Never serve files from sensitive system directories.
-
Directory Listings: Use
isIndexPage: falsein production to prevent directory structure exposure. -
Performance: For high-traffic sites, consider using a dedicated static file server like Nginx or a CDN.
-
Route Priority: Define all your dynamic routes before calling
r.Static()to ensure proper routing. -
Hidden Files: Note that Ming will serve all files in the specified directory, including hidden files, unless handled by other routes.
When using Ming's static file server, you may want to implement HTTP caching headers yourself in a custom middleware for optimal performance.
Ming router resolves route conflicts with these rules:
-
Static routes always take precedence over parameter routes
r.Get("/user/profile", profileHandler) // This takes precedence r.Get("/user/{id}", userHandler) // This matches any other /user/X
-
When routes conflict, the most specific one wins
r.Get("/api/v1/users", listUsersHandler) // Specific to /api/v1/users r.Get("/api/{version}/users", versionHandler) // For any other version
Ming provides built-in panic recovery to prevent crashes:
r := ming.New()
// Custom panic handler
r.PanicHandler = func(ctx *fasthttp.RequestCtx, p interface{}) {
ctx.SetStatusCode(500)
fmt.Fprintf(ctx, "Internal error: %v", p)
// Log the error, notify admins, etc.
}Ming is ideal for creating public APIs that also serve static assets for documentation. Here's a common pattern:
r := ming.New()
// API routes with versioning
r.Get("/api/v1/users", apiV1GetUsers)
r.Post("/api/v1/users", apiV1CreateUser)
r.Get("/api/v1/products", apiV1GetProducts)
// API documentation routes
r.Get("/docs/api", apiDocsHandler)
// Serve static files (API docs, frontend assets, etc.)
r.Static("./public", false)For production applications, consider organizing your static assets with this directory structure:
public/
βββ assets/
β βββ css/
β βββ images/
β βββ js/
βββ docs/
β βββ api/
β βββ guides/
βββ index.html
When serving static files in production:
- Never expose sensitive configuration files or data
- Disable directory listings with
r.Static("./public", false) - Consider adding a middleware for rate limiting static file access
- Set proper cache headers for static assets
- For larger applications, consider a dedicated CDN or static file server
See the static file example for a complete working example.
Ming router supports middleware patterns for authentication, authorization, and other cross-cutting concerns. Here's how to implement authentication:
// Authentication middleware
func authMiddleware(next fasthttp.RequestHandler) fasthttp.RequestHandler {
return func(ctx *fasthttp.RequestCtx) {
// Get token from request header
token := string(ctx.Request.Header.Peek("Authorization"))
// Validate token
if !validateToken(token) {
ctx.SetStatusCode(fasthttp.StatusUnauthorized)
ctx.WriteString("Unauthorized")
return
}
// Token is valid, continue to the handler
next(ctx)
}
}
// Protected route with middleware
r.Get("/api/profile", authMiddleware(func(ctx *fasthttp.RequestCtx) {
// This handler is only executed if auth is successful
ctx.WriteString("Protected data")
}))You can store user information in the request context for use in handlers:
// In auth middleware
ctx.SetUserValue("userId", userId)
// In handler
userId := ctx.UserValue("userId").(string)For role-based authorization, you can stack middleware:
// Admin access middleware
func adminOnly(next fasthttp.RequestHandler) fasthttp.RequestHandler {
return func(ctx *fasthttp.RequestCtx) {
role := ctx.UserValue("role").(string)
if role != "admin" {
ctx.SetStatusCode(fasthttp.StatusForbidden)
ctx.WriteString("Forbidden")
return
}
next(ctx)
}
}
// Endpoint requiring both authentication and admin role
r.Get("/admin/dashboard", authMiddleware(adminOnly(dashboardHandler)))See the auth example for a complete authentication implementation.
Ming is built on fasthttp for maximum performance. See benchmarks:
Source: https://github.com/smallnest/go-web-framework-benchmark

