Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

The STDLIB adapter

The stdlib adapter wires jetwarp’s portable adapter.Adapter API to the Go standard library router, net/http’s ServeMux.

If you want the smallest dependency footprint and the most “plain net/http” experience, this is the best place to start.


Install

Add the adapter module to your project:

go get codeberg.org/iaconlabs/jetwarp/adapter/stdlib@latest

This pulls in:

  • codeberg.org/iaconlabs/jetwarp (core adapter + registry)
  • codeberg.org/iaconlabs/jetwarp/drivers/v1/stdlib (the stdlib driver)

No third-party router dependencies are required.


Create a router

package main

import (
	"log"
	"net/http"

	twstdlib "codeberg.org/iaconlabs/jetwarp/adapter/stdlib"
)

func main() {
	r := twstdlib.New()

	r.HandleFunc(http.MethodGet, "/healthz", func(w http.ResponseWriter, _ *http.Request) {
		_, _ = w.Write([]byte("ok"))
	})

	// Always check accumulated registration errors before serving traffic.
	if err := r.Err(); err != nil {
		log.Fatal(err)
	}

	log.Fatal(http.ListenAndServe(":8080", r))
}

Route patterns and params

The stdlib adapter is the “native home” of jetwarp’s canonical pattern model.

Parameters ({name}) and PathValue

Jetwarp patterns use ServeMux-style {name} parameters:

r.HandleFunc(http.MethodGet, "/users/{id}", func(w http.ResponseWriter, r *http.Request) {
	id := r.PathValue("id")
	_, _ = w.Write([]byte("id=" + id))
})

Because this is standard library routing, PathValue works without any driver bridging.


MethodAny ("*") support

Jetwarp supports method-agnostic routes via the canonical method token "*".

On stdlib, this maps naturally to how Go 1.22+ ServeMux patterns work:

  • method-specific registration uses a mux pattern like: "GET /path"
  • MethodAny registration uses the raw path: "/path"
r.HandleFunc("*", "/any", func(w http.ResponseWriter, r *http.Request) {
	_, _ = w.Write([]byte("ANY:" + r.Method))
})

Method-specific beats ANY for the same path. If you register both:

  • GET /any
  • * /any

a GET /any request must hit the GET handler (independent of registration order).


Scoping (Group) on stdlib

The stdlib driver supports scoping by returning derived drivers that share the same underlying ServeMux and apply a prefix internally.

From application code, you’ll normally use Group:

v1 := r.Group("/api").Group("/v1")
v1.HandleFunc(http.MethodGet, "/students", listStudents)

The router will join prefixes using jetwarp’s path-join rules (no accidental double slashes).


Important gotchas

1) No in-segment suffix/prefix params

The stdlib driver does not claim support for in-segment suffix/prefix patterns (CapParamSuffix).

That means patterns like these are not portable on stdlib and should be rejected during registration:

  • /files/{id}.json
  • /prefix-{id}

If you need this feature, use an adapter whose driver advertises param-suffix support.

Portable alternative: use a plain segment param and validate/parse in the handler:

  • /files/{id} then parse/validate
  • /files/{id}.json (not supported on stdlib)

2) Duplicate registrations can trigger ServeMux panics (but jetwarp turns them into errors)

http.ServeMux may panic when registering invalid or conflicting patterns (including duplicates).

The stdlib driver protects you: it converts those panics into returned errors and keeps the driver usable. Still, it’s a good reason to treat Err() as a required setup check.

3) Trailing slashes are normalized away

Jetwarp normalizes patterns by trimming trailing slashes for non-root paths.

This is intentional (it avoids ambiguous “/x vs /x/” behavior), but it also means you should not rely on ServeMux’s legacy “subtree” semantics that depend on patterns ending with /.

If you need a file-server-style subtree mount, consider:

  • mounting it on a separate parent http.ServeMux, or
  • using a router/driver that supports your desired prefix matching model explicitly.

Accessing the underlying engine (escape hatch)

If you really need direct access to the underlying mux for debugging or integration, the stdlib driver’s engine is a *http.ServeMux.

Be careful: registering routes directly on the mux bypasses jetwarp’s registry and tooling (including OpenAPI).

type engineProvider interface {
	Engine() any
}

if ep, ok := r.(engineProvider); ok {
	if mux, ok := ep.Engine().(*http.ServeMux); ok {
		_ = mux
	}
}

See also