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