mows

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 17, 2026 License: MIT Imports: 14 Imported by: 0

README

MOWS - My Own Web Server

A minimal HTTP web framework for Go, designed for learning, simplicity, and zero boilerplate.

It provides:

  • Lightweight routing
  • Middleware support (global & route-specific)
  • Path parameters and route groups
  • JSON binding & validation
  • Centralized error handling
  • Logging and panic recovery

This project is intentionally minimal and will grow over time.

Installation

go get github.com/saintmili/mows

Quick Start

package main

import "mows"

func main() {
	app := mows.New()

	app.GET("/hello", func(c *mows.Context) error {
		c.JSON(200, map[string]string{
			"message": "hello world",
		})
        return nil
	})

	app.Run(":8080")
}

Run:

go run main.go

Visit http://localhost:8080/hello:

{"message":"hello world"}

Full Example: User API

See full example in examples/user-api/main.go

Features demonstrated:

  • Global middleware
  • Error handling with SetErrorHandler
  • Route groups (/api)
  • CRUD operations with path params (/users/:id)
  • JSON binding and validation
  • HTTP status codes (200, 201, 204, 400, 404)

Features

Feature Description
Routing GET, POST, PUT, DELETE with path params
Route Groups Nested prefixes and middleware
Middleware Global or route-specific middleware
Logging Built-in request logger middleware
Recovery Panic recovery middleware
JSON Binding BindJSON & BindAndValidate
Validation Struct validation using tags
Error Handling Centralized ErrorHandler
Response Helpers JSON(), String(), Status()

Engine

The Engine is the main application container.

app := mows.New()
app.Run(":8080")

The engine implements http.Handler internaly.

Basic routes

app.GET("/users", handler)
app.POST("/users", handler)
app.PUT("/users/:id", handler)
app.DELETE("/users/:id", handler)

Handlers use this signature:

func(c *mows.Context) error

Route Groups

Groups allow shared prefixes and middleware.

api := app.Group("/api")

api.GET("/users", listUsers)
api.GET("/users/:id", getUser)

Nested groups are supported:

admin := api.Group("/admin")
admin.GET("/stats", statsHandler)

Path Parameters

Define params using :name.

app.GET("/users/:id", func(c *mows.Context) error {
    id := c.Param("id")

    c.JSON(200, map[string]string{
        "user_id": id,
    })

    return nil
})

Sending Responses

JSON response
c.JSON(200, map[string]string{
    "status": "ok",
})
Plain text
c.String(200, "hello")

Access params:

id := c.Param("id")

Middleware

Middleware is the heart of MOWS

Middleware signature:

type Middleware func(HandlerFunc) HandlerFunc
Global middleware
app.Use(mows.Logger(), mows.Recover())

Runs for every request.

Route-specific middleware
auth := func(next mows.HandlerFunc) mows.HandlerFunc {
    return func(c *mows.Context) error {
        if c.Request.Header.Get("Authorization") == "" {
            c.JSON(401, map[string]string{"error":"unauthorized"})
            return nil
        }
        return next(c)
    }
}

app.GET("/private", handler, auth)

Middleware order:

Global → Group → Route → Handler

Built-in Middleware

Logger

Logs every request.

app.Use(mows.Logger())

Example output:

[2026-02-17 19:55:01] 200 | 245µs | GET /hello | 127.0.0.1:54321
Recovery

Prevents server crash on panic.

app.Use(mows.Recover())

Returns:

{ "error": "panic message" }

JSON Binding

Bind request JSON to struct.

type CreateUser struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
}

app.POST("/users", func(c *mows.Context) error {
    var req CreateUser

    if err := c.BindJSON(&req); err != nil {
        return err
    }

    c.JSON(200, req)
    return nil
})

Request Lifecycle

For each request:

  1. Request enters Engine
  2. Global middleware runs
  3. Group middleware runs
  4. Route middleware runs
  5. Handler executes
  6. Logger prints result

Project Structure Suggestion

For apps using MOWS:

myapp/
│
├── main.go
├── handlers/
│   ├── users.go
│   └── auth.go
├── middleware/
│   └── auth.go
└── models/

