Skip to content

Handlers ​

A handler is the code of one endpoint. It implements Tomaj\NetteApi\Handlers\ApiHandlerInterface; in practice you extend BaseHandler and write handle().

Open the diagram at full size

The methods ​

MethodDefault in BaseHandlerUsed by
handle(array $params): ResponseInterfaceabstract, you write itApiPresenter
params(): array[]validation, OpenAPI, console
outputs(): array[]output check, OpenAPI
summary(): string''OpenAPI, console
description(): string''OpenAPI, console
tags(): array[]OpenAPI
deprecated(): boolfalseOpenAPI

handle() gets the values of the params keyed by their names. It returns a response such as JsonApiResponse, see Outputs and responses.

php
<?php

declare(strict_types=1);

namespace App\Api;

use App\Model\UserRepository;
use Tomaj\NetteApi\Handlers\BaseHandler;
use Tomaj\NetteApi\Params\GetInputParam;
use Tomaj\NetteApi\Response\JsonApiResponse;
use Tomaj\NetteApi\Response\ResponseInterface;
use Tomaj\NetteApi\Validation\InputType;

final class UserDetailHandler extends BaseHandler
{
    public function __construct(private UserRepository $users)
    {
        parent::__construct();
    }

    public function summary(): string
    {
        return 'Get one user';
    }

    public function params(): array
    {
        return [
            (new GetInputParam('id', InputType::INTEGER))->setRequired(),
        ];
    }

    public function handle(array $params): ResponseInterface
    {
        $user = $this->users->find($params['id']);
        if ($user === null) {
            return new JsonApiResponse(404, ['status' => 'error', 'message' => 'User not found']);
        }
        return new JsonApiResponse(200, ['id' => $user->id, 'name' => $user->name]);
    }
}

BaseHandler has a constructor with an optional Fractal ScopeFactoryInterface. When your handler has its own constructor, call parent::__construct().

Registering a handler ​

ApiDecider::addApi() takes the endpoint, the handler, the authorization and an optional rate limit:

yaml
services:
	- App\Api\UserDetailHandler
	apiDecider:
		factory: Tomaj\NetteApi\ApiDecider
		setup:
			- addApi(Tomaj\NetteApi\EndpointIdentifier('GET', 1, 'users', 'detail'), App\Api\UserDetailHandler, Tomaj\NetteApi\Authorization\NoAuthorization())

The handler can be an object or a class name. A class name is looked up in the DI container only when the endpoint is called, so a handler with heavy dependencies costs nothing on other requests. It also breaks circular dependencies, for example for OpenApiHandler, which needs ApiDecider itself.

Knowing your own endpoint ​

ApiDecider calls setEndpointIdentifier() before handle(), so $this->getEndpoint() returns the matched endpoint. The authorization example returns getEndpoint()->getUrl(), which is v1/bearer/ for that route.

createLink() builds an absolute link to the same endpoint, after you call setupLinkGenerator() with Nette's LinkGenerator (a setup line in neon, see Links); without it, it throws Nette\InvalidStateException. For links to other endpoints see Links and versions.

Handlers that ship with the library ​

ClassWhat it does
DefaultHandlerAnswers 404 Unknown api endpoint, used for unknown endpoints.
AlwaysOkHandlerAnswers 200 {"status":"ok"}, handy for health checks.
EchoHandlerReturns the required status (ok or error) and all its params, for testing.
CorsPreflightHandlerAnswers OPTIONS requests, see CORS.
OpenApiHandlerThe OpenAPI document, see OpenAPI.
ApiListingHandlerA JSON list of the endpoints in its own version.

FAQ ​

Do handlers have to be services? ​

No, addApi() accepts an object created in the neon setup too. Register them as services when they need autowired dependencies. The params example does that.

How do I return a different status code? ​

Pass it as the first argument of the response, for example new JsonApiResponse(404, [...]). See Outputs and responses.

Should I catch exceptions in handle()? ​

Only when you want a specific answer. Uncaught exceptions go to the error handler and become 500. See Error handling.

How do I mark an endpoint as deprecated? ​

Return true from deprecated(). OpenAPI marks the operation as deprecated; the endpoint keeps working. See OpenAPI.

Can one handler serve two endpoints? ​

Yes, register it twice with different EndpointIdentifiers. getEndpoint() tells you which one was called. See Links and versions.

Released under the MIT License.