NotNet

May 3, 2026 ยท View on GitHub

A lightweight HTTP router written in Go.

Release Go Version GoDoc Tests Go Report Card codecov License: MIT

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.

ChatGPT Image Apr 23, 2026, 08_48_29 PM

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/:id out 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/Request wrappers.
  • Server-Sent Events (SSE): Built-in support for streaming real-time updates to clients with res.SSE() and res.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.