Frameworks
How to mount archview, and how routes are detected per framework.
archview serves its UI on its own path (/graph). How you mount it depends on
your framework; how it detects your routes is handled by per-framework
extractors that run automatically (each fires only on a package that uses
its framework).
Mounting archview
av.Handler() is a standard net/http.Handler, so it drops into any router.
archview also excludes its own routes from the graph, so /graph never shows up
as an endpoint.
mux := http.NewServeMux()
av.Mount(mux) // serves /graph and /graph/datar.GET("/graph", gin.WrapH(av.Handler()))
r.GET("/graph/data", gin.WrapH(av.Handler()))e.GET("/graph", echo.WrapHandler(av.Handler()))
e.GET("/graph/data", echo.WrapHandler(av.Handler()))import "github.com/gofiber/fiber/v2/middleware/adaptor"
app.Get("/graph", adaptor.HTTPHandler(av.Handler()))
app.Get("/graph/data", adaptor.HTTPHandler(av.Handler()))// chi, gorilla/mux, or any net/http-compatible router
r.Handle("/graph", av.Handler())
r.Handle("/graph/data", av.Handler())// works with any framework — run archview on its own port
go func() {
m := http.NewServeMux()
av.Mount(m)
http.ListenAndServe(":9000", m)
}()Any HTTP router (gin, echo, fiber, chi, httprouter, …)
Most Go routers share one shape — router.GET/Get/POST/...("/path", handler).
The generic router extractor matches it by the verb method name, a string path,
and a function-typed handler argument — not by the router's concrete type —
so most frameworks work with no configuration. Group("/api") prefixes are
joined onto routes.
api := app.Group("/api") // gin r.Group, echo e.Group, fiber app.Group
api.Get("/users", h.List) // -> endpoint /api/usersnet/http
Detects mux.HandleFunc(pattern, handler) on *http.ServeMux and the
package-level http.HandleFunc. The Go 1.22+ method-prefixed pattern is parsed:
mux.HandleFunc("GET /catalog/items", h.ListItems) // method GET, path /catalog/itemsgorilla/mux
gorilla uses r.HandleFunc("/path", handler) with the method on a chained
.Methods("GET") rather than r.GET(...), so it has its own extractor. Without
.Methods the route is ANY.
r := mux.NewRouter()
r.HandleFunc("/users", h.List).Methods("GET") // -> GET /users
r.HandleFunc("/ws", h.Stream) // -> WS /ws (see below)gRPC
Detected structurally from the generated registration call — no import needed,
so it works with real google.golang.org/grpc as-is. Each RPC method on the
service interface becomes an endpoint bound to the implementing method:
pb.RegisterOrderServiceServer(grpcServer, &OrderServer{...})
// endpoints: /OrderService/CreateOrder, /OrderService/GetOrder, ...GraphQL
Detects gqlgen-style resolvers: an interface named
Query/Mutation/SubscriptionResolver implemented by a project type. Each
field becomes an endpoint bound to the resolver method.
type QueryResolver interface {
Order(ctx context.Context, id string) (*Order, error) // endpoint /Query/order
}Structural detection — no import required. Only root resolvers for now; nested field resolvers are on the roadmap.
ConnectRPC
Detects the generated New<Svc>Handler(impl) constructor (returns
(string, http.Handler)) from connectrpc.com/connect. Each RPC method on the
service handler interface becomes an endpoint — so a Connect service shows up
even though it doesn't use grpc-go's Register…Server.
path, handler := orderv1connect.NewOrderServiceHandler(srv)
// endpoints: /OrderService/CreateOrder, ...WebSocket
A route handler that performs a WebSocket upgrade — gorilla/websocket Upgrade,
coder/nhooyr Accept — is labeled WS instead of its HTTP method. The
connection entry is shown; per-message routing after the upgrade is dynamic
(runtime dispatch) and isn't traced.
r.HandleFunc("/ws", h.Stream) // h.Stream calls upgrader.Upgrade -> WS /wsAdding a framework
Implement the Extractor interface and pass it via Options.Extractors:
type Extractor interface {
Name() string
Match(pkg *packages.Package) bool // does this pkg use the framework?
Extract(pkg *packages.Package) []Route // walk syntax, return routes
}av, _ := archview.New(archview.Options{
Extractors: append(route.Default(), myExtractor{}),
})