Example: hello world
One endpoint, GET /api/v1/hello?name=Ada, answering {"message":"Hello Ada!"}.
The handler
php
<?php
declare(strict_types=1);
namespace App\Api;
use Tomaj\NetteApi\Handlers\BaseHandler;
use Tomaj\NetteApi\Params\GetInputParam;
use Tomaj\NetteApi\Response\JsonApiResponse;
use Tomaj\NetteApi\Response\ResponseInterface;
final class HelloHandler extends BaseHandler
{
public function summary(): string
{
return 'Say hello';
}
public function params(): array
{
return [
(new GetInputParam('name'))->setDefault('world')->setDescription('Who to greet'),
];
}
public function handle(array $params): ResponseInterface
{
return new JsonApiResponse(200, ['message' => 'Hello ' . $params['name'] . '!']);
}
}name is optional with the default world, so GET /api/v1/hello answers {"message":"Hello world!"}.
The route
php
<?php
declare(strict_types=1);
namespace App;
use Nette\Application\Routers\RouteList;
final class RouterFactory
{
public static function create(): RouteList
{
$router = new RouteList();
// every API call goes to the presenter that ships with nette-api
$router->addRoute('/api/v<version>/<package>[/<apiAction>][/<params>]', 'Api:Api:default');
return $router;
}
}The services
yaml
application:
mapping:
# Api:Api:default is Tomaj\NetteApi\Presenters\ApiPresenter
Api: Tomaj\NetteApi\Presenters\*Presenter
services:
router: App\RouterFactory::create
# Nette 3.3 creates presenters only from the DI container
apiPresenter:
factory: Tomaj\NetteApi\Presenters\ApiPresenter
# required by ApiPresenter: when to validate output schemas and show error details
- Tomaj\NetteApi\Output\Configurator\DebuggerConfigurator
# required by ApiPresenter: turns errors into JSON responses
- Tomaj\NetteApi\Error\DefaultErrorHandler
apiDecider:
factory: Tomaj\NetteApi\ApiDecider
setup:
# GET /api/v1/hello
- addApi(
Tomaj\NetteApi\EndpointIdentifier('GET', 1, 'hello'),
App\Api\HelloHandler(),
Tomaj\NetteApi\Authorization\NoAuthorization()
)The test
bash
#!/usr/bin/env bash
# End-to-end check: start the app with the PHP built-in server and call the endpoint with curl.
set -euo pipefail
cd "$(dirname "$0")"
[[ -f vendor/autoload.php ]] || { echo "fail: vendor/ is missing, run composer install first" >&2; exit 1; }
source ../ci/lib.sh
serve 8081
request "$BASE/api/v1/hello"
expect_status 200 "GET /api/v1/hello"
expect_body '{"message":"Hello world!"}' "default name"
ok "GET /api/v1/hello says hello to the world"
request "$BASE/api/v1/hello?name=Nette"
expect_body '{"message":"Hello Nette!"}' "name from the query"
ok "GET /api/v1/hello?name=Nette uses the parameter"
request "$BASE/api/v1/nothing"
expect_status 404 "unknown endpoint"
expect_body '"Unknown api endpoint"' "unknown endpoint"
ok "an unknown endpoint answers 404"
request -X POST "$BASE/api/v1/hello"
expect_status 404 "wrong method"
ok "POST to a GET endpoint answers 404"FAQ
Why does POST /api/v1/hello answer 404?
The endpoint is registered for GET only. The method is part of the endpoint, see Handlers.
Why DebuggerConfigurator?
It shows error details and validates outputs when Tracy is in development mode. See Outputs.
Where does the JSON come from?
JsonApiResponse encodes the array and sets Content-Type: application/json. See Outputs and responses.
Can I add a second endpoint?
Add another addApi() line with a new EndpointIdentifier. The params example has two.
What does each step of the setup do?
Getting started explains the same files step by step.