|
| 1 | +# Overview |
| 2 | + |
| 3 | +Pode has built-in support for converting your routes into OpenAPI 3.0 definitions. There is also support for enabling simple Swagger and/or ReDoc viewers and others. |
| 4 | + |
| 5 | +The OpenApi module has been extended with many more functions, and some old ones have been improved. |
| 6 | + |
| 7 | +For more detailed information regarding OpenAPI and Pode, please refer to [OpenAPI Specification and Pode](../Specification/v3_0_3.md) |
| 8 | + |
| 9 | +You can enable OpenAPI in Pode, and a straightforward definition will be generated. However, to get a more complex definition with request bodies, parameters, and response payloads, you'll need to use the relevant OpenAPI functions detailed below. |
| 10 | + |
| 11 | +## Enabling OpenAPI |
| 12 | + |
| 13 | +To enable support for generating OpenAPI definitions you'll need to use the [`Enable-PodeOpenApi`](../../../Functions/OpenApi/Enable-PodeOpenApi) function. This will allow you to set a title and version for your API. You can also set a default route to retrieve the OpenAPI definition for tools like Swagger or ReDoc, the default is at `/openapi`. |
| 14 | + |
| 15 | +You can also set a route filter (such as `/api/*`, the default is `/*` for everything), so only those routes are included in the definition. |
| 16 | + |
| 17 | +An example of enabling OpenAPI is a follows: |
| 18 | + |
| 19 | +```powershell |
| 20 | +Enable-PodeOpenApi -Title 'My Awesome API' -Version 9.0.0.1 |
| 21 | +``` |
| 22 | + |
| 23 | +An example of setting the OpenAPI route is a follows. This will create a route accessible at `/docs/openapi`: |
| 24 | + |
| 25 | +```powershell |
| 26 | +Enable-PodeOpenApi -Path '/docs/openapi' -Title 'My Awesome API' -Version 9.0.0.1 |
| 27 | +``` |
| 28 | + |
| 29 | +### Default Setup |
| 30 | + |
| 31 | +In the very simplest of scenarios, just enabling OpenAPI will generate a minimal definition. It can be viewed in Swagger/ReDoc etc, but won't be usable for trying calls. |
| 32 | + |
| 33 | +When you enable OpenAPI, and don't set any other OpenAPI data, the following is the minimal data that is included: |
| 34 | + |
| 35 | +* Every route will have a 200 and Default response |
| 36 | +* Although routes will be included, no request bodies, parameters or response payloads will be defined |
| 37 | +* If you have multiple endpoints, then the servers section will be included |
| 38 | +* Any authentication will be included |
| 39 | + |
| 40 | +This can be changed with [`Enable-PodeOpenApi`](../../../Functions/OpenApi/Enable-PodeOpenApi) |
| 41 | + |
| 42 | +For example to change the default response 404 and 500 |
| 43 | + |
| 44 | +```powershell |
| 45 | +Enable-PodeOpenApi -Path '/docs/openapi' -OpenApiVersion '3.0.3' -DefaultResponses ( |
| 46 | + New-PodeOAResponse -StatusCode 404 -Description 'User not found' | Add-PodeOAResponse -StatusCode 500 |
| 47 | + ) |
| 48 | +``` |
| 49 | + |
| 50 | +For disabling the Default Response use: |
| 51 | + |
| 52 | +```powershell |
| 53 | +Enable-PodeOpenApi -Path '/docs/openapi' -OpenApiVersion '3.0.3' -NoDefaultResponses |
| 54 | +``` |
| 55 | + |
| 56 | +For disabling the Minimal Definitions feature use: |
| 57 | + |
| 58 | +```powershell |
| 59 | +Enable-PodeOpenApi -Path '/docs/openapi' -OpenApiVersion '3.0.3' -DisableMinimalDefinitions |
| 60 | +``` |
| 61 | + |
| 62 | +### Get Definition |
| 63 | + |
| 64 | +Instead of defining a route to return the definition, you can write the definition to the response whenever you want, and in any route, using the [`Get-PodeOADefinition`](../../../Functions/OpenApi/Get-PodeOADefinition) function. This could be useful in certain scenarios like in Azure Functions, where you can enable OpenAPI, and then write the definition to the response of a GET request if some query parameter is set; eg: `?openapi=1`. |
| 65 | + |
| 66 | +For example: |
| 67 | + |
| 68 | +```powershell |
| 69 | +Add-PodeRoute -Method Get -Path '/' -ScriptBlock { |
| 70 | + if ($WebEvent.Query.openapi -eq 1) { |
| 71 | + Get-PodeOpenApiDefinition | Write-PodeJsonResponse |
| 72 | + } |
| 73 | +} |
| 74 | +``` |
| 75 | + |
| 76 | +## OpenAPI Info object |
| 77 | + |
| 78 | +In previous releases some of the Info object properties like Version and Title were defined by [`Enable-PodeOpenApi`](../../../Functions/OpenApi/Enable-PodeOpenApi). |
| 79 | +Starting from version 2.10 a new [`Add-PodeOAInfo`](../../../Functions/OpenApi/Add-PodeOAInfo) function has been added to create a full OpenAPI Info spec. |
| 80 | + |
| 81 | +```powershell |
| 82 | +Add-PodeOAInfo -Title 'Swagger Petstore - OpenAPI 3.0' ` |
| 83 | + -Version 1.0.17 ` |
| 84 | + -Description $InfoDescription ` |
| 85 | + -TermsOfService 'http://swagger.io/terms/' ` |
| 86 | + -LicenseName 'Apache 2.0' ` |
| 87 | + -LicenseUrl 'http://www.apache.org/licenses/LICENSE-2.0.html' ` |
| 88 | + -ContactName 'API Support' ` |
| 89 | + -ContactEmail 'apiteam@swagger.io' |
| 90 | +``` |
| 91 | + |
| 92 | +## OpenAPI configuration Best Practice |
| 93 | + |
| 94 | +Pode is rich of functions to create and configure an complete OpenApi spec. Here is a typical code you should use to initiate an OpenApi spec |
| 95 | + |
| 96 | +```powershell |
| 97 | +#Initialize OpenApi |
| 98 | +Enable-PodeOpenApi -Path '/docs/openapi' -Title 'Swagger Petstore - OpenAPI 3.0' ` |
| 99 | + -OpenApiVersion 3.1 -DisableMinimalDefinitions -NoDefaultResponses |
| 100 | +
|
| 101 | +# OpenApi Info |
| 102 | +Add-PodeOAInfo -Title 'Swagger Petstore - OpenAPI 3.0' ` |
| 103 | + -Version 1.0.17 ` |
| 104 | + -Description 'This is a sample Pet Store Server based on the OpenAPI 3.0 specification. ...' ` |
| 105 | + -TermsOfService 'http://swagger.io/terms/' ` |
| 106 | + -LicenseName 'Apache 2.0' ` |
| 107 | + -LicenseUrl 'http://www.apache.org/licenses/LICENSE-2.0.html' ` |
| 108 | + -ContactName 'API Support' ` |
| 109 | + -ContactEmail 'apiteam@swagger.io' ` |
| 110 | + -ContactUrl 'http://example.com/support' |
| 111 | +
|
| 112 | +# Endpoint for the API |
| 113 | + Add-PodeOAServerEndpoint -url '/api/v3.1' -Description 'default endpoint' |
| 114 | +
|
| 115 | + # OpenApi external documentation links |
| 116 | + $extDoc = New-PodeOAExternalDoc -Name 'SwaggerDocs' -Description 'Find out more about Swagger' -Url 'http://swagger.io' |
| 117 | + $extDoc | Add-PodeOAExternalDoc |
| 118 | +
|
| 119 | + # OpenApi documentation viewer |
| 120 | + Enable-PodeOAViewer -Type Swagger -Path '/docs/swagger' |
| 121 | + Enable-PodeOAViewer -Type ReDoc -Path '/docs/redoc' |
| 122 | + Enable-PodeOAViewer -Type RapiDoc -Path '/docs/rapidoc' |
| 123 | + Enable-PodeOAViewer -Type StopLight -Path '/docs/stoplight' |
| 124 | + Enable-PodeOAViewer -Type Explorer -Path '/docs/explorer' |
| 125 | + Enable-PodeOAViewer -Bookmarks -Path '/docs' |
| 126 | +``` |
| 127 | + |
| 128 | +## Authentication |
| 129 | + |
| 130 | +Any authentication defined, either by [`Add-PodeAuthMiddleware`](../../../Functions/Authentication/Add-PodeAuthMiddleware), or using the `-Authentication` parameter on Routes, will be automatically added to the `security` section of the OpenAPI definition. |
| 131 | + |
| 132 | + |
| 133 | +## Tags |
| 134 | + |
| 135 | +In OpenAPI, a "tag" is used to group related operations. Tags are often used to organize and categorize endpoints in an API specification, making it easier to understand and navigate the API documentation. Each tag can be associated with one or more API operations, and these tags are then used in tools like Swagger UI to group and display operations in a more organized way. |
| 136 | + |
| 137 | +Here's an example of how to define and use tags: |
| 138 | + |
| 139 | +```powershell |
| 140 | +# create an External Doc reference |
| 141 | +$swaggerDocs = New-PodeOAExternalDoc -Description 'Find out more about Swagger' -Url 'http://swagger.io' |
| 142 | +
|
| 143 | +# create a Tag |
| 144 | +Add-PodeOATag -Name 'pet' -Description 'Everything about your Pets' -ExternalDoc $swaggerDocs |
| 145 | +
|
| 146 | +Add-PodeRoute -PassThru -Method get -Path '/pet/findByStatus' -Authentication 'Login-OAuth2' -Scope 'read' -AllowAnon -ScriptBlock { |
| 147 | + #route code |
| 148 | +} | Set-PodeOARouteInfo -Summary 'Finds Pets by status' -Description 'Multiple status values can be provided with comma-separated strings' ` |
| 149 | + -Tags 'pet' -OperationId 'findPetsByStatus' |
| 150 | +``` |
| 151 | + |
| 152 | +## Routes |
| 153 | + |
| 154 | +To extend the definition of a route, you can use the `-PassThru` switch on the [`Add-PodeRoute`](../../../Functions/Routes/Add-PodeRoute) function. This will cause the route that was created to be returned, so you can pass it down the pipe into more OpenAPI functions. |
| 155 | + |
| 156 | +To add metadata to a route's definition you can use the [`Set-PodeOARouteInfo`](../../../Functions/OpenApi/Set-PodeOARouteInfo) function. This will allow you to define a summary/description for the route, as well as tags for grouping: |
| 157 | + |
| 158 | +```powershell |
| 159 | +Add-PodeRoute -Method Get -Path "/api/resources" -ScriptBlock { |
| 160 | + Set-PodeResponseStatus -Code 200 |
| 161 | +} -PassThru | |
| 162 | + Set-PodeOARouteInfo -Summary 'Retrieve some resources' -Tags 'Resources' |
| 163 | +``` |
| 164 | + |
| 165 | +Each of the following OpenAPI functions have a `-PassThru` switch, allowing you to chain many of them together. |
| 166 | + |
| 167 | +### Responses |
| 168 | + |
| 169 | +You can define multiple responses for a route, but only one of each status code, using the [`Add-PodeOAResponse`](../../../Functions/OpenApi/Add-PodeOAResponse) function. You can either just define the response and status code, with a custom description, or with a schema defining the payload of the response. |
| 170 | + |
| 171 | +The following is an example of defining simple 200 and 404 responses on a route: |
| 172 | + |
| 173 | +```powershell |
| 174 | +Add-PodeRoute -Method Get -Path "/api/user/:userId" -ScriptBlock { |
| 175 | + # logic |
| 176 | +} -PassThru | |
| 177 | + Add-PodeOAResponse -StatusCode 200 -PassThru | |
| 178 | + Add-PodeOAResponse -StatusCode 404 -Description 'User not found' |
| 179 | +``` |
| 180 | + |
| 181 | +Whereas the following is a more complex definition, which also defines the responses JSON payload. This payload is defined as an object with a string Name, and integer UserId: |
| 182 | + |
| 183 | +```powershell |
| 184 | +Add-PodeRoute -Method Get -Path '/api/users/:userId' -ScriptBlock { |
| 185 | + Write-PodeJsonResponse -Value @{ |
| 186 | + Name = 'Rick' |
| 187 | + UserId = $WebEvent.Parameters['userId'] |
| 188 | + } |
| 189 | +} -PassThru | |
| 190 | + Add-PodeOAResponse -StatusCode 200 -Description 'A user object' --Content @{ |
| 191 | + 'application/json' = (New-PodeOAStringProperty -Name 'Name'| |
| 192 | + New-PodeOAIntProperty -Name 'UserId'| New-PodeOAObjectProperty) |
| 193 | + } |
| 194 | +``` |
| 195 | + |
| 196 | +the JSON response payload defined is as follows: |
| 197 | + |
| 198 | +```json |
| 199 | +{ |
| 200 | + "Name": [string], |
| 201 | + "UserId": [integer] |
| 202 | +} |
| 203 | +``` |
| 204 | + |
| 205 | +In case the response JSON payload is an array |
| 206 | + |
| 207 | +```powershell |
| 208 | +Add-PodeRoute -Method Get -Path '/api/users/:userId' -ScriptBlock { |
| 209 | + Write-PodeJsonResponse -Value @{ |
| 210 | + Name = 'Rick' |
| 211 | + UserId = $WebEvent.Parameters['userId'] |
| 212 | + } |
| 213 | + } -PassThru | |
| 214 | + Add-PodeOAResponse -StatusCode 200 -Description 'A user object' -Content ( |
| 215 | + New-PodeOAContentMediaType -ContentMediaType 'application/json' -Array -Content ( |
| 216 | + New-PodeOAStringProperty -Name 'Name' | |
| 217 | + New-PodeOAIntProperty -Name 'UserId' | |
| 218 | + New-PodeOAObjectProperty |
| 219 | + ) |
| 220 | + ) |
| 221 | +``` |
| 222 | + |
| 223 | +```json |
| 224 | +[ |
| 225 | + { |
| 226 | + "Name": [string], |
| 227 | + "UserId": [integer] |
| 228 | + } |
| 229 | +] |
| 230 | +``` |
| 231 | + |
| 232 | +Internally, each route is created with an empty default 200 and 500 response. You can remove these, or other added responses, by using [`Remove-PodeOAResponse`](../../../Functions/OpenApi/Remove-PodeOAResponse): |
| 233 | + |
| 234 | +```powershell |
| 235 | +Add-PodeRoute -Method Get -Path "/api/user/:userId" -ScriptBlock { |
| 236 | + # route logic |
| 237 | +} -PassThru | |
| 238 | + Remove-PodeOAResponse -StatusCode 200 |
| 239 | +``` |
| 240 | + |
| 241 | +### Requests |
| 242 | + |
| 243 | +#### Parameters |
| 244 | + |
| 245 | +You can set route parameter definitions, such as parameters passed in the path/query, by using the [`Set-PodeOARequest`](../../../Functions/OpenApi/Set-PodeOARequest) function with the `-Parameters` parameter. The parameter takes an array of properties converted into parameters, using the [`ConvertTo-PodeOAParameter`](../../../Functions/OpenApi/ConvertTo-PodeOAParameter) function. |
| 246 | + |
| 247 | +For example, to create some integer `userId` parameter that is supplied in the path of the request, the following will work: |
| 248 | + |
| 249 | +```powershell |
| 250 | +Add-PodeRoute -Method Get -Path '/api/users/:userId' -ScriptBlock { |
| 251 | + Write-PodeJsonResponse -Value @{ |
| 252 | + Name = 'Rick' |
| 253 | + UserId = $WebEvent.Parameters['userId'] |
| 254 | + } |
| 255 | +} -PassThru | |
| 256 | + Set-PodeOARequest -Parameters @( |
| 257 | + (New-PodeOAIntProperty -Name 'userId' -Required | ConvertTo-PodeOAParameter -In Path) |
| 258 | + ) |
| 259 | +``` |
| 260 | + |
| 261 | +Whereas you could use the next example to define 2 query parameters, both strings: |
| 262 | + |
| 263 | +```powershell |
| 264 | +Add-PodeRoute -Method Get -Path '/api/users' -ScriptBlock { |
| 265 | + Write-PodeJsonResponse -Value @{ |
| 266 | + Name = 'Rick' |
| 267 | + UserId = $WebEvent.Query['name'] |
| 268 | + } |
| 269 | +} -PassThru | |
| 270 | + Set-PodeOARequest -Parameters ( |
| 271 | + (New-PodeOAStringProperty -Name 'name' -Required | ConvertTo-PodeOAParameter -In Query), |
| 272 | + (New-PodeOAStringProperty -Name 'city' -Required | ConvertTo-PodeOAParameter -In Query) |
| 273 | + ) |
| 274 | +``` |
| 275 | + |
| 276 | +#### Payload |
| 277 | + |
| 278 | +You can set request payload schemas by using the [`Set-PodeOARequest`](../../../Functions/OpenApi/Set-PodeOARequest)function, with the `-RequestBody` parameter. The request body can be defined using the [`New-PodeOARequestBody`](../../../Functions/OpenApi/New-PodeOARequestBody) function, and supplying schema definitions for content types - this works in very much a similar way to defining responses above. |
| 279 | + |
| 280 | +For example, to define a request JSON payload of some `userId` and `name` you could use the following: |
| 281 | + |
| 282 | +```powershell |
| 283 | +Add-PodeRoute -Method Patch -Path '/api/users' -ScriptBlock { |
| 284 | + Write-PodeJsonResponse -Value @{ |
| 285 | + Name = $WebEvent.Data.name |
| 286 | + UserId = $WebEvent.Data.userId |
| 287 | + } |
| 288 | +} -PassThru | |
| 289 | + Set-PodeOARequest -RequestBody ( |
| 290 | + New-PodeOARequestBody -Required -Content ( |
| 291 | + New-PodeOAContentMediaType -ContentMediaType 'application/json','application/xml' -Content ( New-PodeOAStringProperty -Name 'Name'| New-PodeOAIntProperty -Name 'UserId'| New-PodeOAObjectProperty ) ) |
| 292 | +
|
| 293 | + ) |
| 294 | +``` |
| 295 | + |
| 296 | +The expected payload would look as follows: |
| 297 | + |
| 298 | +```json |
| 299 | +{ |
| 300 | + "name": [string], |
| 301 | + "userId": [integer] |
| 302 | +} |
| 303 | +``` |
| 304 | + |
| 305 | +```xml |
| 306 | +<Object> |
| 307 | + <name type="string"></name> |
| 308 | + <userId type="integer"></userId> |
| 309 | +</Object> |
| 310 | + |
| 311 | +``` |
| 312 | + |
| 313 | + |
0 commit comments