How do their APIs compare?
How do their APIs compare?
About fatedier/frp · 2 messages · last activity 9/3/2026
These two projects have fundamentally different purposes and shapes of "API," so a direct comparison is really about how each exposes its functionality to Go programs.
frp: configuration-driven, service-oriented API
frp is not really designed as a library-first API — it's primarily a standalone reverse-proxy binary (frpc/frps) that is configured rather than scripted. Its Go-level "API" surface, as seen in the codebase, centers on:
- Config structs and
Complete()/validation methods — e.g.,ClientCommonConfig.Complete()[pkg/config/v1/client.go:L85-L101],ServerTransportConfig.Complete()[pkg/config/v1/server.go:L182-L198], andAuthClientConfig.Complete()[pkg/config/v1/client.go:L206-L210]. These fill in defaults and are the main way callers configure a client/server before starting it. - Config loading — functions like
LoadConfigure(exercised in [pkg/config/load_test.go:L694-L713]) andUnmarshalClientConfFromIni[pkg/config/legacy/client.go:L173-L203] parse YAML/TOML/JSON or legacy INI into these structs.RenderWithTemplate[pkg/config/load.go:L84-L99] supports templated config files. - Validation helpers —
GetClientConfigRequirements[pkg/config/v1/validation/client.go:L73-L98] inspects a completed config to determine runtime requirements (e.g., whether a virtual network stack is needed), andvalidateIncludeFiles[pkg/config/v1/validation/client.go:L177-L191] checks include-file references. - Message dispatch API — internally,
pkg/msgprovides aDispatcherwithRun()[pkg/msg/handler.go:L127-L131] andAsyncHandler[pkg/msg/handler.go:L102-L108] for handling the frp wire protocol between client and server, plus a registry-stylemsgCtlfor message types [pkg/msg/ctl.go:L27-L33].
So frp's "API" is oriented around struct-based configuration objects with completion/validation lifecycles, then handing them to internal service/dispatcher machinery. It's not designed to be embedded as a general-purpose library for arbitrary programs — it's the internals of the frpc/frps binaries.
mvdan/sh: library-first parsing/interpretation API
mvdan/sh, by contrast, is explicitly designed as a Go library for shell script tooling, and its public API is built around composable, functional-options-style packages:
syntaxpackage — provides a parser (syntax.NewParser, referenced conceptually via generated token/parsing code like [syntax/gen_token_parse.go:L117-L134]) to turn shell source into an AST.interppackage — providesinterp.New/Runnerfor executing parsed scripts, with pluggable behavior via options likeinterp.ExecHandlers, exercised extensively in tests such as [interp/handler_test.go:L238-L277] and the built-inhelpcommand handling inrunHelp[interp/help.go:L142-L215].patternpackage — exposes glob/pattern-to-regexp translation (Modeflags likeFilenames,EntireString,Shortest,NoGlobCase), as shown extensively in [pattern/pattern_test.go:L1-L120] and implemented in [pattern/pattern.go:L325-L364].fileutilpackage — small utilities likeShebang()for detecting shell interpreters from a script's shebang line [fileutil/file_test.go:L11-L68].
Key contrast
- Design intent: frp's API is internal/config-driven — you build and complete config structs, then the binary's service layer consumes them; it's not meant for embedding arbitrary logic. mvdan/sh's API is explicitly a general-purpose library (parser + interpreter + pattern engine) meant to be imported and extended by other Go programs (e.g., shellcheck-like tools, custom shells).
- Extensibility model: frp extends behavior through config fields and completion defaults (
Complete()methods) and message-type registration (msgCtl). mvdan/sh extends behavior through functional options (interp.ExecHandlers(...)) and **bitmaskModefl
Want to ask your own question?
Open chat for fatedier/frpPublic Q&A. Generated by RepoPilot from the actual source of fatedier/frp. AI answers can be incomplete or stale — verify before relying on them.