<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Daniel G. Taylor</title>
    <description>The latest articles on DEV Community by Daniel G. Taylor (@danielgtaylor).</description>
    <link>https://dev.to/danielgtaylor</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F401273%2F9469209a-7972-4614-8192-eeabd28c5908.jpeg</url>
      <title>DEV Community: Daniel G. Taylor</title>
      <link>https://dev.to/danielgtaylor</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/danielgtaylor"/>
    <language>en</language>
    <item>
      <title>Reducing Go Dependencies</title>
      <dc:creator>Daniel G. Taylor</dc:creator>
      <pubDate>Wed, 07 Feb 2024 17:20:26 +0000</pubDate>
      <link>https://dev.to/danielgtaylor/reducing-go-dependencies-dec</link>
      <guid>https://dev.to/danielgtaylor/reducing-go-dependencies-dec</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;A case study of dependency reduction in &lt;a href="https://github.com/danielgtaylor/huma" rel="noopener noreferrer"&gt;Huma&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;This article is a practical look at reducing dependencies in Go libraries. We'll start by looking at how Go dependencies work, then go into a few ideas around reducing dependencies. Finally, we'll go into a few ways I've implemented these ideas in Huma and the results. Hopefully you can use some of the same techniques in your own projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Reduce Dependencies
&lt;/h2&gt;

&lt;p&gt;Why would you want to reduce the number of dependencies in your Go library? There are a few reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reduced Complexity&lt;/strong&gt;: Fewer dependencies means less overall code to understand and maintain.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reduced Risk&lt;/strong&gt;: Fewer dependencies means fewer potential security vulnerabilities.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reduced Build Times&lt;/strong&gt;: Fewer dependencies can mean faster builds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reduced Binary Size&lt;/strong&gt;: Fewer dependencies can mean smaller binaries.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A bit more subjective, perhaps, is that reducing dependencies can make your library more approachable to potential users. If they see that your library has fewer dependencies, they might be more likely to use it.&lt;/p&gt;

&lt;p&gt;It can also mean the difference between using your library and not using it for certain constrained organizations, like governments, NGOs, or large corporations with strict security policies around third party code.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Go Dependencies Work
&lt;/h2&gt;

&lt;p&gt;Go dependencies are managed using the &lt;code&gt;go.mod&lt;/code&gt; file. This file lists the dependencies of your project, and is used by the &lt;code&gt;go&lt;/code&gt; tool to download and build your dependencies. When a user of your package runs &lt;code&gt;go get&lt;/code&gt;, the &lt;code&gt;go&lt;/code&gt; tool will download your package and some or all of its dependencies.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp5tvc7ticzkuubgij79z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp5tvc7ticzkuubgij79z.png" alt="Go module dependency graph"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As of Go 1.17+, the &lt;code&gt;go&lt;/code&gt; tool does what is called &lt;a href="https://go.dev/ref/mod#graph-pruning" rel="noopener noreferrer"&gt;dependency graph pruning&lt;/a&gt;, meaning it will only add and download the dependencies that are actually used by your project. This means that if you use a module that has a dependency that is not used by your project, it will not be downloaded by the &lt;code&gt;go&lt;/code&gt; tool.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/danielgtaylor/huma" rel="noopener noreferrer"&gt;Huma&lt;/a&gt; is a great example of this, as it uses an adapter concept to support many different router packages. The Huma &lt;code&gt;go.mod&lt;/code&gt; contains a dependency for each router, but because each one is in its own package within the Huma module, only the one that is actually used by the project will be downloaded.&lt;/p&gt;

&lt;p&gt;Another great example is the popular &lt;a href="https://github.com/stretchr/testify" rel="noopener noreferrer"&gt;stretchr/testify&lt;/a&gt; module, which provides a package for test assertions. Many, many libraries use it for testing, but consumers of the library do not need to download that package because they are not using it in their own code.&lt;/p&gt;

&lt;p&gt;This is important to remember: not every dependency you see in a library's &lt;code&gt;go.mod&lt;/code&gt; will wind up in your project's &lt;code&gt;go.mod&lt;/code&gt; because of this pruning!&lt;/p&gt;

&lt;h2&gt;
  
  
  Ideas Around Reducing Dependencies
&lt;/h2&gt;

&lt;p&gt;Here are a few general ideas around reducing dependencies in Go libraries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use The Standard Library&lt;/strong&gt;: Use the Go standard library as much as possible. It's well-tested, well-documented, and has a lot of functionality built in. Sometimes a dependency is just a convenience wrapper around the standard library and isn't strictly necessary.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Build It Yourself&lt;/strong&gt;: Sometimes it's easier to build a small piece of functionality yourself than to bring in a dependency. This can be especially true for small, well-defined pieces of functionality.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;It's Okay To Copy&lt;/strong&gt;: If you only need a small part of a library, consider copying the code into your own project. This can be a good idea if the library is large or has a lot of dependencies.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Reframe The Problem&lt;/strong&gt;: Sometimes you can reframe the problem you're trying to solve to avoid a dependency. For example, if you're using a library to marshal to a specific format, you might be able to use a simpler format or a different approach to avoid the dependency.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Move Examples&lt;/strong&gt;: It's common and helpful to include examples or demos in your codebase to make it easy for new users to adopt and experiment. However, these examples can sometimes bring in a lot of dependencies. Consider moving them to a separate package or directory to avoid adding unnecessary dependencies to your main package.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  A Side Note On Versioning
&lt;/h3&gt;

&lt;p&gt;Keep in mind that as you work to reduce dependencies you have two options related to versioning (assuming you use &lt;a href="https://semver.org/" rel="noopener noreferrer"&gt;semantic versioning&lt;/a&gt;):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Try your best to remain backward-compatible. You cannot break existing users of the library. This is the most desirable outcome and should result in a new minor version.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Make a new major version with breaking changes. This will require all users to change their own code, but it may be worth the benefits.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A third option might be to deprecate, announce, and later remove (or move) features without a new major version. It's not strictly semantic versioning then, but more like library evolution and may or may not work for your users.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Case Study: Huma
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/danielgtaylor/huma" rel="noopener noreferrer"&gt;Huma&lt;/a&gt; is a micro web framework for Go that is designed to be easy to use and provide OpenAPI &amp;amp; JSON Schema support on top of existing routers. It has a number of adapters for different routers, including &lt;code&gt;http.ServeMux&lt;/code&gt;, &lt;code&gt;chi&lt;/code&gt;, &lt;code&gt;fiber&lt;/code&gt;, &lt;code&gt;gorilla/mux&lt;/code&gt;, &lt;code&gt;gin&lt;/code&gt;, etc.&lt;/p&gt;

&lt;p&gt;When originally designing Huma v2.0.0 significant time was spent on the library's interface, usability, and feature set. I think that was the right call, and various dependencies allowed me to move faster and try various approaches easily. However, at release Huma had a lot of dependencies (both direct and indirect), and I wanted to reduce them in part because of feedback from the community and in part because I wanted to make the library more approachable to potential users.&lt;/p&gt;

&lt;p&gt;Pull request &lt;a href="https://github.com/danielgtaylor/huma/pull/223" rel="noopener noreferrer"&gt;#223&lt;/a&gt; significanly reduces the dependencies between Huma versions v2.3.0 and v2.4.0. Read on for a detailed analysis.&lt;/p&gt;

&lt;h3&gt;
  
  
  Environment Variables
&lt;/h3&gt;

&lt;p&gt;Huma depended on &lt;a href="https://github.com/spf13/viper" rel="noopener noreferrer"&gt;spf13/viper&lt;/a&gt; as a way to bind environment variables to command-line options so that a service can be configured either via env vars or flags on the command line. This was a convenience feature, but it was also a significant dependency. I realized that I was pulling in a dozen dependencies just for this, when instead I could write a small amount of code using the standard library to accomplish essentially the same thing. Whereas before I had code similar to this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;

