Getting started
This page takes you from a new Nette project to a working GET /api/v1/hello endpoint. Every step can be copied as it is. The same endpoint as a standalone project is the hello world example.
Requirements
| Package | Version |
|---|---|
| PHP | 8.3 or newer, with the curl, json, session and filter extensions |
| nette/application, nette/http | ^3.0 |
| latte/latte | ^3.0 |
| tracy/tracy | ^2.6 |
| justinrainbow/json-schema | ^5.2 |
0. Create a project
Skip this step if you already have a Nette application. The file paths below are those of the nette/web-project skeleton.
composer create-project nette/web-project my-api
cd my-api1. Install the library
composer require tomaj/nette-api2. Route the API to ApiPresenter
All endpoints go to one presenter, Tomaj\NetteApi\Presenters\ApiPresenter. In app/Core/RouterFactory.php, add the API route before the catch-all <presenter>/<action> route. Otherwise the catch-all route takes /api/... first and you get a 404 from the error presenter.
public static function createRouter(): RouteList
{
$router = new RouteList;
// the API route must come before the catch-all route
$router->addRoute('/api/v<version>/<package>[/<apiAction>][/<params>]', 'Api:Api:default');
$router->addRoute('<presenter>/<action>[/<id>]', 'Home:default');
return $router;
}The presenter reads the version, package and apiAction route parameters, so keep their names.
3. Register the services
Put the API configuration in its own file, config/api.neon. It maps the Api module to the library and registers the services. The application: section here is merged with the one in common.neon, so you do not edit the existing mapping.
application:
mapping:
Api: Tomaj\NetteApi\Presenters\*Presenter
services:
apiPresenter:
factory: Tomaj\NetteApi\Presenters\ApiPresenter
- Tomaj\NetteApi\Output\Configurator\DebuggerConfigurator
- Tomaj\NetteApi\Error\DefaultErrorHandler
apiDecider:
factory: Tomaj\NetteApi\ApiDecider
setup:
- addApi(
Tomaj\NetteApi\EndpointIdentifier('GET', 1, 'hello'),
App\Api\HelloHandler(),
Tomaj\NetteApi\Authorization\NoAuthorization()
)ApiPresenterships invendor/, so Nette's search for presenters inapp/does not find it. Nette creates presenters only from the DI container, so it has to be registered as a service. Keep this one namedapiPresenterservice; other pages addsetup:calls to it.ApiPresenterneeds an output configurator and an error handler.ApiDeciderholds the list of endpoints.- The skeleton indents its neon files with tabs. Tabs or spaces both work, but never mix them in one file; nette/neon refuses to parse it.
Then load the file in app/Bootstrap.php:
private function setupContainer(): void
{
$configDir = $this->rootDir . '/config';
$this->configurator->addConfig($configDir . '/common.neon');
$this->configurator->addConfig($configDir . '/services.neon');
$this->configurator->addConfig($configDir . '/api.neon');
}4. Write the handler
Create app/Api/HelloHandler.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'] . '!']);
}
}handle() gets the validated params as an array and returns a response. Handlers explains the other methods.
5. Call it
Start the built-in PHP server from the project root:
php -S localhost:8000 -t wwwIn another terminal:
curl "http://localhost:8000/api/v1/hello?name=Nette"
# {"message":"Hello Nette!"}An endpoint that is not registered answers 404 with {"status":"error","message":"Unknown api endpoint"}.
Every response also carries Access-Control-Allow-Origin: *, because that is the default CORS setting. Before you deploy, set the origins you trust; see CORS.
FAQ
Why does my endpoint answer 404 even though I registered it?
The method, version, package and action must all match. GET and POST are different endpoints. Check the route parameters too; see Architecture.
Do I need a separate presenter per endpoint?
No, ApiPresenter serves all of them. You write handlers.
Can the version be a string such as 2.1?
Yes. EndpointIdentifier stores the version as a string, so v2.1/users works. See Links and versions.
How do I add authorization to the endpoint?
Pass another object as the third argument of addApi(), for example BearerTokenAuthorization. See Authorization.
Where is a complete project I can run?
In examples/hello-world. Every example has an e2e.sh test that CI runs.