Occasionally, I'm in the mood to write a small utility: maybe a webhook handler to automate something, or a web page to display some information. These are mostly all applications that work over HTTP(S), so, after 2 or 3 of these, I usually start thinking about factoring the common parts into a library, and for that I've bounced between using a library style and a framework style.
Framework would be like my previous iteration go.seankhliao.com/svcrunner, where you wire up everything into the framework and hand it control to run everything. This is quite concise, with minimal boilderplate in application, but at times I found it limiting when I wanted to do something I didn't previously plan for, like hook into a different part of the application lifecycle.
Example:
1func main() {
2 hs := &http.Server{}
3 svr := server.New(hs)
4 svcrunner.Options{}.Run(
5 svcrunner.NewHTTP(hs, svr.Register, svr.Init),
6 )
7}
What I currently do with v2 at
go.seankhliao.com/svcrunner/v2/tshttp
might at first flance look similar,
still with New
and Run
functions,
but it leaves it up to the application to wire things up.
This is much more boilerplate at each callsite,
but crucially doesn't block me from doing anything.
(Though I did pull in a different framework like package in
github.com/google/subcommands...)
Example:
1
2func (c *Cmd) Execute(ctx context.Context, f *flag.FlagSet, args ...any) subcommands.ExitStatus {
3 err := New(ctx, c).Run(ctx)
4 if err != nil {
5 return subcommands.ExitFailure
6 }
7 return subcommands.ExitSuccess
8}
9
10func New(ctx context.Context, c *Cmd) *Server {
11 svr := tshttp.New(ctx, c.tshttpConf)
12 s := &Server{
13 o: svr.O,
14 svr: svr,
15
16 render: webstyle.NewRenderer(webstyle.TemplateCompact),
17 dir: c.dir,
18 }
19
20 svr.Mux.Handle("/eur", otelhttp.NewHandler(s.hView("eur"), "hView - eur"))
21 svr.Mux.Handle("/gbp", otelhttp.NewHandler(s.hView("gbp"), "hView - gbp"))
22 svr.Mux.Handle("/twd", otelhttp.NewHandler(s.hView("twd"), "hView - twd"))
23 svr.Mux.Handle("/", otelhttp.NewHandler(http.HandlerFunc(s.hIndex), "hIndex"))
24 svr.Mux.HandleFunc("/-/ready", func(rw http.ResponseWriter, r *http.Request) { rw.Write([]byte("ok")) })
25 return s
26}
27
28func (s *Server) Run(ctx context.Context) error {
29 return s.svr.Run(ctx)
30}