Not sure how to structure your Go web application?
My new book guides you through the start-to-finish build of a real world web application in Go — covering topics like how to structure your code, manage dependencies, create dynamic database-driven pages, and how to authenticate and authorize users securely.
One of my favorite things about the recent Go 1.16 release is a small — but very welcome — addition to the flag package: the flag.Func() function. This makes it much easier to define and use custom command-line flags in your application.
For example, if you want to parse a flag like --urls="http://example.com http://example.org" directly into a []*url.URL slice, then previously you had two options. You could either create a custom type to implement the flag.Value interface, or leverage a third-party package like pflag.
But now the flag.Func() function gives you a simple and lightweight alternative. In this short post we're going to take a look at a few examples of how you can use it in your own code.
Parsing custom flag types
To demonstrate how this works, let's start with the example I gave above and create an application which accepts a list of URLs, converts each one to a url.URL type, and then prints them out. Similar to this:
To make this work, we'll need to do two things:
Split the values in the --urls flag on whitespace to get the individual URLs. The strings.Fields() function is a good fit for this task.
Convert the individual URL string values to a url.URL type. We can do this using the url.Parse() function.
We can use those together with flag.Func() like so:
If you try to run this application, you should find that the flags are parsed and work just like you would expect. For example:
Whereas if you provide an invalid flag value that triggers an error in the flag.Func() function, Go will automatically display the corresponding error message and exit. For example:
Default flag values
It's really important to point out that if a flag isn't provided, the corresponding flag.Func() function will not be called at all. This means that you cannot set a default value inside a flag.Func() function, so trying to do something like this won't work:
Instead you need to set the default value for a flag beforeflag.Func() is called. For example:
Validating flag values
The flag.Func() function also opens up some new opportunities for validating input data from command-line flags. For example, let's say that your application has an --environment flag and you want to restrict the possible values to development, staging or production.
To do that, you can implement a flag.Func() function similar to this:
Making reusable helpers
If you find yourself repeating the same code in your flag.Func() functions, or the logic is getting too complex, it's possible to break it out into a reusable helper. For example, we could rewrite the example above to process our --environment flag via a generic enumFlag() function, like so:
If you enjoyed this article, you might like to check out my recommended tutorials list or check out my books Let's Go and Let's Go Further, which teach you everything you need to know about how to build professional production-ready web applications and APIs with Go.