$npx -y skills add Lonsdale201/wp-agent-skills --skill br-openapiGenerate or serve better-route 1.1 OpenAPI 3.1 documents from Router/Resource/Woo contracts. Use for OpenApiExporter, OpenApiRouteRegistrar, contracts, contractsFromSources, route args to parameters, explicit parameter overrides, custom responses, OPTIONS 204, strictSchemas, comp
| 1 | # better-route: OpenAPI 3.1 |
| 2 | |
| 3 | Export collected contracts directly or publish a protected REST document endpoint. |
| 4 | |
| 5 | ## Export |
| 6 | |
| 7 | ```php |
| 8 | use BetterRoute\BetterRoute; |
| 9 | |
| 10 | $contracts = array_merge( |
| 11 | $router->contracts(openApiOnly: true), |
| 12 | $resource->contracts(openApiOnly: true), |
| 13 | ); |
| 14 | |
| 15 | $document = BetterRoute::openApiExporter()->export($contracts, [ |
| 16 | 'title' => 'My API', |
| 17 | 'version' => 'v1.1.0', |
| 18 | 'serverUrl' => '/wp-json', |
| 19 | 'strictSchemas' => true, |
| 20 | 'components' => [ |
| 21 | 'schemas' => [/* application schemas */], |
| 22 | ], |
| 23 | ]); |
| 24 | ``` |
| 25 | |
| 26 | Contracts exist after route declarations for a Router and after `register()` for a Resource/Woo registrar result. |
| 27 | |
| 28 | ## Publish openapi.json |
| 29 | |
| 30 | ```php |
| 31 | use BetterRoute\OpenApi\OpenApiRouteRegistrar; |
| 32 | |
| 33 | OpenApiRouteRegistrar::register( |
| 34 | restNamespace: 'myapp/v1', |
| 35 | contractsProvider: static fn (): array => OpenApiRouteRegistrar::contractsFromSources([ |
| 36 | $router, |
| 37 | $resource, |
| 38 | $woo, |
| 39 | ]), |
| 40 | options: [ |
| 41 | 'title' => 'My API', |
| 42 | 'version' => 'v1.1.0', |
| 43 | // Omit to keep the manage_options default. |
| 44 | 'permissionCallback' => static fn (): bool => current_user_can('view_api_docs'), |
| 45 | ], |
| 46 | ); |
| 47 | ``` |
| 48 | |
| 49 | The registrar mounts `GET /wp-json/myapp/v1/openapi.json`. Its default permission is `current_user_can('manage_options')`. Make it public only deliberately: |
| 50 | |
| 51 | ```php |
| 52 | 'permissionCallback' => static fn (): bool => true, |
| 53 | ``` |
| 54 | |
| 55 | The provider must be callable and return a contract list. `contractsFromSources()` accepts a mixed list of Router instances, Resource instances, and contract lists and filters each source with `openApiOnly: true` by default. |
| 56 | |
| 57 | ## Route inclusion |
| 58 | |
| 59 | Exclude a route with the actual metadata shape: |
| 60 | |
| 61 | ```php |
| 62 | $router->get('/internal', $handler) |
| 63 | ->permission($adminPermission) |
| 64 | ->meta(['openapi' => ['include' => false]]); |
| 65 | ``` |
| 66 | |
| 67 | Then use `contracts(true)` or the exporter's default `includeExcluded: false`. The obsolete `meta(['openApiOnly' => false])` shape does not control inclusion. |
| 68 | |
| 69 | ## 1.1 parameter derivation |
| 70 | |
| 71 | Executable WordPress route `args` automatically become OpenAPI path/query parameters: |
| 72 | |
| 73 | ```php |
| 74 | $router->get('/articles/(?P<id>\d+)', $handler) |
| 75 | ->publicRoute() |
| 76 | ->args([ |
| 77 | 'id' => ['type' => 'integer', 'required' => true], |
| 78 | 'context' => ['type' => 'string', 'enum' => ['view', 'edit']], |
| 79 | ]); |
| 80 | ``` |
| 81 | |
| 82 | The exporter renders the path as `/myapp/v1/articles/{id}`, puts `id` in `path`, and `context` in `query`. It carries supported schema keys such as enum/default/format/items/min/max/length/pattern. |
| 83 | |
| 84 | Explicit `meta.parameters` entries override a derived entry with the same case-insensitive `in` + `name`; derived parameters not overridden remain present. Use explicit metadata for headers/cookies or richer descriptions, not to duplicate every `args` rule. |
| 85 | |
| 86 | ## Responses |
| 87 | |
| 88 | Defaults are: |
| 89 | |
| 90 | - POST: `201`; |
| 91 | - OPTIONS: `204` with no JSON body; |
| 92 | - other supported methods: `200`; |
| 93 | - `default`: `ErrorResponse`. |
| 94 | |
| 95 | An explicit `meta.responses[status]` replaces the default at that same status: |
| 96 | |
| 97 | ```php |
| 98 | ->meta([ |
| 99 | 'responses' => [ |
| 100 | '202' => ['description' => 'Accepted'], |
| 101 | 'default' => ['$ref' => '#/components/responses/ErrorResponse'], |
| 102 | ], |
| 103 | ]) |
| 104 | ``` |
| 105 | |
| 106 | `HEAD` and `204` responses are emitted without content. |
| 107 | |
| 108 | ## Resource envelope schemas |
| 109 | |
| 110 | Resource create/update responses are `{data: ...}` and must reference `<Resource>Response`. A get references `<Resource>` unless `uniformEnvelope(true)` is enabled, in which case it also references `<Resource>Response`. Lists use `<Resource>ListResponse`. |
| 111 | |
| 112 | In strict mode provide, as applicable: |
| 113 | |
| 114 | - `<Resource>` |
| 115 | - `<Resource>Input` |
| 116 | - `<Resource>Response` |
| 117 | - `<Resource>ListResponse` |
| 118 | - `DeleteResponse` |
| 119 | |
| 120 | ## Security |
| 121 | |
| 122 | Declare schemes and document defaults explicitly: |
| 123 | |
| 124 | ```php |
| 125 | 'securitySchemes' => [ |
| 126 | 'bearerAuth' => [ |
| 127 | 'type' => 'http', |
| 128 | 'scheme' => 'bearer', |
| 129 | 'bearerFormat' => 'JWT', |
| 130 | ], |
| 131 | ], |
| 132 | 'globalSecurity' => [['bearerAuth' => []]], |
| 133 | ``` |
| 134 | |
| 135 | `publicRoute()` and explicitly public Resource actions emit operation `security: []`. `protectedByMiddleware('bearerAuth')` or a list of security objects sets route metadata. Better Route does not infer a scheme definition from middleware; the component must still be supplied. |
| 136 | |
| 137 | ## Components and strict mode |
| 138 | |
| 139 | `strictSchemas: true` throws when a referenc |