&lt;span class="n"&gt;cfg&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;viper&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetEnvPrefix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SERVICE"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetEnvKeyReplacer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewReplacer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"_"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AutomaticEnv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c"&gt;// ... later on ...&lt;/span&gt;
&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetDefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;defaultValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BindPFlag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Instead, I could write a small amount of code like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;

&lt;span class="n"&gt;envName&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;"SERVICE_"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;casing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Snake&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ToUpper&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;defaultValue&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tag&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"default"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;envName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Env vars will override the default value, which is used to document&lt;/span&gt;
    &lt;span class="c"&gt;// what the value is if no options are passed.&lt;/span&gt;
    &lt;span class="n"&gt;defaultValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now, instead of pulling in a dozen dependencies, I was only using the standard library. Command-line options would either have the default set from the service code &lt;em&gt;or&lt;/em&gt; be overridden by the environment variable, causing the help text to be accurate for that particular run.&lt;/p&gt;

&lt;p&gt;This worked great as I really didn't need the full power of &lt;code&gt;viper&lt;/code&gt; and was able to replace it with a small amount of code. You should consider whether you really need the full power of a library you are about to pull into your project.&lt;/p&gt;

&lt;h3&gt;
  
  
  YAML Generation
&lt;/h3&gt;

&lt;p&gt;Huma supports generating OpenAPI 3.1 from your service, and it's common for OpenAPI to use both JSON and YAML for the spec document. It's also common for people to use extensions to the OpenAPI spec, meaning they put their own fields into the resulting JSON/YAML document. This is also the &lt;strong&gt;only&lt;/strong&gt; part of my code that needs to use YAML, and it only needs to be marshaled (i.e. no parsing).&lt;/p&gt;

&lt;p&gt;I was originally using &lt;a href="https://github.com/goccy/go-yaml" rel="noopener noreferrer"&gt;goccy/go-yaml&lt;/a&gt; as it has built-in support for both &lt;code&gt;,inline&lt;/code&gt; to merge in extension fields, as well as a JSON formatter during serialization (which works because JSON is a subset of YAML). This worked great to support both JSON and YAML using one library, but it was a significant dependency with its own validation and parsing code, colorized output support using additional dependencies, special error dependencies, etc.&lt;/p&gt;

&lt;p&gt;Rethinking and reframing this problem, I realized that I could use the standard library to generate JSON, and then use a small amount of code to merge in the extension fields using custom &lt;code&gt;MarshalJSON() ([]byte, error)&lt;/code&gt; methods on my OpenAPI and JSON Schema structs (the &lt;a href="https://github.com/danielgtaylor/huma/blob/main/openapi.go#L55-L78" rel="noopener noreferrer"&gt;&lt;code&gt;marshalJSON&lt;/code&gt; utility&lt;/a&gt; is omitted for brevity):&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;MarshalJSON&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;marshalJSON&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="n"&gt;jsonFieldInfo&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;omitNever&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;omitEmpty&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"termsOfService"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TermsOfService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;omitEmpty&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"contact"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Contact&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;omitEmpty&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"license"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;License&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;omitEmpty&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;omitNever&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Extensions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Once I had the JSON, it's actually not that difficult to convert it to YAML. I began to write a small library to do so, but found &lt;a href="https://github.com/itchyny/json2yaml" rel="noopener noreferrer"&gt;someone else had already done it&lt;/a&gt; in just a few lines and was able to copy it into my codebase.&lt;/p&gt;

&lt;p&gt;This removed a significant dependency and replaced it with mostly standard library code. I kept the &lt;code&gt;,inline&lt;/code&gt; field tags so if someone using Huma wanted to augment or serialize using a proper YAML library everything will still just work as expected.&lt;/p&gt;

&lt;h3&gt;
  
  
  JSON Schema Validation
&lt;/h3&gt;

&lt;p&gt;Huma has its own JSON Schema request validator built-in, but for some validations it depended on third-party libraries, particularly for validating the &lt;code&gt;format&lt;/code&gt; of strings.&lt;/p&gt;

&lt;p&gt;For example, it used &lt;a href="https://github.com/google/uuid" rel="noopener noreferrer"&gt;google/uuid&lt;/a&gt; to validate that a UUID is in the correct format. But, that library does a lot more than simple validation. It also has parsing, string generation, and other features that I didn't need. It's a mature library. I was able just copy a small amount of code from it to validate UUIDs in my own codebase.&lt;/p&gt;

&lt;h3&gt;
  
  
  Text Manipulation
&lt;/h3&gt;

&lt;p&gt;As part of the OpenAPI generation, Huma uses a registry of schemas which are created from your Go operation handler input/output structs. These schemas are named using the struct name and support unicode, generics, and scalar types too.&lt;/p&gt;

&lt;p&gt;For consistency the code which generates these names converts the input to &lt;code&gt;PascalCase&lt;/code&gt;, which is common for public (exported) structs in Go code but not used everywhere, and generics using scalars result in type names like &lt;code&gt;MyGeneric[int]&lt;/code&gt; which we want converted to &lt;code&gt;MyGenericInt&lt;/code&gt;. This means you need to title case the parts of the name after removing any generics brackets.&lt;/p&gt;

&lt;p&gt;Unfortunately &lt;a href="https://pkg.go.dev/strings#Title" rel="noopener noreferrer"&gt;&lt;code&gt;strings.Title&lt;/code&gt;&lt;/a&gt; is deprecated, and the suggestion is to use &lt;code&gt;golang.org/x/text/cases&lt;/code&gt; instead. That brings in over 40mb of source as a dependency due to all the unicode handling and other features it provides! This makes it hard to justify using it for just a single line of code, slows down compilation, potentially increases the binary size, and makes it impossible for users to share runnable snippets on the Go Playground as the builds will timeout.&lt;/p&gt;

&lt;p&gt;Instead, I was able to write a tiny amount of code to handle this case and avoid the dependency entirely. It's not as featureful as the &lt;code&gt;golang.org/x/text/cases&lt;/code&gt; package, but it's good enough for my use case and avoids a significant dependency while still supporting unicode and generics:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;

&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;part&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FieldsFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="kt"&gt;rune&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Split on special characters. Note that `,` is used when there are&lt;/span&gt;
    &lt;span class="c"&gt;// multiple inputs to a generic type.&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sc"&gt;'['&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sc"&gt;']'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sc"&gt;'*'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sc"&gt;','&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Split fully qualified names like `github.com/foo/bar.Baz` into `Baz`.&lt;/span&gt;
    &lt;span class="n"&gt;fqn&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;part&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;fqn&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fqn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c"&gt;// Add to result, and uppercase for better scalar support (`int` -&amp;gt; `Int`).&lt;/span&gt;
    &lt;span class="c"&gt;// Use unicode-aware uppercase to support non-ASCII characters.&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;utf8&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DecodeRuneInString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ToUpper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;It continues to pass a pretty extensive suite of test cases, and I'm happy with the result.&lt;/p&gt;

&lt;h3&gt;
  
  
  Code Examples
&lt;/h3&gt;

&lt;p&gt;Huma has a "transformer" concept, which enables you to write a piece of code which modifies the response of an HTTP request &lt;strong&gt;before&lt;/strong&gt; it is marshaled to e.g. JSON. It works with Go instances, and is a powerful feature used to e.g. insert &lt;code&gt;$schema&lt;/code&gt; JSON Schema references which enabled code completion and linting as you type in editors like VSCode.&lt;/p&gt;

