NotNet
May 3, 2026 ยท View on GitHub
A lightweight HTTP router written in Go.
NotNet is a lightweight, high-performance, and ergonomic routing framework for Go. It allows you to quickly structure RESTful HTTP services with an expressive API, built-in middleware, and custom group routing.
Features
- Expressive Routing: Simple
.GET(),.POST(),.PUT(), etc., for robust route handling. - Route Groups: Group endpoints with shared prefixes and nested middleware stacks.
- Path Parameters: Extract dynamic URL parameters like
/users/:idout of the box. - Pre-packaged Middleware:
Logger: Detailed request logging.Recovery: Auto-recovery from application panics.CORS: Simplifies Cross-Origin Resource Sharing.RateLimit: Prevents abuse through IP-based request limits.AuthRequired: Simple stubbed bearer token checking.RequestID: Track requests universally with trace IDs.
- Extensible Request/Response Engine: Bind JSON payloads quickly, return robust JSON, HTML or strings using dedicated
Response/Requestwrappers. - Server-Sent Events (SSE): Built-in support for streaming real-time updates to clients with
res.SSE()andres.SendEvent().
Installation
Run this in your Go module project to get notnet:
go get github.com/nottechdm/notnet
Quick Start
Creating an API in NotNet takes only a few lines:
package main
import (
"log"
"github.com/nottechdm/notnet/pkg/notnet"
)
func main() {
app := notnet.New(nil)
// Attach Universal Middleware
app.Use(notnet.Logger())
app.Use(notnet.Recovery())
// Simple Route
app.GET("/ping", func(req *notnet.Request, res *notnet.Response) error {
return res.String(200, "pong")
})
// Dynamic Path Parameters
app.GET("/users/:id", func(req *notnet.Request, res *notnet.Response) error {
id := req.Param("id")
return res.JSON(200, map[string]string{"user_id": id})
})
// Read JSON Payload
app.POST("/api/data", func(req *notnet.Request, res *notnet.Response) error {
var payload map[string]interface{}
if err := req.BindJSON(&payload); err != nil {
return res.JSON(400, map[string]string{"error": "invalid json"})
}
payload["received"] = true
return res.JSON(201, payload)
})
log.Println("Server running on :8080")
log.Fatal(app.Listen(":8080"))
}
Route Groups & Custom Middleware
You can isolate your authentication handlers from public endpoints easily by using groups:
// Public
app.GET("/status", func(req *notnet.Request, res *notnet.Response) error {
return res.JSON(200, map[string]string{"status": "ok"})
})
// Protected API V1 Group
api := app.Group("/api/v1", notnet.AuthRequired())
api.GET("/dashboard", func(req *notnet.Request, res *notnet.Response) error {
return res.JSON(200, map[string]string{"msg": "You have access!"})
})
Custom Handlers
NotNet comes with default 404, error, and panic handling, but you can override them with your unique application responses at any time:
app.SetNotFoundHandler(func(req *notnet.Request, res *notnet.Response) {
res.JSON(404, map[string]string{
"error": "This page does not exist",
"path": req.Path(),
})
})
Server-Sent Events (SSE)
NotNet provides native support for Server-Sent Events, allowing you to stream real-time updates to your clients effortlessly:
app.GET("/events", func(req *notnet.Request, res *notnet.Response) error {
// Set up the SSE connection (sets headers and flushes)
res.SSE()
ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop()
for i := 0; i < 5; i++ {
<-ticker.C
// Send a named event with data (can be string, []byte, or any JSON-marshalable object)
res.SendEvent("message", map[string]interface{}{
"id": i,
"text": "Hello from NotNet!",
})
}
return nil
})
License
This project is licensed under the MIT License.