OpenAPI
OpenApiHandler is a normal handler that returns an OpenAPI 3.0 document of all endpoints with the same version as its own. Register it like any endpoint.
application:
mapping:
Api: Tomaj\NetteApi\Presenters\*Presenter
*: App\UI\*\**Presenter
latte:
extensions:
# the {apiLink} tag
- Tomaj\NetteApi\Link\ApiLinkExtension
parameters:
apiTokens:
secret-token: '*'
services:
router: App\RouterFactory::create
apiPresenter:
factory: Tomaj\NetteApi\Presenters\ApiPresenter
- App\UI\Console\ConsolePresenter
- Tomaj\NetteApi\Output\Configurator\EnvConfigurator
- Tomaj\NetteApi\Error\DefaultErrorHandler
- Tomaj\NetteApi\Misc\IpDetector
apiLink: Tomaj\NetteApi\Link\ApiLink
# {apiLink} calls the filter of this name
latte.latteFactory:
setup:
- addFilter(apiLink, [@apiLink, link])
- App\Api\BookListHandler
- App\Api\BookCreateHandler
# the listing handlers need ApiDecider, so they are registered by class name below
# and created only when their endpoint is called
- Tomaj\NetteApi\Handlers\OpenApiHandler(initData: [info: [title: 'Bookshelf API', description: 'Example API documented with OpenAPI']])
- Tomaj\NetteApi\Handlers\ApiListingHandler
apiDecider:
factory: Tomaj\NetteApi\ApiDecider
setup:
- addApi(Tomaj\NetteApi\EndpointIdentifier('GET', 1, 'books'), App\Api\BookListHandler, Tomaj\NetteApi\Authorization\NoAuthorization())
- addApi(Tomaj\NetteApi\EndpointIdentifier('POST', 1, 'books'), App\Api\BookCreateHandler, Tomaj\NetteApi\Authorization\BearerTokenAuthorization(Tomaj\NetteApi\Misc\StaticTokenRepository(%apiTokens%)))
# GET /api/v1/openapi, ?format=yaml with symfony/yaml installed
- addApi(Tomaj\NetteApi\EndpointIdentifier('GET', 1, 'openapi'), Tomaj\NetteApi\Handlers\OpenApiHandler, Tomaj\NetteApi\Authorization\NoAuthorization())
# GET /api/v1/endpoints, a short JSON list of the version 1 endpoints
- addApi(Tomaj\NetteApi\EndpointIdentifier('GET', 1, 'endpoints'), Tomaj\NetteApi\Handlers\ApiListingHandler, Tomaj\NetteApi\Authorization\NoAuthorization())OpenApiHandler needs ApiDecider, which in turn holds the handlers, so register it by class name in addApi(). It is created only when its endpoint is called.
What goes into the document
| From | Into |
|---|---|
summary(), description(), tags(), deprecated() | the operation |
params() | parameters, and requestBody for body params such as JSON and raw input |
outputs() | responses with JSON schemas |
| the authorization class | securitySchemes and security: Bearer, Basic, or apiKey in query, header or cookie |
| params, the authorization | 400 when there are params, 401 unless NoAuthorization, and 500 always |
Endpoints with your own authorization class appear without security.
Constructor
new OpenApiHandler(
ApiDecider $apiDecider,
ApiLink $apiLink,
Nette\Http\Request $request,
array $initData = [], // merged into the document, e.g. info.title
array $allowedEndpoints = [], // path pattern => methods to include; empty means all
?SpecBuilderInterface $specBuilder = null // custom builder, see below
)?format=yaml returns YAML when symfony/yaml is installed; otherwise only json is allowed.
allowedEndpoints
Keys (or values in a list) are patterns matched case-insensitively against the endpoint path without version (e.g. users/list); values limit HTTP methods, e.g. ['users' => ['get', 'post'], 'orders/list' => 'get'].
WARNING
A pattern matches anywhere in the path, so users also includes admin/users-internal, and * is not a wildcard. Use patterns that are unique enough and check the generated document when it is public. See #211.
Builders
The document is created by small builders in Tomaj\NetteApi\OpenApi, each responsible for one part:
| Class | Creates |
|---|---|
SpecBuilder | whole document: info, servers, components, paths (filters APIs by version) |
PathBuilder | paths and operations (summary, tags, parameters, request body, security, responses) |
ParameterBuilder | query and cookie parameters |
RequestBodyBuilder | JSON, raw, form and multipart request bodies |
ResponseBuilder | error responses and their schemas, responses from handler outputs |
SecuritySchemeBuilder | security schemes and requirements from authorizations (override createScheme()) |
SchemaRegistry (transforms JSON schemas and collects definitions into components/schemas) and EndpointFilter (allowedEndpoints) are helpers created for each generated document.
Replacing a builder via DI
Extend the builder and register it in DI together with PathBuilder and SpecBuilder. OpenApiHandler gets SpecBuilder autowired, otherwise the default builders are used. Links are always created by the ApiLink of the handler.
Since 4.0 each builder has an interface (SpecBuilderInterface, PathBuilderInterface, ParameterBuilderInterface, RequestBodyBuilderInterface, ResponseBuilderInterface, SecuritySchemeBuilderInterface) and constructors accept the interface, so you can also replace a builder with your own implementation instead of extending it.
use Tomaj\NetteApi\Handlers\ApiHandlerInterface;
use Tomaj\NetteApi\OpenApi\ParameterBuilder;
class MyParameterBuilder extends ParameterBuilder
{
public function build(ApiHandlerInterface $handler): array
{
$parameters = parent::build($handler);
$parameters[] = ['name' => 'X-Request-Id', 'in' => 'header', 'required' => false, 'schema' => ['type' => 'string']];
return $parameters;
}
}services:
- MyParameterBuilder
- Tomaj\NetteApi\OpenApi\PathBuilder
- Tomaj\NetteApi\OpenApi\SpecBuilderFAQ
How do I change the title "Nette API"?
Pass initData: [info: [title: 'My API']]. It is merged recursively over the generated document.
Can I hide internal endpoints?
Yes, list the public ones in allowedEndpoints (mind the matching rules), or register a second OpenApiHandler for a different version.
Why are endpoints of version 2 missing?
The document covers only the version of the OpenApiHandler endpoint. Register one per version; see Links and versions.
Can I use the document with Swagger UI?
Yes, point Swagger UI at the URL of the endpoint. The library also has its own web console.
Is the OpenAPI endpoint public?
Only if you register it with NoAuthorization, as the OpenAPI example does. Any authorization works.