&lt;p&gt;Since it's not a concept found in other routers or web frameworks, I wanted to provide more examples of what might be possible with it. My mistake was including them in the main package, causing additional dependencies like &lt;a href="https://github.com/danielgtaylor/shorthand" rel="noopener noreferrer"&gt;danielgtaylor/shorthand&lt;/a&gt; and &lt;a href="https://github.com/danielgtaylor/mexpr" rel="noopener noreferrer"&gt;danielgtaylor/mexpr&lt;/a&gt; to get pulled in.&lt;/p&gt;

&lt;p&gt;Moving such examples into an &lt;code&gt;examples&lt;/code&gt; directory with its own &lt;code&gt;go.mod&lt;/code&gt; makes things much cleaner and avoids pulling in unnecessary dependencies. It also makes it easier for users to understand the core library and what is just an example or toy to show it off.&lt;/p&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;p&gt;The results speak for themselves. Using the &lt;a href="https://huma.rocks/tutorial/your-first-api/" rel="noopener noreferrer"&gt;Huma tutorial code&lt;/a&gt; as a benchmark, and replacing the Chi router with Go 1.22's improved &lt;code&gt;http.ServeMux&lt;/code&gt; router to truly show the required dependencies of Huma's core library, we can see the following results from Huma v2.3.0 (before the changes):&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;

&lt;span class="n"&gt;module&lt;/span&gt; &lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;demo&lt;/span&gt;

&lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="m"&gt;1.22&lt;/span&gt;

&lt;span class="n"&gt;require&lt;/span&gt; &lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;danielgtaylor&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;huma&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;v2&lt;/span&gt; &lt;span class="n"&gt;v2&lt;/span&gt;&lt;span class="m"&gt;.3.0&lt;/span&gt;

&lt;span class="n"&gt;require&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;danielgtaylor&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;casing&lt;/span&gt; &lt;span class="n"&gt;v0&lt;/span&gt;&lt;span class="m"&gt;.0.0&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;20210126043903&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;4e55&lt;/span&gt;&lt;span class="n"&gt;e6373ac3&lt;/span&gt; &lt;span class="c"&gt;// indirect&lt;/span&gt;
    &lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;danielgtaylor&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;mexpr&lt;/span&gt; &lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="m"&gt;.8.0&lt;/span&gt; &lt;span class="c"&gt;// indirect&lt;/span&gt;
    &lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;danielgtaylor&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;shorthand&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;v2&lt;/span&gt; &lt;span class="n"&gt;v2&lt;/span&gt;&lt;span class="m"&gt;.1.1&lt;/span&gt; &lt;span class="c"&gt;// indirect&lt;/span&gt;
    &lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;fatih&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="m"&gt;.15.0&lt;/span&gt; &lt;span class="c"&gt;// indirect&lt;/span&gt;
    &lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;fsnotify&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;fsnotify&lt;/span&gt; &lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="m"&gt;.6.0&lt;/span&gt; &lt;span class="c"&gt;// indirect&lt;/span&gt;
    &lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;fxamacker&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;cbor&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;v2&lt;/span&gt; &lt;span class="n"&gt;v2&lt;/span&gt;&lt;span class="m"&gt;.5.0&lt;/span&gt; &lt;span class="c"&gt;// indirect&lt;/span&gt;
    &lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;goccy&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;yaml&lt;/span&gt; &lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="m"&gt;.11.0&lt;/span&gt; &lt;span class="c"&gt;// indirect&lt;/span&gt;
    &lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;google&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt; &lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="m"&gt;.3.1&lt;/span&gt; &lt;span class="c"&gt;// indirect&lt;/span&gt;
    &lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;hashicorp&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;hcl&lt;/span&gt; &lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="m"&gt;.0.0&lt;/span&gt; &lt;span class="c"&gt;// indirect&lt;/span&gt;
    &lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;inconshreveable&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;mousetrap&lt;/span&gt; &lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="m"&gt;.1.0&lt;/span&gt; &lt;span class="c"&gt;// indirect&lt;/span&gt;
    &lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;magiconair&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;properties&lt;/span&gt; &lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="m"&gt;.8.7&lt;/span&gt; &lt;span class="c"&gt;// indirect&lt;/span&gt;
    &lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;mattn&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;colorable&lt;/span&gt; &lt;span class="n"&gt;v0&lt;/span&gt;&lt;span class="m"&gt;.1.13&lt;/span&gt; &lt;span class="c"&gt;// indirect&lt;/span&gt;
    &lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;mattn&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;isatty&lt;/span&gt; &lt;span class="n"&gt;v0&lt;/span&gt;&lt;span class="m"&gt;.0.20&lt;/span&gt; &lt;span class="c"&gt;// indirect&lt;/span&gt;
    &lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;mitchellh&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;mapstructure&lt;/span&gt; &lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="m"&gt;.5.0&lt;/span&gt; &lt;span class="c"&gt;// indirect&lt;/span&gt;
    &lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;pelletier&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;toml&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;v2&lt;/span&gt; &lt;span class="n"&gt;v2&lt;/span&gt;&lt;span class="m"&gt;.0.8&lt;/span&gt; &lt;span class="c"&gt;// indirect&lt;/span&gt;
    &lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;spf13&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;afero&lt;/span&gt; &lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="m"&gt;.9.5&lt;/span&gt; &lt;span class="c"&gt;// indirect&lt;/span&gt;
    &lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;spf13&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;cast&lt;/span&gt; &lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="m"&gt;.5.1&lt;/span&gt; &lt;span class="c"&gt;// indirect&lt;/span&gt;
    &lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;spf13&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;cobra&lt;/span&gt; &lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="m"&gt;.8.0&lt;/span&gt; &lt;span class="c"&gt;// indirect&lt;/span&gt;
    &lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;spf13&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;jwalterweatherman&lt;/span&gt; &lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="m"&gt;.1.0&lt;/span&gt; &lt;span class="c"&gt;// indirect&lt;/span&gt;
    &lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;spf13&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;pflag&lt;/span&gt; &lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="m"&gt;.0.5&lt;/span&gt; &lt;span class="c"&gt;// indirect&lt;/span&gt;
    &lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;spf13&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;viper&lt;/span&gt; &lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="m"&gt;.15.0&lt;/span&gt; &lt;span class="c"&gt;// indirect&lt;/span&gt;
    &lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;subosito&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;gotenv&lt;/span&gt; &lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="m"&gt;.4.2&lt;/span&gt; &lt;span class="c"&gt;// indirect&lt;/span&gt;
    &lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;x448&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;float16&lt;/span&gt; &lt;span class="n"&gt;v0&lt;/span&gt;&lt;span class="m"&gt;.8.4&lt;/span&gt; &lt;span class="c"&gt;// indirect&lt;/span&gt;
    &lt;span class="n"&gt;golang&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;exp&lt;/span&gt; &lt;span class="n"&gt;v0&lt;/span&gt;&lt;span class="m"&gt;.0.0&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;20230515195305&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;f3d0a9c9a5cc&lt;/span&gt; &lt;span class="c"&gt;// indirect&lt;/span&gt;
    &lt;span class="n"&gt;golang&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;net&lt;/span&gt; &lt;span class="n"&gt;v0&lt;/span&gt;&lt;span class="m"&gt;.19.0&lt;/span&gt; &lt;span class="c"&gt;// indirect&lt;/span&gt;
    &lt;span class="n"&gt;golang&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt; &lt;span class="n"&gt;v0&lt;/span&gt;&lt;span class="m"&gt;.15.0&lt;/span&gt; &lt;span class="c"&gt;// indirect&lt;/span&gt;
    &lt;span class="n"&gt;golang&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="n"&gt;v0&lt;/span&gt;&lt;span class="m"&gt;.14.0&lt;/span&gt; &lt;span class="c"&gt;// indirect&lt;/span&gt;
    &lt;span class="n"&gt;golang&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;xerrors&lt;/span&gt; &lt;span class="n"&gt;v0&lt;/span&gt;&lt;span class="m"&gt;.0.0&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;20220907171357&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;04&lt;/span&gt;&lt;span class="n"&gt;be3eba64a2&lt;/span&gt; &lt;span class="c"&gt;// indirect&lt;/span&gt;
    &lt;span class="n"&gt;gopkg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;in&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;ini&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;v1&lt;/span&gt; &lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="m"&gt;.67.0&lt;/span&gt; &lt;span class="c"&gt;// indirect&lt;/span&gt;
    &lt;span class="n"&gt;gopkg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;in&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;yaml&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;v3&lt;/span&gt; &lt;span class="n"&gt;v3&lt;/span&gt;&lt;span class="m"&gt;.0.1&lt;/span&gt; &lt;span class="c"&gt;// indirect&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;After the changes above from PR &lt;a href="https://github.com/danielgtaylor/huma/pull/223" rel="noopener noreferrer"&gt;#223&lt;/a&gt; which are released in Huma v2.4.0, the dependencies are significantly reduced:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;

