github.com/thetreep/go-swagger@v0.0.0-20240223100711-35af64f14f01/docs/tutorial/custom-server.md (about)

     1  # Custom Server Tutorial
     2  
     3  In this tutorial we'll be building up a custom server.
     4  The core will be generated using a manually written and maintained OpenAPI 2.0 spec.
     5  The cli code will be a thin layer around that, and will simply setup the API and server,
     6  using the parsed configurations and our hand written handlers.
     7  
     8  <!--more-->
     9  
    10  The server we'll building will be very simple.
    11  In this tutorial we'll assume you are already familiar with defining an API,
    12  using the OpenAPI 2.0 yaml specification format.
    13  Please consult [the official OpenAPI 2.0 specification][OpenAPI2.0]
    14  for more information in case you're new to OpenAPI (Also known as Swagger).
    15  
    16  The end product of this tutorial can be found as `./examples/tutorials/custom-server`.
    17  
    18  The server we'll be building, will be generated using the following spec:
    19  
    20  ```yaml
    21  ---
    22  swagger: '2.0'
    23  info:
    24    version: 1.0.0
    25    title: Greeting Server
    26  paths:
    27    /hello:
    28      get:
    29        produces:
    30          - text/plain
    31        parameters:
    32          - name: name
    33            required: false
    34            type: string
    35            in: query
    36            description: defaults to World if not given
    37        operationId: getGreeting
    38        responses:
    39          200:
    40            description: returns a greeting
    41            schema:
    42                type: string
    43                description: contains the actual greeting as plain text
    44  ```
    45  
    46  As you can see, there is only 1 operation,
    47  allowing us to focus on how to create a custom server,
    48  without losing track in the details of any specific implementation.
    49  
    50  Where you store the specification is not important.
    51  By default the [swagger cli][go-swagger] expects it to be stored as `./swagger.yml`,
    52  but we'll store it as `./swagger/swagger.yml`, to keep our project's root folder clean and tidy.
    53  
    54  Once we have our OpenAPI specification ready, it is time to generate our server.
    55  This can be done using the following command, from within our root folder:
    56  
    57  ```
    58  $ swagger generate server -t gen -f ./swagger/swagger.yml --exclude-main -A greeter
    59  ```
    60  
    61  In the command above we're specifying the `-t` (target) flag,
    62  specifying that swagger should store all generated code in the given _target_ directory. We're also specifying the `-f` flag, to explicitly define that our spec file can be found at `./swagger/swagger.yml`, rather then the default `./swagger.yml`. As we are writing a custom server, we also don't want the automatically generated `cmd` server, and is excluded using the `--exclude-main` flag. Finally we're also explicitly naming our server using the `-A` flag.
    63  Please consult `swagger generate server --help` for more flags and information.
    64  
    65  Once we've executed this command, you should have following file tree:
    66  
    67  ```
    68  ├── gen
    69  │   └── restapi
    70  │       ├── configure_greeter.go
    71  │       ├── doc.go
    72  │       ├── embedded_spec.go
    73  │       ├── operations
    74  │       │   ├── get_greeting.go
    75  │       │   ├── get_greeting_parameters.go
    76  │       │   ├── get_greeting_responses.go
    77  │       │   ├── get_greeting_urlbuilder.go
    78  │       │   └── greeter_api.go
    79  │       └── server.go
    80  └── swagger
    81      └── swagger.yml
    82  ```
    83  
    84  After generation we should find only 1 sub directory in our `gen` folder.
    85  `restapi`, which contains the core server. It consists among other things out of the API (`operations/greeter_api.go`), operations (`operations/*`) and parameters (`*_parameters.go`).
    86  
    87  Note that in case we also had defined models in the global definitions section,
    88  there would be a `models` folder as well in our `gen` folder,
    89  containing the generated models for those definitions.
    90  But as we don't have any definitions to share, you won't find it in this tutorial.
    91  
    92  For more information, read through the generated code.
    93  It might help to keep the `swagger/swagger.yml` definition next to you,
    94  to help you realize what is defined, for what and where.
    95  
    96  So now that we have the generated server, it is time to write our actual main file.
    97  In it we'll parse some simple flags that can be used to configure the server,
    98  we'll setup the API and finally start the server. All in all, very minimal and simple.
    99  
   100  so let's start writing the `./cmd/greeter/main.go` file:
   101  
   102  We start by defining our flags, in our example using the standard `flag` pkg:
   103  
   104  ```go
   105  var portFlag = flag.Int("port", 3000, "Port to run this service on")
   106  ```
   107  
   108  Now it's time to write our main logic,
   109  starting by loading our embedded swagger spec.
   110  This is required, as it is used to configure the dynamic server,
   111  the core of our generated server (found under the `./gen/restapi` dir).
   112  
   113  ```go
   114  swaggerSpec, err := loads.Analyzed(restapi.SwaggerJSON, "")
   115  if err != nil {
   116  	log.Fatalln(err)
   117  }
   118  ```
   119  
   120  With the spec loaded in, we can create our API and server:
   121  
   122  ```go
   123  api := operations.NewGreeterAPI(swaggerSpec)
   124  server := restapi.NewServer(api)
   125  defer server.Shutdown()
   126  ```
   127  
   128  With the server created, we can overwrite the default port, using our `portFlag`:
   129  
   130  ```go
   131  flag.Parse()
   132  server.Port = *portFlag
   133  ```
   134  
   135  After that, we can serve our API and finish our main logic:
   136  
   137  ```go
   138  if err := server.Serve(); err != nil {
   139  	log.Fatalln(err)
   140  }
   141  ```
   142  
   143  Putting that all together, we have the following main function:
   144  
   145  ```go
   146  func main() {
   147    var portFlag = flag.Int("port", 3000, "Port to run this service on")
   148  
   149  	// load embedded swagger file
   150  	swaggerSpec, err := loads.Analyzed(restapi.SwaggerJSON, "")
   151  	if err != nil {
   152  		log.Fatalln(err)
   153  	}
   154  
   155  	// create new service API
   156  	api := operations.NewGreeterAPI(swaggerSpec)
   157  	server := restapi.NewServer(api)
   158  	defer server.Shutdown()
   159  
   160  	// parse flags
   161  	flag.Parse()
   162  	// set the port this service will be run on
   163  	server.Port = *portFlag
   164  
   165  	// TODO: Set Handle
   166  
   167  	// serve API
   168  	if err := server.Serve(); err != nil {
   169  		log.Fatalln(err)
   170  	}
   171  }
   172  ```
   173  
   174  Now that we have our server defined, let's give it a first spin!
   175  You can run it from our root directory using the following command:
   176  
   177  ```
   178  $ go run ./cmd/greeter/main.go --port 3000
   179  ```
   180  
   181  Let's now try to call our only defined operation,
   182  using the [httpie][] cli:
   183  
   184  ```
   185  $ http get :3000/hello
   186  ```
   187  
   188  Sadly this gives us the following output:
   189  
   190  ```http
   191  HTTP/1.1 501 Not Implemented
   192  Connection: close
   193  Content-Length: 50
   194  Content-Type: text/plain
   195  Date: Thu, 26 Jan 2017 13:09:52 GMT
   196  
   197  operation GetGreeting has not yet been implemented
   198  ```
   199  
   200  The good news is that our OpenAPI-based Golang service is working.
   201  The bad news is that we haven't implemented our handlers yet.
   202  We'll need one handler per operation, that does the actual logic.
   203  
   204  You might wonder why it does give us a sane response, rather then panicking.
   205  After grepping for that error message, or using a recursive search in your favorite editor, you'll find that this error originates from the greeter API constructor (`NewGreeterAPI`) found in `./gen/restapi/operations/greeter_api.go`.
   206  Here we'll see that all our consumers, producers and handlers have sane defaults.
   207  
   208  So now that we know that we just got our ass saved by go-swagger,
   209  let's actually start working towards implementing our handlers.
   210  
   211  Inspecting the `gen/restapi/operations/get_greeting.go` file,
   212  we'll find the following snippet:
   213  
   214  ```go
   215  // GetGreetingHandlerFunc turns a function with the right signature into a get greeting handler
   216  type GetGreetingHandlerFunc func(GetGreetingParams) middleware.Responder
   217  
   218  // Handle executing the request and returning a response
   219  func (fn GetGreetingHandlerFunc) Handle(params GetGreetingParams) middleware.Responder {
   220  	return fn(params)
   221  }
   222  ```
   223  
   224  Here we can read that there is a function type `GetGreetingHandlerFunc`, defined for the `getGreeting` operation which takes in one parameter of type `GetGreetingParams` and returns a `middleware.Responder`. This is the type alias our Handler has to adhere to.
   225  
   226  A bit more down we'll also encounter an interface `GetGreetingHandler`, defined for the `getGreeting` operation:
   227  
   228  ```go
   229  // GetGreetingHandler interface for that can handle valid get greeting params
   230  type GetGreetingHandler interface {
   231  	Handle(GetGreetingParams) middleware.Responder
   232  }
   233  ```
   234  
   235  Its only defined method looks very similar to the function type `GetGreetingHandlerFunc` function defined above. This is no coincidence.
   236  
   237  Even better, the `GetGreetingHandlerFunc` implements the `Handle` function as defined by the `GetGreetingHandler` interface, meaning that we can use a function respecting the type alias as defined by `GetGreetingHandlerFunc`, where we normally would have to implement a struct adhering to the `GetGreetingHandler` interface.
   238  
   239  Implementing our handler as a struct allows us to handle with a certain state in mind. You can check out the [kvstore example][] to see a more elaborate example, where you can see the handlers being implemented using a struct per operation.
   240  
   241  Our Greeter API is however simple enough, that we'll opt for just a simple method.
   242  [KISS][] never grows old. So with all of this said, let's implement our one and only handler.
   243  
   244  Back to the `./cmd/greeter/main.go` file, we'll define our handler as follows:
   245  
   246  ```go
   247  api.GetGreetingHandler = operations.GetGreetingHandlerFunc(
   248  	func(params operations.GetGreetingParams) middleware.Responder {
   249  		name := swag.StringValue(params.Name)
   250  		if name == "" {
   251  			name = "World"
   252  		}
   253  
   254  		greeting := fmt.Sprintf("Hello, %s!", name)
   255  		return operations.NewGetGreetingOK().WithPayload(greeting)
   256  	})
   257  ```
   258  
   259  Which replaces the `TODO: Set Handle` comment, originally defined.
   260  
   261  Let's break down the snippet above.
   262  First we make use of the [go-openapi/swag][] package, which is full of Goodies.
   263  In this case we use the `StringValue` function which transforms a `*string` into a `string`. The result will be empty in case it was nil. This comes in handy as we know that our parameter can be nil when not given, as it is _not_ required. Finally we form our greeting and return it as our payload with our `200 OK` response.
   264  
   265  Let's run our server:
   266  
   267  ```
   268  $ go run ./cmd/greeter/main.go --port 3000
   269  ```
   270  
   271  And now we're ready to test our greeter API once again:
   272  
   273  ```
   274  $ http get :3000/hello
   275  ```
   276  
   277  ```http
   278  HTTP/1.1 200 OK
   279  Connection: close
   280  Content-Length: 13
   281  Content-Type: text/plain
   282  Date: Thu, 26 Jan 2017 13:47:49 GMT
   283  
   284  Hello, World!
   285  ```
   286  
   287  Hurray, let's now greet _Swagger_:
   288  
   289  ```
   290  $ http get :3000/hello name==Swagger
   291  ```
   292  
   293  ```http
   294  HTTP/1.1 200 OK
   295  Connection: close
   296  Content-Length: 15
   297  Content-Type: text/plain
   298  Date: Thu, 26 Jan 2017 13:48:40 GMT
   299  
   300  Hello, Swagger!
   301  ```
   302  
   303  Great, Swagger will be happy to hear that.
   304  
   305  As we just learned, using [go-swagger][] and a manually defined [OpenAPI2.0][] specification file, we can build a Golang service with minimal effort. Please read the other [go-swagger][] docs for more information about how to use it and its different elements.
   306  
   307  Also please checkout the [kvstore example][] for a more complex example.
   308  It is the main inspiration for this tutorial and has been built using the exact
   309  same techniques as described in this tutorial.
   310  
   311  [OpenAPI2.0]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md
   312  [httpie]:https://httpie.org
   313  [kvstore example]: https://github.com/go-openapi/kvstore
   314  [KISS]: https://en.wikipedia.org/wiki/KISS_principle
   315  [go-openapi/swag]: https://github.com/go-openapi/swag
   316  [go-swagger]: https://github.com/thetreep/go-swagger