Testing Example

Use Go's built-in test server.

func TestHelloRoute(t *testing.T) {
	app := mows.New()

	app.GET("/hello", func(c *mows.Context) error {
		c.String(200, "ok")
        return nil
	})

	req := httptest.NewRequest("GET", "/hello", nil)
	w := httptest.NewRecorder()

	app.ServeHTTP(w, req)

	if w.Code != 200 {
		t.Fatal("expected 200")
	}
}

Roadmap (Upcoming)

Planned features:

  • Static file serving
  • HTML templates
  • Request ID middleware
  • Error handling / abort system
  • Validation helpers
  • OpenAPI generation

Documentation

Overview

Package mows is a minimal HTTP web framework designed for learning and building small web services.

Example:

app := mows.New()
app.Use(mows.Logger(), mows.Recover())

app.GET("/hello", func(c *mows.Context) error {
    c.JSON(200, map[string]string{"message":"hello"})
	return error
})

app.Run(":8080")

Package mows provides a minimal HTTP web framework focused on simplicity, middleware-first design, and clean routing.

MOWS is intended as a lightweight alternative for learning and building small services without heavy abstractions.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Context

type Context struct {
	Writer  *responseWriter
	Request *http.Request
	Params  map[string]string
	Status  int
	Engine  *Engine
}

Context carries request and response data across handlers and middleware.

It provides helper methods for:

  • Sending responses
  • Reading request data
  • Accessing path parameters

func NewContext

func NewContext(w *responseWriter, r *http.Request, engine *Engine) *Context

NewContext creates a new Context for the incoming HTTP request. It is used internally by the Engine during request dispatch.

func (*Context) BindJSON

func (c *Context) BindJSON(v any) error

BindJSON parses the request body as JSON into the provided struct.

Returns an error if:

  • Content-Type is not application/json
  • JSON is malformed
  • Decoding fails

func (*Context) BindJSONAndValidate

func (c *Context) BindJSONAndValidate(v any) error

BindJSONAndValidate binds JSON request body into the struct and validates it. This is a convenience helper combining BindJSON and Validate.

func (*Context) DefaultQuery

func (c *Context) DefaultQuery(key string, defaultValue string) string

DefaultQuery returns the query value or a default value if the key is missing.

func (*Context) DefaultQueryBool

func (c *Context) DefaultQueryBool(key string, defaultValue bool) bool

DefaultQueryBool returns a query parameter as bool or a default value if missing or invalid.

func (*Context) DefaultQueryInt

func (c *Context) DefaultQueryInt(key string, defaultValue int) int

DefaultQueryInt returns a query parameter as int or a default value if missing or invalid.

func (*Context) JSON

func (c *Context) JSON(code int, v any) error

JSON sends a JSON response with the provided status code.

func (*Context) Param

func (c *Context) Param(key string) string

Param returns the value of a path parameter.

Example:

app.GET("/users/:id", func(c *Context) error {
    id := c.Param("id")
	return nil
})

func (*Context) Query

func (c *Context) Query(key string) string

Query returns the value of a query parameter.

Example:

/users?page=2 → c.Query("page") == "2"

func (*Context) QueryBool

func (c *Context) QueryBool(key string) (bool, error)

QueryBool returns a query parameter parsed as a boolean. Accepted values: true, false, 1, 0.

func (*Context) QueryInt

func (c *Context) QueryInt(key string) (int, error)

QueryInt returns a query parameter parsed as an int. Returns an error if the value cannot be converted.

func (*Context) Text

func (c *Context) Text(code int, s string) error

String sends a plain text response.

func (*Context) Validate

func (c *Context) Validate(v any) error

Validate validates a struct using the configured validator. Returns an error if validation fails.

type Engine

type Engine struct {
	// contains filtered or unexported fields
}

Engine is the main application instance.

Engine implements http.Handler and is responsible for:

  • Route registration
  • Global middleware
  • Request dispatching

Create a new engine using New().

func New

func New() *Engine

New creates and returns a new Engine instance.

Example:

app := mows.New()

func (*Engine) DELETE