&lt;span class="n"&gt;module&lt;/span&gt; &lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;demo&lt;/span&gt;

&lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="m"&gt;1.22&lt;/span&gt;

&lt;span class="n"&gt;require&lt;/span&gt; &lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;danielgtaylor&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;huma&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;v2&lt;/span&gt; &lt;span class="n"&gt;v2&lt;/span&gt;&lt;span class="m"&gt;.3.0&lt;/span&gt;

&lt;span class="n"&gt;require&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;danielgtaylor&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;casing&lt;/span&gt; &lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="m"&gt;.0.0&lt;/span&gt; &lt;span class="c"&gt;// indirect&lt;/span&gt;
    &lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;fxamacker&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;cbor&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;v2&lt;/span&gt; &lt;span class="n"&gt;v2&lt;/span&gt;&lt;span class="m"&gt;.5.0&lt;/span&gt; &lt;span class="c"&gt;// indirect&lt;/span&gt;
    &lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;inconshreveable&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;mousetrap&lt;/span&gt; &lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="m"&gt;.1.0&lt;/span&gt; &lt;span class="c"&gt;// indirect&lt;/span&gt;
    &lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;spf13&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;cobra&lt;/span&gt; &lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="m"&gt;.8.0&lt;/span&gt; &lt;span class="c"&gt;// indirect&lt;/span&gt;
    &lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;spf13&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;pflag&lt;/span&gt; &lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="m"&gt;.0.5&lt;/span&gt; &lt;span class="c"&gt;// indirect&lt;/span&gt;
    &lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;x448&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;float16&lt;/span&gt; &lt;span class="n"&gt;v0&lt;/span&gt;&lt;span class="m"&gt;.8.4&lt;/span&gt; &lt;span class="c"&gt;// indirect&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;It's now so small and fast that it's easy to show off various features in the Go Playground, for example: &lt;a href="https://go.dev/play/p/Qmv5VwNTg-L" rel="noopener noreferrer"&gt;https://go.dev/play/p/Qmv5VwNTg-L&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There could be further improvement, but not without breaking changes. For example, the optional CLI functionality could be moved to its own package and that would get rid of the &lt;code&gt;spf13/cobra&lt;/code&gt;, &lt;code&gt;spf13/pflag&lt;/code&gt;, and &lt;code&gt;inconshreveable/mousetrap&lt;/code&gt; dependencies if not using that functionality. In practice, every service I've seen does use it so the utility of doing so is limited. Maybe this can be deprecated and removed over time, but for now I'm extremely happy with the results.&lt;/p&gt;

&lt;p&gt;Aside from the smaller &lt;code&gt;go.mod&lt;/code&gt;, build times were reduced by 12% and compiled binary size was reduced by 20% just from having to parse/compile/link less code.&lt;/p&gt;

&lt;p&gt;Hopefully this dive into reducing dependencies in Huma has given you some ideas for your own projects. It's a good exercise to go through your dependencies and see if you really need them all, and if not, to consider whether you can replace them with a small amount of code or a different approach.&lt;/p&gt;

</description>
      <category>go</category>
      <category>programming</category>
      <category>opensource</category>
      <category>openapi</category>
    </item>
    <item>
      <title>APIs in Go with Huma 2.0</title>
      <dc:creator>Daniel G. Taylor</dc:creator>
      <pubDate>Wed, 06 Dec 2023 17:13:53 +0000</pubDate>
      <link>https://dev.to/danielgtaylor/apis-in-go-with-huma-20-2eb4</link>
      <guid>https://dev.to/danielgtaylor/apis-in-go-with-huma-20-2eb4</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;A modern, simple, fast &amp;amp; flexible micro framework for building HTTP REST/RPC APIs in Go backed by OpenAPI 3 and JSON Schema.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  History
&lt;/h2&gt;

&lt;p&gt;Back in 2016 I was working for a small-ish company doing live video streaming services which would eventually become part of Warner Bros Discovery through a complex chain of acquisitions and a "spin-merge." We were working on pulling apart a monolithic live video transcoding application meant to run on individual physical server racks into a set of web services run and dynamically scaled in the cloud in an effort to improve reliability, resiliency, and to realize potential cost savings.&lt;/p&gt;

&lt;p&gt;We started to dabble in using &lt;a href="https://go.dev/" rel="noopener noreferrer"&gt;Go&lt;/a&gt;, and I dove deep into the way the language works and how to write idiomatic Go code using the tools it provides. My confidence with Go quickly improved, and I started to help others to pick it up as well.&lt;/p&gt;

&lt;p&gt;I wound up on a different team with pre-existing Python code so temporarily shelved my use of Go for a bit, and we used &lt;a href="https://sanic.dev/en/" rel="noopener noreferrer"&gt;Sanic&lt;/a&gt; (an async Python framework built on top of the excellent &lt;a href="https://github.com/MagicStack/uvloop" rel="noopener noreferrer"&gt;uvloop&lt;/a&gt; &amp;amp; &lt;a href="https://libuv.org/" rel="noopener noreferrer"&gt;libuv&lt;/a&gt; that also powers Node.js) to build some APIs for live channel management &amp;amp; operations. We hand-wrote our OpenAPI and used it to generate documentation and a CLI, which was an improvement over what was there (or not) before. Other teams used the OpenAPI document to generate SDKs to interact with our service.&lt;/p&gt;

&lt;p&gt;Unfortunately design-first was hard for us, as it is for most teams. The &lt;a href="https://spec.openapis.org/oas/v3.1.0" rel="noopener noreferrer"&gt;OpenAPI&lt;/a&gt; description document quickly got out of sync with the actual service, and clients ran into problems with missing or incorrect features in the CLI/SDK. Design first is great in concept, but without the robust framework of enforced API governance along with excellent scenario test coverage utilizing the OpenAPI document it's extremely difficult for teams to be successful. There is a high barrier of entry, especially for what should be a quick &amp;amp; simple API.&lt;/p&gt;

&lt;p&gt;When it came time to evaluate replacing the now aging service, I prototyped a new version using the rapidly growing &lt;a href="https://fastapi.tiangolo.com/" rel="noopener noreferrer"&gt;FastAPI&lt;/a&gt; framework, which automatically generates OpenAPI from the code for you, eliminating the possibility of the code &amp;amp; OpenAPI being out of sync, with the downstream effect of the docs, CLI, and SDKs also staying in sync properly. You can also still use a design-first approach, it just involves some structures written in code to be reviewed first. Design vs. code first is a &lt;a href="https://sookocheff.com/post/api/the-false-dichotomoy-of-design-first-and-code-first-api-development/" rel="noopener noreferrer"&gt;false dichotomy by which we needn't be constrained&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Separately in my personal time I had been continuing my use of Go on &lt;a href="https://rest.sh/" rel="noopener noreferrer"&gt;Restish&lt;/a&gt;, a generic CLI for web APIs. I then evaluated the Go landscape for something like FastAPI in Go, but came away disappointed. I wondered if I could make something similar, and started to prototype what this might look like. With some excellent mentorship and a &lt;strong&gt;lot&lt;/strong&gt; of rewrites, I wound up with &lt;a href="https://huma.rocks/" rel="noopener noreferrer"&gt;Huma&lt;/a&gt; as a mostly idiomatic version of something like FastAPI in Golang.&lt;/p&gt;

&lt;p&gt;It was then announced at work that &lt;em&gt;all&lt;/em&gt; new services would be written in Go. Having had experience with it before, I was excited to get started. I evaluated the landscape of packages again and pitched using Huma to the team, which was an easy sell after the benefits we saw in the FastAPI prototype and the pain we had felt for years hand-writing a separate OpenAPI document.&lt;/p&gt;

&lt;p&gt;And so, Huma was born and rapidly got production usage handling the configuration &amp;amp; live operations of thousands and thousands of live channels. Third party contributions started to come in and several organizations began to use it for production systems, resulting in a stable v1.0.0 followed by many fixes and additions. Here's an unsolicited quote from Reddit user &lt;a href="https://www.reddit.com/r/golang/comments/zhitcg/comment/izmg6vk/?utm_source=reddit&amp;amp;utm_medium=web2x&amp;amp;context=3" rel="noopener noreferrer"&gt;Jeb_Jenky&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;[Huma] is by far my favorite web framework for Go. It is inspired by FastAPI, which is also amazing, and conforms to many RFCs for common web things...&lt;br&gt;
Anyway I really like the feature set, the fact that it uses Chi, and the fact that it is still somehow relatively simple to use. I've tried other frameworks and they do not spark joy for me.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The value of a software package's ability to spark joy cannot be understated!&lt;/p&gt;

&lt;h2&gt;
  
  
  Huma 2.0
&lt;/h2&gt;

&lt;p&gt;Since that initial release I have gotten a ton of great feedback, and &lt;a href="https://tip.golang.org/doc/go1.18#generics" rel="noopener noreferrer"&gt;Go 1.18/1.20&lt;/a&gt; added new interesting features to the language which could help prevent some common mistakes via strongly-typed compile-time checks.&lt;/p&gt;

&lt;p&gt;I created a Huma &lt;code&gt;v2&lt;/code&gt; branch to rewrite it for Go 1.20+ improving on almost every aspect of the framework, and the &lt;a href="https://github.com/danielgtaylor/huma/releases/tag/v2.0.0" rel="noopener noreferrer"&gt;2.0.0 release is available now&lt;/a&gt; alongside a brand new &lt;a href="https://huma.rocks/" rel="noopener noreferrer"&gt;Huma Documentation&lt;/a&gt; site.&lt;/p&gt;

&lt;h3&gt;
  
  
  Features
&lt;/h3&gt;

&lt;p&gt;What makes Huma special and why should you use it? How does 2.0.0 differ from the 1.x.x versions?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Standards compliant: built-in support for OpenAPI 3.1, JSON Schema 2020-12, JSON, CBOR, RFC 7807 errors, client-driven content negotiation, conditional requests, PATCH support, &lt;code&gt;describedby&lt;/code&gt; link relations &amp;amp; more.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Up to date: docs &amp;amp; generated tooling are always up to date with the server interface, no matter how many teams work on it or how old the project gets.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Router agnostic: use whatever router you want. Integrate it into existing projects easily for incremental adoption.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Strongly typed: request inputs and response outputs are strongly typed and just use standard Go structs. Generics ensure compile-time checks for fast feedback.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Idiomatic Go: handlers use &lt;code&gt;context.Context&lt;/code&gt;, your input/output structs, and &lt;code&gt;error&lt;/code&gt;. There is less &lt;code&gt;interface{}&lt;/code&gt; or &lt;code&gt;any&lt;/code&gt; magic.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Errors: Huma uses standardized (RFC 7807), exhaustive errors so users can know what is wrong right away and fix it, rather than making request after request with a different error response each time.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Extensible: all of the OpenAPI is editable, including extensions. You have direct access to the router for serving static content, HTML rendering, websockets, or other features without needing to open pull requests. It's easy to wrap or replace built-in functionality, like error models. It's easy to add custom validation rules, too.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fast: reflection is pre-computed and cached at startup; validation is zero or low-allocation and blazing fast, and buffer pools are shared to amortize allocation cost whenever possible. Go is also &lt;a href="https://huma.rocks/why/benchmarks/" rel="noopener noreferrer"&gt;significantly faster&lt;/a&gt; than Node.js or Python.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Well tested: Huma is used in production services to run large successful live linear and live event channels. Code coverage is 93%, and some optional pieces like the shorthand PATCH syntax are additionally fuzz tested for potentially problematic inputs.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;See &lt;a href="https://huma.rocks/features/" rel="noopener noreferrer"&gt;https://huma.rocks/features/&lt;/a&gt; for more information and a deep dive on how the various features work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example
&lt;/h2&gt;

&lt;p&gt;Here is a basic "Hello, world!" style example that implements a single API endpoint &lt;code&gt;/greeting/{name}&lt;/code&gt; and replies with a JSON object like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Hello, {name}!"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;{name}&lt;/code&gt; is replaced with whatever value is sent to the API. This example is taken directly from the &lt;a href="https://huma.rocks/tutorial/installation/" rel="noopener noreferrer"&gt;Huma Tutorial&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"context"&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"net/http"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/danielgtaylor/huma/v2"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/danielgtaylor/huma/v2/adapters/humachi"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/go-chi/chi/v5"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// GreetingInput represents the greeting operation request.&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;GreetingInput&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`path:"name" maxLength:"30" example:"world" doc:"Name to greet"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// GreetingOutput represents the greeting operation response.&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;GreetingOutput&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Body&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Message&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`json:"message" example:"Hello, world!" doc:"Greeting message"`&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Create a new router &amp;amp; API&lt;/span&gt;
    &lt;span class="n"&gt;router&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;chi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewMux&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;api&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;humachi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;huma&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DefaultConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"My API"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"1.0.0"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="c"&gt;// Register GET /greeting/{name}&lt;/span&gt;
    &lt;span class="n"&gt;huma&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;huma&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Operation&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;OperationID&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"get-greeting"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Summary&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;     &lt;span class="s"&gt;"Get a greeting"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Method&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;      &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MethodGet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;        &lt;span class="s"&gt;"/greeting/{name}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;GreetingInput&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;GreetingOutput&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;GreetingOutput&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
        &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello, %s!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="c"&gt;// Start the server!&lt;/span&gt;
    &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListenAndServe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"127.0.0.1:8888"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now you can test it out, e.g. with &lt;a href="https://rest.sh/" rel="noopener noreferrer"&gt;Restish&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# In one tab, run the service:&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;go run main.go

&lt;span class="c"&gt;# In another tab, make a request&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;restish :8888/greeting/world
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can even see generated interactive documentation powered by Huma's OpenAPI support at &lt;a href="http://localhost:8888/docs" rel="noopener noreferrer"&gt;http://localhost:8888/docs&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fhuma.rocks%2Ftutorial%2Fapidocs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fhuma.rocks%2Ftutorial%2Fapidocs.png" alt="Huma generated documentation"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With just a few lines of code you now have a fully functional API with an OpenAPI document and generated documentation!&lt;/p&gt;

&lt;h2&gt;
  
  
  Q &amp;amp; A
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;What about DBs and other features?&lt;/p&gt;

&lt;p&gt;Huma is deliberately a lightweight micro-framework. It is a building block to build on top of, and will never be something like Django. Others can definitely take it and make something like Django for Go, it's just not what this projects is about.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;What about gRPC?&lt;/p&gt;

&lt;p&gt;gRPC is an HTTP/2-based protocol built around binary protobuf messages. It has excellent tooling for Go and uses code generation from a description format similar to OpenAPI. I use it at work alongside Huma services, and I've written &lt;a href="https://github.com/danielgtaylor/python-betterproto" rel="noopener noreferrer"&gt;one of the more popular Python protobuf libraries&lt;/a&gt;, so you could say I know a little bit about it. Overall in concept it's quite similar to OpenAPI and can be a great choice for your organization or team.&lt;/p&gt;

&lt;p&gt;I tend to prefer OpenAPI for interfacing with third-party customers, as they are most likely already familiar and comfortable with HTTP and/or REST services (and probably use e.g. AWS, Google Cloud, Azure, etc). There are good HTTP gateways for gRPC, but they have quirks and don't always map well to REST concepts if that's something you care about. Additionally protobuf has no built-in validation, though there are third-party libraries for it.&lt;/p&gt;

&lt;p&gt;We actually expose some of our gRPC services through Huma rather than another gateway using the &lt;a href="https://github.com/istreamlabs/protoc-gen-huma" rel="noopener noreferrer"&gt;protoc-gen-huma&lt;/a&gt; plugin. You'll have to evaluate what works best for your team and product.&lt;/p&gt;

&lt;p&gt;It's worth noting that Huma also supports HTTP/2 and the CBOR binary format out of the box, so you get some of the same benefits (connection multiplexing, smaller wire size, faster encoding) of using gRPC. It's also possible to use &lt;a href="https://github.com/danielgtaylor/huma/tree/main/examples/protodemo" rel="noopener noreferrer"&gt;protobuf &lt;em&gt;with&lt;/em&gt; Huma&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Further Reading
&lt;/h2&gt;

&lt;p&gt;Use these links to learn more and start using Huma for your next project!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://huma.rocks/" rel="noopener noreferrer"&gt;Huma Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://huma.rocks/tutorial/installation/" rel="noopener noreferrer"&gt;Huma Tutorial&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pkg.go.dev/github.com/danielgtaylor/huma/v2" rel="noopener noreferrer"&gt;Go reference docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/danielgtaylor/huma" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://rest.sh/" rel="noopener noreferrer"&gt;Restish CLI&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>go</category>
      <category>openapi</category>
      <category>rest</category>
      <category>jsonschema</category>
    </item>
    <item>
      <title>Mapping OpenAPI to the CLI</title>
      <dc:creator>Daniel G. Taylor</dc:creator>
      <pubDate>Fri, 15 Apr 2022 20:02:45 +0000</pubDate>
      <link>https://dev.to/danielgtaylor/mapping-openapi-to-the-cli-37pb</link>
      <guid>https://dev.to/danielgtaylor/mapping-openapi-to-the-cli-37pb</guid>
      <description>&lt;p&gt;In this post we'll explore &lt;a href="https://rest.sh/" rel="noopener noreferrer"&gt;Restish&lt;/a&gt;, a CLI for APIs with built-in OpenAPI support. How does it go from an OpenAPI service description to CLI commands &amp;amp; arguments? Read on to find out!&lt;/p&gt;

&lt;h2&gt;
  
  
  Autodiscovery
&lt;/h2&gt;

&lt;p&gt;Restish supports OpenAPI autodiscovery using several different mechanisms. You can provide an &lt;a href="https://tools.ietf.org/html/rfc8631" rel="noopener noreferrer"&gt;RFC 8631&lt;/a&gt; &lt;code&gt;service-desc&lt;/code&gt; link relation, an &lt;a href="https://tools.ietf.org/html/rfc5988#section-6.2.2" rel="noopener noreferrer"&gt;RFC 5988&lt;/a&gt; &lt;code&gt;describedby&lt;/code&gt; link relation, or provide an &lt;code&gt;/openapi.yaml&lt;/code&gt; or &lt;code&gt;/openapi.json&lt;/code&gt; with your API.&lt;/p&gt;

&lt;h3&gt;
  
  
  Link Relation Header
&lt;/h3&gt;

&lt;p&gt;When using the &lt;code&gt;service-desc&lt;/code&gt; or &lt;code&gt;describedby&lt;/code&gt; link relation, Restish follows the link to find the OpenAPI. For example, if you get &lt;code&gt;https://api.example.com/&lt;/code&gt; it might return the following header:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Link: &amp;lt;/path/to/openapi.yaml&amp;gt;; rel="service-desc"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restish would then fetch &lt;code&gt;https://api.example.com/path/to/openapi.yaml&lt;/code&gt; and load that into operations, arguments, flags, etc.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fallback Mechanism
&lt;/h3&gt;

&lt;p&gt;If not link relation header is present, a fallback mechanism is used. Restish looks for &lt;code&gt;https://api.example.com/openapi.yaml&lt;/code&gt; or &lt;code&gt;https://api.example.com/openapi.json&lt;/code&gt;. If found, it will load it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Anatomy of a CLI Operation
&lt;/h2&gt;

&lt;p&gt;A CLI operation can consist of several parts:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1p3lv7a57cfuav0wu7hl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1p3lv7a57cfuav0wu7hl.png" alt="CLI command anatomy: restish, API, operation, optional flags, arguments, optional body"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;API&lt;/td&gt;
&lt;td&gt;Short-name of the API, configured when registering the API with Restish (can be anything you want).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Operation&lt;/td&gt;
&lt;td&gt;OpenAPI Operation ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Options&lt;/td&gt;
&lt;td&gt;Optional operation query or header parameter(s)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Arguments&lt;/td&gt;
&lt;td&gt;Required operation path parameter(s)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Request Body&lt;/td&gt;
&lt;td&gt;Optional request body, which can be passed in via stdin or via &lt;a href="https://rest.sh/#/shorthand" rel="noopener noreferrer"&gt;CLI Shorthand&lt;/a&gt; in the command.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Aside from those, there is also the act of generating &lt;code&gt;--help&lt;/code&gt; output: markdown descriptions and output JSON Schemas which need to be handled.&lt;/p&gt;

&lt;p&gt;This is how those would map into an OpenAPI 3 YAML:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzyr0gpj3l3q9ftgx489x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzyr0gpj3l3q9ftgx489x.png" alt="OpenAPI 3 YAML example"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Github API Example
&lt;/h2&gt;

&lt;p&gt;Let's take a look at a real-world example from the &lt;a href="https://github.com/github/rest-api-description" rel="noopener noreferrer"&gt;Github V3 OpenAPI&lt;/a&gt;. Here is a truncated version of the code search operation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/search/code"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Search code&lt;/span&gt;
    &lt;span class="na"&gt;operationId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;search/code&lt;/span&gt;
    &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;q&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;The query contains one or more search keywords...&lt;/span&gt;
      &lt;span class="na"&gt;in&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;query&lt;/span&gt;
      &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sort&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Sorts the results of your query...&lt;/span&gt;
      &lt;span class="na"&gt;in&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;query&lt;/span&gt;
      &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
      &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
        &lt;span class="na"&gt;enum&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;indexed&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$ref"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;#/components/parameters/order"&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$ref"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;#/components/parameters/per-page"&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$ref"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;#/components/parameters/page"&lt;/span&gt;
    &lt;span class="na"&gt;responses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;200'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Response&lt;/span&gt;
        &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;application/json&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;object&lt;/span&gt;
              &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;total_count&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;incomplete_results&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;items&lt;/span&gt;
              &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;total_count&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;integer&lt;/span&gt;
                &lt;span class="na"&gt;incomplete_results&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;boolean&lt;/span&gt;
                &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;array&lt;/span&gt;
                  &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$ref"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;#/components/schemas/code-search-result-item"&lt;/span&gt;
            &lt;span class="na"&gt;examples&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$ref"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;#/components/examples/code-search-result-item-paginated"&lt;/span&gt;
    &lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;304'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
        &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$ref"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;#/components/responses/not_modified"&lt;/span&gt;
      &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;503'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
        &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$ref"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;#/components/responses/service_unavailable"&lt;/span&gt;
      &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;422'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
        &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$ref"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;#/components/responses/validation_failed"&lt;/span&gt;
      &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;403'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
        &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$ref"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;#/components/responses/forbidden"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This translates into the following command help in Restish showing you how to use it. Note the operation ID &lt;code&gt;search-code&lt;/code&gt; and the parameters like &lt;code&gt;--sort&lt;/code&gt; and &lt;code&gt;--per-page&lt;/code&gt; from the &lt;code&gt;$ref&lt;/code&gt;s above. The response is also used to generate a terminal-friendly representation of the response schema so users know what to expect as output.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;restish github search-code &lt;span class="nt"&gt;--help&lt;/span&gt;
Description truncated &lt;span class="k"&gt;for &lt;/span&gt;example...

&lt;span class="c"&gt;## Response 200 (application/json)&lt;/span&gt;

&lt;span class="sb"&gt;`&lt;/span&gt;schema
&lt;span class="o"&gt;{&lt;/span&gt;
  incomplete_results: &lt;span class="o"&gt;(&lt;/span&gt;boolean&lt;span class="o"&gt;)&lt;/span&gt;
  items: &lt;span class="o"&gt;[&lt;/span&gt;
    &lt;span class="o"&gt;{&lt;/span&gt;
      git_url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
      html_url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
      name: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
      path: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
      repository: &lt;span class="o"&gt;{&lt;/span&gt;
        archive_url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
        assignees_url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
        blobs_url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
        branches_url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
        collaborators_url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
        comments_url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
        commits_url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
        compare_url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
        contents_url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
        contributors_url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
        description: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
        downloads_url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
        events_url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
        fork: &lt;span class="o"&gt;(&lt;/span&gt;boolean&lt;span class="o"&gt;)&lt;/span&gt;
        forks_url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
        full_name: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
        git_commits_url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
        git_refs_url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
        git_tags_url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
        hooks_url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
        html_url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;id&lt;/span&gt;: &lt;span class="o"&gt;(&lt;/span&gt;number&lt;span class="o"&gt;)&lt;/span&gt;
        issue_comment_url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
        issue_events_url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
        issues_url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
        keys_url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
        labels_url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
        languages_url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
        merges_url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
        milestones_url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
        name: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
        node_id: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
        notifications_url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
        owner: &lt;span class="o"&gt;{&lt;/span&gt;
          avatar_url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
          events_url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
          followers_url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
          following_url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
          gists_url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
          gravatar_id: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
          html_url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
          &lt;span class="nb"&gt;id&lt;/span&gt;: &lt;span class="o"&gt;(&lt;/span&gt;number&lt;span class="o"&gt;)&lt;/span&gt;
          login: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
          node_id: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
          organizations_url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
          received_events_url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
          repos_url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
          site_admin: &lt;span class="o"&gt;(&lt;/span&gt;boolean&lt;span class="o"&gt;)&lt;/span&gt;
          starred_url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
          subscriptions_url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
          &lt;span class="nb"&gt;type&lt;/span&gt;: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
          url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        private: &lt;span class="o"&gt;(&lt;/span&gt;boolean&lt;span class="o"&gt;)&lt;/span&gt;
        pulls_url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
        stargazers_url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
        statuses_url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
        subscribers_url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
        subscription_url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
        tags_url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
        teams_url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
        trees_url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
        url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;
      score: &lt;span class="o"&gt;(&lt;/span&gt;number&lt;span class="o"&gt;)&lt;/span&gt;
      sha: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
      url: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;]&lt;/span&gt;
  total_count: &lt;span class="o"&gt;(&lt;/span&gt;number&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="sb"&gt;`&lt;/span&gt;

Usage:
  restish github search-code &lt;span class="o"&gt;[&lt;/span&gt;flags]

Flags:
      &lt;span class="nt"&gt;--accept&lt;/span&gt; application/vnd.github.v3+json   Setting to...
  &lt;span class="nt"&gt;-h&lt;/span&gt;, &lt;span class="nt"&gt;--help&lt;/span&gt;                                    &lt;span class="nb"&gt;help &lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;search-code
      &lt;span class="nt"&gt;--order&lt;/span&gt; desc                              Determines whether the first...
      &lt;span class="nt"&gt;--page&lt;/span&gt; int                                Page number of the results to fetch. &lt;span class="o"&gt;(&lt;/span&gt;default 1&lt;span class="o"&gt;)&lt;/span&gt;
      &lt;span class="nt"&gt;--per-page&lt;/span&gt; int                            Results per page &lt;span class="o"&gt;(&lt;/span&gt;max 100&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;default 30&lt;span class="o"&gt;)&lt;/span&gt;
      &lt;span class="nt"&gt;--q&lt;/span&gt; string                                The query contains one or more search...
      &lt;span class="nt"&gt;--sort&lt;/span&gt; indexed                            Sorts the results of your query...

Global Flags:
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note also that &lt;em&gt;all&lt;/em&gt; parameters use double slashes (&lt;code&gt;--&lt;/code&gt;), since single slashes are reserved for Restish use. Conversely, all Restish parameters are prefixed with &lt;code&gt;--rsh-&lt;/code&gt; in order to prevent collisions.&lt;/p&gt;

&lt;p&gt;For operations with required arguments and/or bodies, Restish is able to generate usage and example documentation as well, including example CLI Shorthand input. For example, when creating a new repo fork:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;## Request Schema (application/json)&lt;/span&gt;

&lt;span class="sb"&gt;`&lt;/span&gt;schema
&lt;span class="o"&gt;{&lt;/span&gt;
  organization: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt; Optional parameter to specify the organization name &lt;span class="k"&gt;if &lt;/span&gt;forking into an organization.
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="sb"&gt;`&lt;/span&gt;

...

Usage:
  restish github repos-create-fork owner repo &lt;span class="o"&gt;[&lt;/span&gt;flags]

Examples:
  restish repos-create-fork owner repo organization: string
  restish repos-create-fork owner repo &amp;lt;input.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Overrides
&lt;/h2&gt;

&lt;p&gt;Sometimes you might want the CLI operation name or parameter name to be different from what the official name is in the API, or hide a particular deprecated parameter, or even tell Restish how to automatically configure auth. These are all possible with &lt;a href="https://rest.sh/#/openapi?id=openapi-extensions" rel="noopener noreferrer"&gt;Restish OpenAPI Extensions&lt;/a&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;x-cli-aliases&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sets up command aliases for operations.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;x-cli-config&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Automatic CLI configuration settings.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;x-cli-description&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Provide an alternate description for the CLI.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;x-cli-ignore&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ignore this path, operation, or parameter.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;x-cli-hidden&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Hide this path, or operation.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;x-cli-name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Provide an alternate name for the CLI.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For example, in the search operation above, the query parameter is named &lt;code&gt;q&lt;/code&gt; and would show up in Restish as &lt;code&gt;--q&lt;/code&gt; which is not very friendly. You might rename it via &lt;code&gt;x-cli-name: query&lt;/code&gt; so that people can use &lt;code&gt;--query&lt;/code&gt; instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Hopefully this has shed some light on how Restish is able to dynamically generate CLI commands from OpenAPI specifications, and how you can expect those commands to operate if you are already familiar with the backend API.&lt;/p&gt;

</description>
      <category>cli</category>
      <category>openapi</category>
      <category>rest</category>
      <category>http2</category>
    </item>
    <item>
      <title>A CLI for REST APIs</title>
      <dc:creator>Daniel G. Taylor</dc:creator>
      <pubDate>Wed, 03 Jun 2020 17:58:27 +0000</pubDate>
      <link>https://dev.to/danielgtaylor/a-cli-for-rest-apis-part-1-104b</link>
      <guid>https://dev.to/danielgtaylor/a-cli-for-rest-apis-part-1-104b</guid>
      <description>&lt;p&gt;&lt;em&gt;Standardizing how power users and scripts interact with your service&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Sorry about the acronym soup in the title! 😅&lt;/p&gt;

&lt;p&gt;Today I want to talk about an oft-neglected aspect of building REST or HTTP APIs. There are plenty of articles about API design, API description formats like OpenAPI, and lots of libraries and code generators used to talk to the API from code. What is missing in my opinion is a high-quality command line client that’s generic enough to be used for most HTTP APIs.&lt;/p&gt;

&lt;p&gt;But there’s &lt;code&gt;curl&lt;/code&gt;, you say. Many service docs even include examples specifically for calling the service’s API via &lt;code&gt;curl&lt;/code&gt; commands. I love &lt;code&gt;curl&lt;/code&gt; and HTTPie, but these are just generic HTTP clients. There’s not enough specialization for REST or HTTP APIs. HTTPie comes close since it speaks JSON, the lingua franca of web APIs, but falls short in other ways, such as describing complex inputs for API operations.&lt;/p&gt;

&lt;p&gt;What fancy-pants features would make me happy, you ask? My list would include at least the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Easy to install &amp;amp; fast startup&lt;/li&gt;
&lt;li&gt;Default to HTTP/2 (with TLS), which is now 5 years old! 😮&lt;/li&gt;
&lt;li&gt;Speaks JSON, YAML, and their binary relatives natively&lt;/li&gt;
&lt;li&gt;Caching of responses when appropriate headers are set&lt;/li&gt;
&lt;li&gt;Built-in common API authentication mechanisms like OAuth 2&lt;/li&gt;
&lt;li&gt;Automatically handle pagination of collections&lt;/li&gt;
&lt;li&gt;An easy way to input complex nested data&lt;/li&gt;
&lt;li&gt;Pretty colorized output with the ability to filter built-in&lt;/li&gt;
&lt;li&gt;An understanding of API specs like OpenAPI with auto-discovery&lt;/li&gt;
&lt;li&gt;Always having access to the latest API features &amp;amp; docs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I think that would be a good start to getting where we should be in 2020. With that in mind, I’d like to introduce my new CLI tool for REST &amp;amp; HTTP APIs called Restish (&lt;a href="https://rest.sh/" rel="noopener noreferrer"&gt;https://rest.sh/&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fv17eg9cu3b4oxbjq75mc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fv17eg9cu3b4oxbjq75mc.png" alt="Restish logo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Restish can do all of the above wish list, and more. Installing it is easy if you have Homebrew:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;brew tap danielgtaylor/restish &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; brew &lt;span class="nb"&gt;install &lt;/span&gt;restish


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Windows users will need to &lt;a href="https://github.com/danielgtaylor/restish/releases" rel="noopener noreferrer"&gt;download a release&lt;/a&gt;. At its most basic it can be used very similar to how you might use &lt;code&gt;curl&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;restish &lt;span class="nt"&gt;-H&lt;/span&gt; Authorization:abc123 api.example.com/items/1
HTTP/2.0 200 OK
Content-Encoding: br
Content-Type: application/json
Date: ...

&lt;span class="o"&gt;{&lt;/span&gt;
  name: &lt;span class="s2"&gt;"Item number one"&lt;/span&gt;
  cost: 12.34
  created: &lt;span class="s2"&gt;"2020-01-01T12:00:00Z"&lt;/span&gt;
  tags: &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"grocery"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The real magic happens when you register an API base URI with Restish through the interactive &lt;code&gt;configure&lt;/code&gt; sub-command:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;restish api configure example
? Base URI &lt;span class="o"&gt;[&lt;/span&gt;? &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="nb"&gt;help&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;: https://api.example.com
Setting up a &lt;span class="sb"&gt;`&lt;/span&gt;default&lt;span class="sb"&gt;`&lt;/span&gt; profile
? Select option &lt;span class="k"&gt;for &lt;/span&gt;profile &lt;span class="sb"&gt;`&lt;/span&gt;default&lt;span class="sb"&gt;`&lt;/span&gt; Add header
? Header name Authorization
? Header value &lt;span class="o"&gt;(&lt;/span&gt;optional&lt;span class="o"&gt;)&lt;/span&gt; abc123
? Select option Save and &lt;span class="nb"&gt;exit&lt;/span&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;restish get example/items/1
...

&lt;span class="nv"&gt;$ &lt;/span&gt;restish post example/items name: Another, cost: 2.50, tags[]: household
HTTP/2.0 201 Created
Location: /items/2


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If the API provides an OpenAPI spec via a known link relation header or some common path like /openapi.json then things get even more interesting because you can directly call described API operations rather than specific URIs:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;restish example create-item name: Another, cost: 2.50, tags[]: household
HTTP/2.0 201 Created
Location: /items/3


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The popular &lt;a href="https://fastapi.tiangolo.com/" rel="noopener noreferrer"&gt;FastAPI Python library&lt;/a&gt; provides exactly that, so any services written with it will just work out of the box with Restish. And because Restish loads the OpenAPI spec on the fly, whenever you push updates to your FastAPI service then your CLI users will already have the new features available.&lt;/p&gt;

&lt;p&gt;Even better, CLI users can query the API for information on structure, including any updates you push to the API. For example:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;restish example create-item &lt;span class="nt"&gt;--help&lt;/span&gt;
Create a new item &lt;span class="k"&gt;in &lt;/span&gt;the inventory

&lt;span class="c"&gt;# Request Schema (application/json)&lt;/span&gt;

&lt;span class="o"&gt;{&lt;/span&gt;
  name&lt;span class="k"&gt;*&lt;/span&gt;: &lt;span class="o"&gt;(&lt;/span&gt;string&lt;span class="o"&gt;)&lt;/span&gt; The item name
  cost&lt;span class="k"&gt;*&lt;/span&gt;: &lt;span class="o"&gt;(&lt;/span&gt;number min:0&lt;span class="o"&gt;)&lt;/span&gt; The cost of the item
  tags: &lt;span class="o"&gt;[&lt;/span&gt;
    &lt;span class="o"&gt;(&lt;/span&gt;string maxLen:12&lt;span class="o"&gt;)&lt;/span&gt; Tag name
  &lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Response 201&lt;/span&gt;

A new item has been created. The &lt;span class="sb"&gt;`&lt;/span&gt;Location&lt;span class="sb"&gt;`&lt;/span&gt; header links to the item.

Usage:
  restish example create-item ...

Example:
  restish example create-item name: string, cost: 1.0, tags[]: string
  restish example create-item &amp;lt;input.json
...


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This is win-win for everyone. Users get a high quality CLI tool with advanced features built-in to interact with your service. Your developers don’t have to waste time building and maintaining a custom CLI or getting users to upgrade to access the newest API features.&lt;/p&gt;

&lt;p&gt;You should consider adopting Restish for your APIs and adding the “&lt;a href="https://img.shields.io/badge/Works%20With-Restish-ff5f87" rel="noopener noreferrer"&gt;Works with Restish&lt;/a&gt;” badge to your documentation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fupuey70l61iq1xrqk6ml.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fupuey70l61iq1xrqk6ml.png" alt="Works with Restish"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is a ton more breadth and depth to Restish. This quick introduction is just the first of a multi-part series to go into the various features and use-cases with lots of examples, so stay tuned and thanks for reading!&lt;/p&gt;

</description>
      <category>rest</category>
      <category>http2</category>
      <category>cli</category>
      <category>openapi</category>
    </item>
  </channel>
</rss>
