Building a minimal Go framework in public (v0.1.3)

Published: (January 20, 2026 at 12:58 AM EST)
3 min read
Source: Dev.to

Source: Dev.to

Introduction

Most Go web frameworks pull in dozens of dependencies—Gin has 9 direct dependencies, Echo 11, Fiber 15—so your go.mod quickly looks like a phone book. Marten takes a different approach: it uses only what Go gives you (e.g., net/http, encoding/json). No external packages, no vendor lock‑in.

Complete API Example

package main

import (
    "github.com/gomarten/marten"
    "github.com/gomarten/marten/middleware"
)

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

    app.Use(middleware.Logger)
    app.Use(middleware.Recover)

    app.GET("/", func(c *marten.Ctx) error {
        return c.OK(marten.M{"message": "Hello, World!"})
    })

    app.GET("/users/:id", func(c *marten.Ctx) error {
        id := c.ParamInt("id")
        return c.OK(marten.M{"id": id})
    })

    app.Run(":8080")
}

Router

Marten uses a radix‑tree router for efficient path matching. It supports:

  • Path parameters (:id)
  • Wildcards (*filepath)
  • Route groups
api := app.Group("/api/v1")
api.GET("/users", listUsers)
api.GET("/users/:id", getUser)
api.POST("/users", createUser)

Middleware

Middleware in Marten is just a function that wraps a handler:

func Timer(next marten.Handler) marten.Handler {
    return func(c *marten.Ctx) error {
        start := time.Now()
        err := next(c)
        log.Printf("took %v", time.Since(start))
        return err
    }
}

Built‑in Middleware

Marten ships with 14 built‑in middleware components:

  • Logger
  • Recover
  • CORS
  • RateLimit
  • BasicAuth
  • Timeout
  • Secure
  • BodyLimit
  • Compress
  • ETag
  • RequestID
  • Static
  • NoCache

Context (Ctx) Usage

Every request gets a Ctx object from a sync.Pool, reducing allocations and keeping memory usage low:

func handler(c *marten.Ctx) error {
    // Path parameters
    id := c.Param("id")

    // Query parameters
    page := c.QueryInt("page")

    // JSON binding
    var user User
    c.Bind(&user)

    // Response helpers
    return c.OK(user)
}

Static File Serving (v0.1.3)

The latest release adds static file serving with a full feature set:

app.Use(middleware.StaticWithConfig(middleware.StaticConfig{
    Root:   "./public",
    Prefix: "/static",
    MaxAge: 3600,
    Browse: false,
}))

Features include content‑type detection, HTTP caching (If-Modified-Since), directory browsing, and protection against directory‑traversal attacks.

Performance Benchmarks

Benchmarks against Gin, Echo, and Chi show Marten holding its own:

BenchmarkMartenGinEchoChi
Static Route1464 ns/op1336 ns/op1436 ns/op2202 ns/op
Param Route1564 ns/op1418 ns/op1472 ns/op2559 ns/op
JSON Response1755 ns/op2050 ns/op1835 ns/op1868 ns/op

Not the fastest, but competitive—and with zero dependencies.

Building Production‑Ready APIs

app := marten.New()

app.Use(
    middleware.RequestID,
    middleware.Logger,
    middleware.Recover,
    middleware.CORS(middleware.DefaultCORSConfig()),
    middleware.RateLimit(middleware.RateLimitConfig{
        Max:    100,
        Window: time.Minute,
    }),
)

api := app.Group("/api/v1")
api.GET("/users", listUsers)
api.POST("/users", createUser, authMiddleware)

SPA Fallback

// API routes
app.GET("/api/users", listUsers)

// Serve static files
app.Use(middleware.Static("./dist"))

// SPA fallback
app.NotFound(func(c *marten.Ctx) error {
    if strings.HasPrefix(c.Path(), "/api/") {
        return c.NotFound("endpoint not found")
    }
    // Serve index.html for client‑side routing
    return c.HTML(200, indexHTML)
})

Tests

Marten v0.1.3 ships with 325 tests covering:

  • Unit tests for every component
  • Integration tests for real‑world workflows
  • Stress tests with 1,000+ concurrent requests
  • Edge cases and error conditions

All tests pass with Go’s race detector; no known memory leaks.

What Marten Doesn’t Provide

  • ORM integration – use database/sql directly
  • Template engine – use html/template from the stdlib
  • Validation library – write your own or import a third‑party package
  • WebSocket support – planned for a future release

Philosophy

If the standard library can do it, use the standard library. If you need more, add it yourself—still keeping the zero‑dependency constraint.

Future Releases

  • WebSocket middleware
  • Template rendering helpers
  • Session management middleware
  • Enhanced static file serving options

All with the same zero‑dependency philosophy.

Installation

go get github.com/gomarten/marten@v0.1.3

Check out the examples for CRUD APIs, JWT auth, file servers, and more.

Conclusion

Marten isn’t trying to replace Gin or Echo; it’s an experiment in minimalism—a proof that you can build a capable web framework without pulling in the world. Sometimes, less is more.

  • GitHub:
  • Documentation:
  • Discussions:
Back to Blog

Related posts

Read more »

Go 1.26 Interactive Tour

Article URL: https://antonz.org/go-1-26/ Comments URL: https://news.ycombinator.com/item?id=46686972 Points: 17 Comments: 0...

[인터뷰] 토스인슈어런스는 왜 디지털 온보딩 시스템을 개발했나

법인보험대리점GA 토스인슈어런스는 신규 설계사가 업무를 시작하기 위한 초기 준비 절차온보딩를 전면 전산화한 ‘디지털 온보딩 시스템’을 구축했다. 복잡하게 흩어져 있던 필수 과정을 하나의 흐름으로 통합해 설계사의 정착 속도와 사용자 경험을 동시에 개선했다는 평가다. 토스인슈어런스의 디지털...