func (e *Engine) DELETE(path string, handlers ...HandlerFunc)

DELETE registers a route that responds to HTTP DELETE requests.

func (*Engine) GET

func (e *Engine) GET(path string, handlers ...HandlerFunc)

GET registers a route that responds to HTTP GET requests.

func (*Engine) Group

func (e *Engine) Group(prefix string, m ...Middleware) *RouterGroup

Group creates a new RouterGroup with the provided path prefix.

Example:

api := app.Group("/api")

func (*Engine) POST

func (e *Engine) POST(path string, handlers ...HandlerFunc)

POST registers a route that responds to HTTP POST requests.

func (*Engine) PUT

func (e *Engine) PUT(path string, handlers ...HandlerFunc)

PUT registers a route that responds to HTTP PUT requests.

func (*Engine) Run

func (e *Engine) Run(addr string) error

Run starts the HTTP server and listens on the given address.

This is a helper wrapper around http.ListenAndServe.

Example:

app.Run(":8080")

func (*Engine) RunTLS

func (e *Engine) RunTLS(addr, certFile, keyFile string) error

Run starts the HTTPS server and listens on the given address.

This is a helper wrapper around http.ListenAndServeTLS.

Example:

app.RunTLS(":443")

func (*Engine) ServeHTTP

func (e *Engine) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP implements the http.Handler interface. It should not be called directly by users.

func (*Engine) SetErrorHandler

func (e *Engine) SetErrorHandler(h ErrorHandler)

SetErrorHandler replaces the default error handler.

This allows applications to customize how errors are returned.

func (*Engine) Use

func (e *Engine) Use(m ...Middleware)

Use registers global middleware that runs for every request.

Middleware execution order:

Global → Group → Route → Handler

type ErrorHandler

type ErrorHandler func(*Context, error)

ErrorHandler defines a centralized error handling function.

It is invoked when a handler returns or triggers an error.

type HandlerFunc

type HandlerFunc func(*Context) error

HandlerFunc defines a request handler used by MOWS.

type Middleware

type Middleware func(HandlerFunc) HandlerFunc

Middleware defines a function that wraps a HandlerFunc.

Middleware can run logic before and/or after the next handler.

func Logger

func Logger() Middleware

Logger returns a middleware that logs HTTP requests.

Logged information includes:

  • Status code
  • Request latency
  • HTTP method
  • Request path
  • Client IP

func Recover

func Recover() Middleware

Recover returns a middleware that recovers from panics.

If a panic occurs, the middleware:

  • Prevents the server from crashing
  • Returns HTTP 500
  • Sends the panic message as JSON

type Router

type Router struct {
	// contains filtered or unexported fields
}

Router stores registered routes and performs route matching.

It supports static routes and parameterized paths such as:

/users/:id

func NewRouter

func NewRouter() *Router

NewRouter creates and initializes a new Router instance.

type RouterGroup

type RouterGroup struct {
	// contains filtered or unexported fields
}

RouterGroup represents a group of routes sharing a common prefix and optional middleware.

func (*RouterGroup) DELETE

func (rg *RouterGroup) DELETE(path string, handlers ...HandlerFunc)

DELETE registers a DELETE route inside the RouterGroup.

func (*RouterGroup) GET

func (rg *RouterGroup) GET(path string, handlers ...HandlerFunc)

GET registers a GET route inside the RouterGroup.

func (*RouterGroup) Group

func (rg *RouterGroup) Group(prefix string, m ...Middleware) *RouterGroup

Group creates a nested RouterGroup with an additional path prefix.

func (*RouterGroup) POST

func (rg *RouterGroup) POST(path string, handlers ...HandlerFunc)

POST registers a POST route inside the RouterGroup.

func (*RouterGroup) PUT

func (rg *RouterGroup) PUT(path string, handlers ...HandlerFunc)

PUT registers a PUT route inside the RouterGroup.

func (*RouterGroup) Use

func (rg *RouterGroup) Use(m ...Middleware)

Use attaches middleware to the RouterGroup.

Group middleware runs after global middleware but before route-specific middleware.

Directories

Path Synopsis
examples
user-api command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL