### Index Users Endpoint with Query Parameters Source: https://context7.com/contributte/apitte/llms.txt This example demonstrates a GET endpoint for listing users, utilizing multiple query parameters for pagination, filtering, and sorting. It shows how to specify types, whether parameters are required, and allowed enum values. ```APIDOC ## GET /api/v1/users/ ### Description Retrieves a list of users with support for pagination, limiting results, filtering by active status, and sorting. ### Method GET ### Endpoint /api/v1/users/ ### Parameters #### Query Parameters - **page** (int) - Optional - The page number for pagination. - **limit** (int) - Optional - The maximum number of users to return per page. - **active** (bool) - Optional - Filters users by their active status. Can be empty. - **sort** (string) - Optional - Specifies the sorting order. Allowed values: "asc", "desc". Defaults to "asc". ### Request Example GET /api/v1/users?page=2&limit=20&active=true&sort=desc ### Response #### Success Response (200) - **page** (int) - The current page number. - **limit** (int) - The number of items per page. - **active** (bool|null) - The active status filter. - **sort** (string) - The sorting order applied. #### Response Example { "page": 2, "limit": 20, "active": true, "sort": "desc" } ``` -------------------------------- ### Installation Source: https://context7.com/contributte/apitte/llms.txt Install Apitte via Composer and register the DI extension in your NEON configuration. ```bash composer require contributte/apitte ``` ```neon # config/common.neon extensions: api: Apitte\Core\DI\ApiExtension api: debug: %debugMode% catchException: true ``` -------------------------------- ### Standalone Application Bootstrap Source: https://context7.com/contributte/apitte/llms.txt Example of a standalone Apitte entry point that boots the DI container and runs the API application. ```php // www/index.php — standalone API use Apitte\Core\Application\IApplication; use App\Bootstrap; require __DIR__ . '/../vendor/autoload.php'; Bootstrap::boot() ->createContainer() ->getByType(IApplication::class) ->run(); ``` -------------------------------- ### Install Contributte DI ResourceExtension Source: https://context7.com/contributte/apitte/llms.txt Install the contributte/di package to enable automatic controller registration via the ResourceExtension. ```bash composer require contributte/di ``` -------------------------------- ### Detail User Endpoint with Path Parameter Source: https://context7.com/contributte/apitte/llms.txt This example shows how to define a GET endpoint that accepts a user ID as a path parameter. The `#[RequestParameter]` attribute ensures the ID is an integer and provides a description. ```APIDOC ## GET /api/v1/users/{id} ### Description Retrieves details for a specific user identified by their ID. ### Method GET ### Endpoint /api/v1/users/{id} ### Parameters #### Path Parameters - **id** (int) - Required - User ID ### Request Example (No request body for GET request) ### Response #### Success Response (200) - **id** (int) - The ID of the user. - **name** (string) - The name of the user. #### Response Example { "id": 1, "name": "John Doe" } ``` -------------------------------- ### Install Contributte/Apitte with Composer Source: https://github.com/contributte/apitte/blob/master/README.md Use this command to install the latest version of the contributte/apitte package using Composer. ```bash composer require contributte/apitte ``` -------------------------------- ### Combined Nette UI + Apitte API Bootstrap Source: https://context7.com/contributte/apitte/llms.txt Example of bootstrapping a project that hosts both a Nette UI application and an Apitte API, routing based on URI prefix. ```php // www/index.php — combined Nette UI + Apitte API use Apitte\Core\Application\IApplication as ApiApplication; use Nette\Application\Application as UIApplication; use App\Bootstrap; require __DIR__ . '/../vendor/autoload.php'; $isApi = str_starts_with($_SERVER['REQUEST_URI'], '/api'); $container = Bootstrap::boot()->createContainer(); if ($isApi) { $container->getByType(ApiApplication::class)->run(); } else { $container->getByType(UIApplication::class)->run(); } ``` -------------------------------- ### Create User Endpoint with Request Body Entity Source: https://context7.com/contributte/apitte/llms.txt This example illustrates how to handle a POST request to create a user. It uses the `#[RequestBody]` attribute to automatically deserialize the JSON request body into a `CreateUserEntity` object. ```APIDOC ## POST /api/v1/users ### Description Creates a new user based on the provided information in the request body. ### Method POST ### Endpoint /api/v1/users ### Parameters #### Request Body - **firstName** (string) - Required - The first name of the user. - **lastName** (string) - Required - The last name of the user. - **email** (string) - Required - The email address of the user. - **age** (int) - Optional - The age of the user. ### Request Example { "firstName": "John", "lastName": "Doe", "email": "john.doe@example.com", "age": 30 } ### Response #### Success Response (201 Created) - **created** (bool) - Indicates if the user was successfully created. - **email** (string) - The email of the created user. #### Response Example { "created": true, "email": "john.doe@example.com" } ``` -------------------------------- ### Define a Custom Plugin for API Configuration Source: https://context7.com/contributte/apitte/llms.txt Extend the Plugin base class to add custom services, overrides, or compiler passes to your DI container. This example shows a RateLimitPlugin. ```php // src/Api/Plugin/RateLimitPlugin.php namespace App\Api\Plugin; use Apitte\Core\DI\Plugin\Plugin; use Nette\DI\ContainerBuilder; class RateLimitPlugin extends Plugin { public static function getName(): string { return 'rateLimit'; } public function loadPluginConfiguration(): void { $builder = $this->getContainerBuilder(); $builder->addDefinition($this->prefix('limiter')) ->setFactory(App\Api\RateLimit\RateLimiter::class) ->addSetup('setLimit', [100]); // Register as a request decorator automatically $builder->addDefinition($this->prefix('decorator')) ->setFactory(App\Api\Decorator\RateLimitDecorator::class) ->addTag('apitte.core.decorator', ['priority' => 5]); } } ``` -------------------------------- ### Implement Content Negotiation in PHP Source: https://context7.com/contributte/apitte/llms.txt Use NegotiationPlugin and ArrayEntity to automatically serialize response entities based on the Accept header or URL suffix. This example shows default JSON negotiation. ```php namespace App\Api\V1\Controllers; use Apitte\Core\Annotation\Controller\Method; use Apitte\Core\Annotation\Controller\Negotiation; use Apitte\Core\Annotation\Controller\Path; use Apitte\Core\Http\ApiRequest; use Apitte\Core\Http\ApiResponse; use Apitte\Negotiation\Http\ArrayEntity; #[Path("/users")] class UsersController extends BaseV1Controller { // Responds to: // GET /api/v1/users/ (defaults to json via FallbackNegotiator) // GET /api/v1/users.json (SuffixNegotiator) // GET /api/v1/users.xml (custom XmlTransformer) #[Path("/")] #[Method("GET")] #[Negotiation(default: true, suffix: "json")] public function index(ApiRequest $request, ApiResponse $response): ApiResponse { $entity = ArrayEntity::from([ ['id' => 1, 'firstName' => 'John', 'email' => 'john@doe.com'], ['id' => 2, 'firstName' => 'Elon', 'email' => 'elon@spacex.com'], ]); return $response ->withStatus(ApiResponse::S200_OK) ->withEntity($entity); } } ``` -------------------------------- ### GET /api/v1/users/ Source: https://context7.com/contributte/apitte/llms.txt Retrieves a list of users. This endpoint is defined using `#[Path]` and `#[Method]` attributes on a controller method. ```APIDOC ## GET /api/v1/users/ ### Description Retrieves a list of users. ### Method GET ### Endpoint /api/v1/users/ ### Parameters #### Query Parameters None #### Request Body None ### Response #### Success Response (200) - **id** (integer) - User ID - **firstName** (string) - User's first name - **lastName** (string) - User's last name - **email** (string) - User's email address ### Response Example ```json [ {'id': 1, 'firstName': 'John', 'lastName': 'Doe', 'email': 'john@doe.com'}, {'id': 2, 'firstName': 'Elon', 'lastName': 'Musk', 'email': 'elon@spacex.com'} ] ``` ``` -------------------------------- ### Implement Custom Schema Validation Source: https://context7.com/contributte/apitte/llms.txt Enforce custom schema-level rules at compile time by implementing the IValidation interface. This example ensures all endpoints have tags. ```php // src/Api/Validation/RequireTagValidation.php namespace App\Api\Validation; use Apitte\Core\Schema\Builder\SchemaBuilder; use Apitte\Core\Schema\Validation\IValidation; use Apitte\Core\Exception\Logical\InvalidSchemaException; class RequireTagValidation implements IValidation { public function validate(SchemaBuilder $builder): void { foreach ($builder->getControllers() as $controller) { foreach ($controller->getMethods() as $method) { if (empty($method->getTags())) { throw new InvalidSchemaException(sprintf( 'Endpoint %s::%s() has no tags defined.', $controller->getClass(), $method->getName(), )); } } } } } ``` -------------------------------- ### Bootstrap Application with Middlewares Source: https://context7.com/contributte/apitte/llms.txt Use `Contributte\Middlewares\Application\IApplication` when integrating `MiddlewaresPlugin`. This ensures the application correctly processes middleware in the pipeline. ```php // www/index.php — use Contributte\Middlewares IApplication when using MiddlewaresPlugin use Contributte\Middlewares\Application\IApplication; use App\Bootstrap; require __DIR__ . '/../vendor/autoload.php'; Bootstrap::boot()->createContainer()->getByType(IApplication::class)->run(); ``` -------------------------------- ### Combined Nette UI and Apitte API Bootstrap Source: https://context7.com/contributte/apitte/llms.txt Bootstrap a project that hosts both a Nette UI application and an Apitte API. Routing is based on the URI prefix. ```php require __DIR__ . '/../vendor/autoload.php'; $isApi = str_starts_with($_SERVER['REQUEST_URI'], '/api'); $container = Bootstrap::boot()->createContainer(); if ($isApi) { $container->getByType(ApiApplication::class)->run(); } else { $container->getByType(UIApplication::class)->run(); } ``` -------------------------------- ### Configure Debug Plugin (Tracy Integration) Source: https://context7.com/contributte/apitte/llms.txt Enable the Tracy bar panel and URL suffixes for response inspection by configuring the DebugPlugin in your NEON configuration. ```neon api: plugins: Apitte\Debug\DI\DebugPlugin: debug: panel: %debugMode% negotiation: %debugMode% ``` -------------------------------- ### Configure Console Plugin Source: https://context7.com/contributte/apitte/llms.txt Enable the ConsolePlugin in your Neon configuration to use Apitte's console commands. ```neon api: plugins: Apitte\Console\DI\ConsolePlugin: ``` -------------------------------- ### Define Base API Controller with Path Prefix Source: https://context7.com/contributte/apitte/llms.txt Define a base controller that sets a common API root path using the #[Path] attribute. Controllers must implement IController. ```php namespace App\Api\V1\Controllers; use Apitte\Core\Annotation\Controller\Path; use Apitte\Core\UI\Controller\IController; #[Path("/api/v1")] abstract class BaseV1Controller implements IController {} ``` -------------------------------- ### Register Controller as a Service Source: https://context7.com/contributte/apitte/llms.txt Manually register a controller as a service in your NEON configuration. This makes the controller available for dependency injection. ```neon services: - App\Api\V1\Controllers\UsersController ``` -------------------------------- ### Configure Middlewares Plugin and API Extension Source: https://context7.com/contributte/apitte/llms.txt Configure the `MiddlewaresExtension` and `ApiExtension` in your NEON configuration. Enable specific middleware features like Tracy integration and autobasepath. ```neon extensions: middleware: Contributte\Middlewares\DI\MiddlewaresExtension api: Apitte\Core\DI\ApiExtension middleware: debug: %debugMode% api: plugins: Apitte\Middlewares\DI\MiddlewaresPlugin: tracy: true autobasepath: true services: corsMiddleware: factory: App\Api\Middleware\CorsMiddleware tags: [middleware: [priority: 10]] ``` -------------------------------- ### Standalone Apitte Application Bootstrap Source: https://context7.com/contributte/apitte/llms.txt Bootstrap a standalone Apitte API application. This entry point handles the full request lifecycle for the API. ```php require __DIR__ . '/../vendor/autoload.php'; Bootstrap::boot() ->createContainer() ->getByType(IApplication::class) ->run(); ``` -------------------------------- ### Console Commands Source: https://context7.com/contributte/apitte/llms.txt List all registered API endpoints from the CLI using `ConsolePlugin` and the `apitte:route:dump` command. ```APIDOC ## Console Commands **`ConsolePlugin` + `apitte:route:dump`** — list all registered API endpoints from the CLI. ```neon api: plugins: Apitte\Console\DI\ConsolePlugin: ``` ```bash # List all endpoints with paths, methods, and metadata php bin/console apitte:route:dump # Example output: # +--------+----------------------------+--------+ # | Method | Path | ID | # +--------+----------------------------+--------+ # | GET | /api/v1/users/ | # | GET | /api/v1/users/{id} | # | POST | /api/v1/users/ | # | GET | /openapi/ | # +--------+----------------------------+--------+ ``` ``` -------------------------------- ### Configure CoreDecoratorPlugin Source: https://context7.com/contributte/apitte/llms.txt Enable the CoreDecoratorPlugin to utilize request, response, and error decorators. This plugin acts as a central point for applying custom transformations. ```neon api: plugins: Apitte\Core\DI\Plugin\CoreDecoratorPlugin: ``` -------------------------------- ### Configure OpenAPI Plugin Source: https://context7.com/contributte/apitte/llms.txt Configure the OpenAPI plugin in your Neon configuration to automatically generate an OpenAPI 3.x schema. ```neon api: plugins: Apitte\OpenApi\DI\OpenApiPlugin: definition: openapi: "3.0.2" info: title: My API version: "1.0.0" swaggerUi: panel: %debugMode% expansion: list filter: true title: "My API v1" ``` -------------------------------- ### Configure Apitte DI Extension Source: https://context7.com/contributte/apitte/llms.txt Register the Apitte DI extension and configure basic settings like debugging and exception catching in your NEON configuration file. ```neon extensions: api: Apitte\Core\DI\ApiExtension api: debug: %debugMode% catchException: true ``` -------------------------------- ### Implement CORS Middleware Source: https://context7.com/contributte/apitte/llms.txt Implement the `IMiddleware` interface to handle Cross-Origin Resource Sharing. This middleware intercepts OPTIONS requests and adds necessary CORS headers to all responses. ```php // src/Api/Middleware/CorsMiddleware.php namespace App\Api\Middleware; use Contributte\Middlewares\IMiddleware; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; class CorsMiddleware implements IMiddleware { public function __invoke( ServerRequestInterface $request, ResponseInterface $response, callable $next, ): ResponseInterface { if ($request->getMethod() === 'OPTIONS') { return $response ->withHeader('Access-Control-Allow-Origin', '*') ->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS') ->withHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization') ->withStatus(204); } $response = $next($request, $response); return $response->withHeader('Access-Control-Allow-Origin', '*'); } } ``` -------------------------------- ### Implement Request Decorator for Authentication Source: https://context7.com/contributte/apitte/llms.txt Create an AuthDecorator implementing IRequestDecorator to intercept requests, validate tokens, and attach user information. It throws an EarlyReturnResponseException for unauthorized access. ```php // src/Api/Decorator/AuthDecorator.php namespace App\Api\Decorator; use Apitte\Core\Decorator\IRequestDecorator; use Apitte\Core\Exception\Runtime\EarlyReturnResponseException; use Apitte\Core\Http\ApiRequest; use Apitte\Core\Http\ApiResponse; use Apitte\Core\Http\RequestAttributes; use Apitte\Core\Schema\Endpoint; use GuzzleHttp\Psr7\Utils; class AuthDecorator implements IRequestDecorator { public function decorateRequest(ApiRequest $request, ApiResponse $response): ApiRequest { /** @var Endpoint $endpoint */ $endpoint = $request->getAttribute(RequestAttributes::ATTR_ENDPOINT); // Skip authentication for public endpoints tagged with 'public' if ($endpoint->hasTag('public')) { return $request; } $token = $request->getHeaderLine('Authorization'); if (!$this->isValidToken($token)) { $body = Utils::streamFor(json_encode([ 'status' => 'error', 'code' => 401, 'message' => 'Unauthorized. Invalid or missing token.', ])); throw new EarlyReturnResponseException( $response->withStatus(401)->withBody($body) ); } // Attach user info to request for downstream use return $request->withAttribute('user', $this->resolveUser($token)); } private function isValidToken(string $token): bool { /* ... */ return true; } private function resolveUser(string $token): array { /* ... */ return []; } } ``` -------------------------------- ### POST /api/v1/users/ and PUT /api/v1/users/ Source: https://context7.com/contributte/apitte/llms.txt Creates a new user or updates an existing one. Accepts user data in the request body. ```APIDOC ## POST /api/v1/users/ and PUT /api/v1/users/ ### Description Creates a new user or updates an existing one. Persists the provided user data. ### Method POST, PUT ### Endpoint /api/v1/users/ ### Parameters #### Query Parameters None #### Request Body - **field1** (type) - Description of the field (e.g., user data) ### Request Example ```json { "example": "request body" } ``` ### Response #### Success Response (201 Created) - **id** (integer) - The ID of the created or updated user. ### Response Example ```json { "id": 42 } ``` ``` -------------------------------- ### Dump API Routes Command Source: https://context7.com/contributte/apitte/llms.txt Use the 'apitte:route:dump' command to list all registered API endpoints from the CLI, including their methods, paths, and IDs. ```bash # List all endpoints with paths, methods, and metadata php bin/console apitte:route:dump # Example output: # +--------+----------------------------+--------+ # | Method | Path | ID | # +--------+----------------------------+--------+ # | GET | /api/v1/users/ | # | GET | /api/v1/users/{id} | # | POST | /api/v1/users/ | # | GET | /openapi/ | # +--------+----------------------------+--------+ ``` -------------------------------- ### Configure NegotiationPlugin in NEON Source: https://context7.com/contributte/apitte/llms.txt Configuration for the NegotiationPlugin in NEON format. Enables response unification, wrapping JSON responses in a {status,data} envelope. ```neon api: plugins: Apitte\Negotiation\DI\NegotiationPlugin: unification: true # wrap all JSON responses in {status,data} envelope ``` -------------------------------- ### Error Handling with ClientErrorException and ServerErrorException Source: https://context7.com/contributte/apitte/llms.txt Demonstrates how to throw typed API exceptions that are automatically serialized into structured JSON error responses using `ClientErrorException` and `ServerErrorException`. ```APIDOC ## GET /users/{id} ### Description Retrieves a user by their ID. Throws `ClientErrorException` if the user is not found, or `ServerErrorException` if an error occurs during processing. ### Method GET ### Endpoint /users/{id} ### Parameters #### Path Parameters - **id** (int) - Required - The ID of the user to retrieve. ### Request Example (No request body for GET request) ### Response #### Success Response (200) - **(object)** - The user data. #### Error Response (404) - **status** (string) - "error" - **code** (int) - 404 - **message** (string) - "User not found" #### Error Response (500) - **status** (string) - "error" - **code** (int) - 500 - **message** (string) - "Processing failed" - **context** (object) - Optional - Additional error details. ### Request Example ```php // Example of calling the detail method (internal representation) $this->detail($request, $response); ``` ### Response Example ```json // Default error response shape (SimpleErrorHandler) { "status": "error", "code": 404, "message": "User not found" } // Generic server error (non-ApiException) { "status": "error", "code": 500, "message": "Application encountered an internal error. Please try again later." } ``` ``` -------------------------------- ### OpenAPI Controller Implementation Source: https://context7.com/contributte/apitte/llms.txt Implement a controller to expose the OpenAPI JSON schema. This controller uses ISchemaBuilder to build the schema and returns it as a JSON response. ```php // src/Api/V1/Controllers/OpenApiController.php use Apitte\Core\Annotation\Controller\Method; use Apitte\Core\Annotation\Controller\Path; use Apitte\Core\Http\ApiRequest; use Apitte\Core\Http\ApiResponse; use Apitte\Core\UI\Controller\IController; use Apitte\OpenApi\ISchemaBuilder; #[Path("/openapi")] final class OpenApiController implements IController { public function __construct( private readonly ISchemaBuilder $schemaBuilder, ) {} // GET /openapi/ — returns the full OpenAPI JSON schema #[Path("/")] #[Method("GET")] public function index(ApiRequest $request, ApiResponse $response): ApiResponse { $schema = $this->schemaBuilder->build(); return $response->writeJsonBody($schema->toArray()); } } ``` -------------------------------- ### Register Custom Plugin in NEON Configuration Source: https://context7.com/contributte/apitte/llms.txt Register your custom plugin by specifying its class name under the 'plugins' key in your API configuration. ```neon api: plugins: App\Api\Plugin\RateLimitPlugin: ``` -------------------------------- ### Content Negotiation with NegotiationPlugin Source: https://context7.com/contributte/apitte/llms.txt Enables automatic serialization of response entities to the format requested via the `Accept` header or URL suffix (`.json`, `.xml`, `.csv`) using the `NegotiationPlugin` and `ArrayEntity`. ```APIDOC ## GET /users ### Description Retrieves a list of users. The response format (e.g., JSON, XML) is determined by the `Accept` header or URL suffix. ### Method GET ### Endpoint /users ### Parameters #### Query Parameters (None explicitly documented for this endpoint) #### Request Body (None for this endpoint) ### Request Example (No request body for GET request) ### Response #### Success Response (200) - **(Array of objects)** - A list of user objects, each containing 'id', 'firstName', and 'email'. ### Response Example ```json { "status": "success", "data": [ { "id": 1, "firstName": "John", "email": "john@doe.com" }, { "id": 2, "firstName": "Elon", "email": "elon@spacex.com" } ] } ``` ``` -------------------------------- ### Controller with Inline OpenAPI Annotations Source: https://context7.com/contributte/apitte/llms.txt Define OpenAPI schema details directly within controller annotations using the OpenApi attribute. This allows for inline specification of summaries, operation IDs, tags, and parameters. ```php // Controller with full OpenAPI annotation inline use Apitte\Core\Annotation\Controller\OpenApi; use Apitte\Core\Annotation\Controller\Method; use Apitte\Core\Annotation\Controller\Path; use Apitte\Core\Annotation\Controller\Response; use Apitte\Core\Annotation\Controller\Tag; use Apitte\Negotiation\Http\ArrayEntity; #[Path("/users")] #[Tag("User")] final class UserController extends BaseV1Controller { #[Path("/")] #[Method("GET")] #[Response(code: "200", description: "List of users")] #[Response(code: "401", description: "Unauthorized")] #[OpenApi(<<<'EOT' summary: List all users operationId: listUsers tags: - User parameters: - name: limit in: query required: false schema: type: integer EOT)] public function index(ApiRequest $request, ApiResponse $response): ApiResponse { return $response->withStatus(200)->withEntity(ArrayEntity::from([])); } } ``` -------------------------------- ### Register Decorators with Priority Source: https://context7.com/contributte/apitte/llms.txt Register custom decorators (e.g., AuthDecorator, CorsDecorator) as services and tag them with 'apitte.core.decorator', specifying their execution priority. ```neon services: decorator.auth: class: App\Api\Decorator\AuthDecorator tags: [apitte.core.decorator: [priority: 1]] decorator.response.cors: class: App\Api\Decorator\CorsDecorator tags: [apitte.core.decorator: [priority: 50]] ``` -------------------------------- ### Define Path and Query Parameters with #[RequestParameter] Source: https://context7.com/contributte/apitte/llms.txt Use the #[RequestParameter] attribute to validate and type-cast path, query, header, or cookie parameters. Type conversion failures result in an automatic HTTP 400 response. ```php namespace App\Api\V1\Controllers; use Apitte\Core\Annotation\Controller\Method; use Apitte\Core\Annotation\Controller\Path; use Apitte\Core\Annotation\Controller\RequestParameter; use Apitte\Core\Http\ApiRequest; use Apitte\Core\Http\ApiResponse; use Nette\Utils\Json; #[Path("/users")] class UsersController extends BaseV1Controller { // GET /api/v1/users/{id} — id is validated as integer #[Path("/{id}")] #[Method("GET")] #[RequestParameter(name: "id", type: "int", description: "User ID")] public function detail(ApiRequest $request, ApiResponse $response): ApiResponse { /** @var int $id — guaranteed to be an integer */ $id = $request->getParameter('id'); if ($id <= 0) { throw new \Apitte\Core\Exception\Api\ClientErrorException('Invalid ID', 400); } return $response->writeBody(Json::encode(['id' => $id, 'name' => 'John Doe'])); } // GET /api/v1/users?page=2&limit=20&active=true #[Path("/")] #[Method("GET")] #[RequestParameter(name: "page", type: "int", in: "query", required: false)] #[RequestParameter(name: "limit", type: "int", in: "query", required: false)] #[RequestParameter(name: "active", type: "bool", in: "query", required: false, allowEmpty: true)] #[RequestParameter(name: "sort", type: "string", in: "query", required: false, enum: ["asc", "desc"])] public function index(ApiRequest $request, ApiResponse $response): ApiResponse { $page = $request->getParameter('page') ?? 1; // int $limit = $request->getParameter('limit') ?? 10; // int $active = $request->getParameter('active'); // bool|null $sort = $request->getParameter('sort') ?? 'asc'; // string return $response->writeBody(Json::encode(compact('page', 'limit', 'active', 'sort'))); } } ``` -------------------------------- ### Define API Endpoints with Attributes Source: https://context7.com/contributte/apitte/llms.txt Define API endpoints using #[Path] and #[Method] attributes. The full URL is a concatenation of path attributes from the controller and method. ```php namespace App\Api\V1\Controllers; use Apitte\Core\Annotation\Controller\Method; use Apitte\Core\Annotation\Controller\Path; use Apitte\Core\Http\ApiRequest; use Apitte\Core\Http\ApiResponse; use Nette\Utils\Json; #[Path("/users")] class UsersController extends BaseV1Controller { // GET /api/v1/users/ #[Path("/")] #[Method("GET")] public function index(ApiRequest $request, ApiResponse $response): ApiResponse { return $response->writeBody(Json::encode([ ['id' => 1, 'firstName' => 'John', 'lastName' => 'Doe', 'email' => 'john@doe.com'], ['id' => 2, 'firstName' => 'Elon', 'lastName' => 'Musk', 'email' => 'elon@spacex.com'], ])); } // POST /api/v1/users/ and PUT /api/v1/users/ #[Path("/")] #[Method(["POST", "PUT"])] public function create(ApiRequest $request, ApiResponse $response): ApiResponse { $body = Json::decode((string) $request->getBody(), Json::FORCE_ARRAY); // ... persist $body return $response->withStatus(ApiResponse::S201_CREATED) ->writeBody(Json::encode(['id' => 42])); } } ``` -------------------------------- ### Implement Response Decorator for CORS Headers Source: https://context7.com/contributte/apitte/llms.txt Create a CorsDecorator implementing IResponseDecorator to add CORS headers to the response. This is useful for enabling cross-origin requests from web clients. ```php // src/Api/Decorator/CorsDecorator.php — Response decorator example namespace App\Api\Decorator; use Apitte\Core\Decorator\IResponseDecorator; use Apitte\Core\Http\ApiRequest; use Apitte\Core\Http\ApiResponse; class CorsDecorator implements IResponseDecorator { public function decorateResponse(ApiRequest $request, ApiResponse $response): ApiResponse { return $response ->withHeader('Access-Control-Allow-Origin', '*') ->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS'); } } ``` -------------------------------- ### File Response Source: https://context7.com/contributte/apitte/llms.txt Attach a file stream to a response with correct headers and cache-control directives using `FileResponseAdjuster::adjust()`. ```APIDOC ## File Response **`FileResponseAdjuster::adjust()`** — attach a file stream to a response with correct headers and cache-control directives. ```php use Apitte\Core\Adjuster\FileResponseAdjuster; use Apitte\Core\Annotation\Controller\Method; use Apitte\Core\Annotation\Controller\Path; use Apitte\Core\Http\ApiRequest; use Apitte\Core\Http\ApiResponse; use GuzzleHttp\Psr7\Utils; #[Path("/export")] #[Method("GET")] public function export(ApiRequest $request, ApiResponse $response): ApiResponse { $csvPath = $this->reportService->generateCsv(); $stream = Utils::streamFor(fopen($csvPath, 'rb')); // Sets Content-Disposition, Content-Type, disables caching return FileResponseAdjuster::adjust($response, $stream, 'report.csv', 'text/csv'); } ``` ``` -------------------------------- ### Configure SymfonyValidator with DI and Translation Source: https://context7.com/contributte/apitte/llms.txt Integrate SymfonyValidator for advanced validation using Symfony Constraints. Configure its factory, translator, and translation domain for DI-aware validation. ```neon services: symfonyValidator: factory: Apitte\Core\Mapping\Validator\SymfonyValidator setup: - setConstraintValidatorFactory(Contributte\Validator\ContainerConstraintValidatorFactory()) - setTranslator(@Contributte\Translation\Translator) - setTranslationDomain('validators') - setGroups(['input']) api: plugins: Apitte\Core\DI\Plugin\CoreMappingPlugin: request: validator: @symfonyValidator ``` -------------------------------- ### Configure Schema Validation Plugin in NEON Source: https://context7.com/contributte/apitte/llms.txt Register your custom validation class within the CoreSchemaPlugin configuration in your NEON file. ```neon api: plugins: Apitte\Core\DI\Plugin\CoreSchemaPlugin: validations: - App\Api\Validation\RequireTagValidation ``` -------------------------------- ### Debug API Responses with .debug Suffixes Source: https://context7.com/contributte/apitte/llms.txt Use .debug and .debugdata URL suffixes to dump the full response or just the entity data in the Tracy debugger. ```bash # Dump the full response (headers + body) in Tracy debugger curl http://example.com/api/v1/users.debug # Dump only the response entity data curl http://example.com/api/v1/users.debugdata ``` -------------------------------- ### OpenAPI Schema Generation Source: https://context7.com/contributte/apitte/llms.txt Automatically generate an OpenAPI 3.x schema from controller attributes and expose it via a dedicated endpoint using `OpenApiPlugin` and `ISchemaBuilder`. ```APIDOC ## OpenAPI Schema Generation **`OpenApiPlugin` + `ISchemaBuilder`** — auto-generate an OpenAPI 3.x schema from controller attributes and expose it via a dedicated endpoint. ```neon api: plugins: Apitte\OpenApi\DI\OpenApiPlugin: definition: openapi: "3.0.2" info: title: My API version: "1.0.0" swaggerUi: panel: %debugMode% expansion: list filter: true title: "My API v1" ``` ```php // src/Api/V1/Controllers/OpenApiController.php use Apitte\Core\Annotation\Controller\Method; use Apitte\Core\Annotation\Controller\Path; use Apitte\Core\Http\ApiRequest; use Apitte\Core\Http\ApiResponse; use Apitte\Core\UI\Controller\IController; use Apitte\OpenApi\ISchemaBuilder; #[Path("/openapi")] final class OpenApiController implements IController { public function __construct( private readonly ISchemaBuilder $schemaBuilder, ) {} // GET /openapi/ — returns the full OpenAPI JSON schema #[Path("/")] #[Method("GET")] public function index(ApiRequest $request, ApiResponse $response): ApiResponse { $schema = $this->schemaBuilder->build(); return $response->writeJsonBody($schema->toArray()); } } ``` ```php // Controller with full OpenAPI annotation inline use Apitte\Core\Annotation\Controller\OpenApi; use Apitte\Core\Annotation\Controller\Method; use Apitte\Core\Annotation\Controller\Path; use Apitte\Core\Annotation\Controller\Response; use Apitte\Core\Annotation\Controller\Tag; use Apitte\Negotiation\Http\ArrayEntity; #[Path("/users")] #[Tag("User")] final class UserController extends BaseV1Controller { #[Path("/")] #[Method("GET")] #[Response(code: "200", description: "List of users")] #[Response(code: "401", description: "Unauthorized")] #[OpenApi(<<<'EOT' summary: List all users operationId: listUsers tags: - User parameters: - name: limit in: query required: false schema: type: integer EOT)] public function index(ApiRequest $request, ApiResponse $response): ApiResponse { return $response->withStatus(200)->withEntity(ArrayEntity::from([])); } } ``` ``` -------------------------------- ### Configure BasicValidator for Requests Source: https://context7.com/contributte/apitte/llms.txt Use BasicValidator to check for @required docblock annotations in requests. This is a simple, built-in validator. ```neon api: plugins: Apitte\Core\DI\Plugin\CoreMappingPlugin: request: validator: Apitte\Core\Mapping\Validator\BasicValidator ``` -------------------------------- ### Implement Custom Parameter Type Mapper with ITypeMapper Source: https://context7.com/contributte/apitte/llms.txt Register custom parameter types for #[RequestParameter(type: "mytype")] by implementing the ITypeMapper interface. This allows for custom validation and normalization of parameter values. ```php // src/Api/Mapping/EmailTypeMapper.php use Apitte\Core\Mapping\Parameter\ITypeMapper; use Apitte\Core\Exception\Runtime\InvalidArgumentTypeException; class EmailTypeMapper implements ITypeMapper { public function normalize(mixed $value): { if (is_string($value) && filter_var($value, FILTER_VALIDATE_EMAIL)) { return strtolower($value); } throw new InvalidArgumentTypeException('email', 'Pass a valid email address.'); } } ``` ```neon api: plugins: Apitte\Core\DI\Plugin\CoreMappingPlugin: types: email: App\Api\Mapping\EmailTypeMapper ``` ```php // Usage in controller #[RequestParameter(name: "email", type: "email", in: "query")] public function byEmail(ApiRequest $request, ApiResponse $response): ApiResponse { $email = $request->getParameter('email'); // validated, lower-cased email string // ... } ``` -------------------------------- ### Implement Custom XML Response Transformer Source: https://context7.com/contributte/apitte/llms.txt Extends `AbstractTransformer` to convert API responses into XML format. Handles both successful responses and exceptions, transforming errors into a structured XML output. ```php // src/Api/Transformer/XmlTransformer.php namespace App\Api\Transformer; use Apitte\Core\Exception\ApiException; use Apitte\Core\Http\ApiRequest; use Apitte\Core\Http\ApiResponse; use Apitte\Core\Http\ResponseAttributes; use Apitte\Negotiation\Http\ArrayEntity; use Apitte\Negotiation\Transformer\AbstractTransformer; use Throwable; class XmlTransformer extends AbstractTransformer { public function transform(ApiRequest $request, ApiResponse $response, array $context = []): ApiResponse { if (isset($context['exception'])) { return $this->transformError($context['exception'], $request, $response); } return $this->transformResponse($request, $response); } protected function transformResponse(ApiRequest $request, ApiResponse $response): ApiResponse { $data = $this->getEntity($response)->getData(); $xml = $this->arrayToXml($data); $response->getBody()->write($xml); return $response->withHeader('Content-Type', 'application/xml'); } protected function transformError(Throwable $error, ApiRequest $request, ApiResponse $response): ApiResponse { [$code, $message] = $error instanceof ApiException ? [$error->getCode(), $error->getMessage()] : [500, 'Application encountered an internal error. Please try again later.']; return $response ->withStatus($code) ->withAttribute(ResponseAttributes::ATTR_ENTITY, ArrayEntity::from([ 'status' => 'error', 'message' => $message, ])); } private function arrayToXml(array $data): string { $xml = new \SimpleXMLElement(''); array_walk_recursive($data, [$xml, 'addChild']); return $xml->asXML(); } } ``` -------------------------------- ### File Response Adjustment Source: https://context7.com/contributte/apitte/llms.txt Use FileResponseAdjuster to attach a file stream to a response. This automatically sets the correct Content-Disposition, Content-Type headers, and disables caching. ```php use Apitte\Core\Adjuster\FileResponseAdjuster; use Apitte\Core\Annotation\Controller\Method; use Apitte\Core\Annotation\Controller\Path; use Apitte\Core\Http\ApiRequest; use Apitte\Core\Http\ApiResponse; use GuzzleHttp\Psr7\Utils; #[Path("/export")] #[Method("GET")] public function export(ApiRequest $request, ApiResponse $response): ApiResponse { $csvPath = $this->reportService->generateCsv(); $stream = Utils::streamFor(fopen($csvPath, 'rb')); // Sets Content-Disposition, Content-Type, disables caching return FileResponseAdjuster::adjust($response, $stream, 'report.csv', 'text/csv'); } ``` -------------------------------- ### Register Custom XML Response Transformer Source: https://context7.com/contributte/apitte/llms.txt Register a custom transformer for a specific URL suffix (e.g., 'xml'). Ensure the transformer class is correctly defined and tagged. ```neon services: - factory: App\Api\Transformer\XmlTransformer tags: [apitte.negotiator.transformer: [suffix: xml, fallback: false]] ``` -------------------------------- ### Router Request Attributes Source: https://context7.com/contributte/apitte/llms.txt Access matched endpoint metadata and raw route parameters inside any controller or decorator using `RequestAttributes` constants. ```APIDOC ## Router Request Attributes **`RequestAttributes` constants** — access matched endpoint metadata and raw route parameters inside any controller or decorator. ```php use Apitte\Core\Http\RequestAttributes; use Apitte\Core\Schema\Endpoint; use Apitte\Core\Schema\Schema; // Inside a controller method or decorator: /** @var Endpoint $endpoint — full schema of the matched endpoint */ $endpoint = $request->getAttribute(RequestAttributes::ATTR_ENDPOINT); // Check tags for conditional logic (e.g. skip auth for public routes) if ($endpoint->hasTag('public')) { return $request; } /** @var mixed[] $parameters — raw (pre-mapping) path + query params */ $parameters = $request->getAttribute(RequestAttributes::ATTR_PARAMETERS); /** @var mixed $routerMatch — all matched parts of the URL */ $routerMatch = $request->getAttribute(RequestAttributes::ATTR_ROUTER); ``` ``` -------------------------------- ### Entity with Symfony Constraints Source: https://context7.com/contributte/apitte/llms.txt Define an entity class extending BasicEntity and apply Symfony Validator constraints like NotBlank, Length, Email, and Range to its properties. ```php // Entity with Symfony constraints use Apitte\Core\Mapping\Request\BasicEntity; use Symfony\Component\Validator\Constraints as Assert; final class CreateUserEntity extends BasicEntity { #[Assert\NotBlank] #[Assert\Length(min: 2, max: 100)] public string $firstName; #[Assert\NotBlank] #[Assert\Email] public string $email; #[Assert\Range(min: 0, max: 150)] public ?int $age = null; } ``` -------------------------------- ### Default Error Response Shapes Source: https://context7.com/contributte/apitte/llms.txt Illustrates the default JSON error response structure produced by SimpleErrorHandler for client and server errors. ```json // Default error response shape (SimpleErrorHandler) { "status": "error", "code": 404, "message": "User not found" } ``` ```json // Generic server error (non-ApiException) { "status": "error", "code": 500, "message": "Application encountered an internal error. Please try again later." } ``` -------------------------------- ### Deserialize Request Body to Entity with #[RequestBody] Source: https://context7.com/contributte/apitte/llms.txt Use the #[RequestBody] attribute to automatically deserialize the HTTP request body into a typed entity object, accessible via $request->getEntity(). This simplifies data handling for POST and PUT requests. ```php // src/Api/Entity/Request/CreateUserEntity.php namespace App\Api\Entity\Request; use Apitte\Core\Mapping\Request\BasicEntity; final class CreateUserEntity extends BasicEntity { public string $firstName; public string $lastName; public string $email; public ?int $age = null; } ``` ```php // src/Api/V1/Controllers/UsersController.php use Apitte\Core\Annotation\Controller\Method; use Apitte\Core\Annotation\Controller\Path; use Apitte\Core\Annotation\Controller\RequestBody; use Apitte\Core\Http\ApiRequest; use Apitte\Core\Http\ApiResponse; use App\Api\Entity\Request\CreateUserEntity; use Nette\Utils\Json; #[Path("/")] #[Method("POST")] #[RequestBody(entity: CreateUserEntity::class)] public function create(ApiRequest $request, ApiResponse $response): ApiResponse { /** @var CreateUserEntity $entity */ $entity = $request->getEntity(); // $entity->firstName, $entity->email, etc. are populated from JSON body return $response ->withStatus(ApiResponse::S201_CREATED) ->writeBody(Json::encode(['created' => true, 'email' => $entity->email])); } ``` -------------------------------- ### Accessing Router Request Attributes Source: https://context7.com/contributte/apitte/llms.txt Access matched endpoint metadata and raw route parameters within controllers or decorators using RequestAttributes. This allows for conditional logic based on endpoint tags or access to raw parameters. ```php use Apitte\Core\Http\RequestAttributes; use Apitte\Core\Schema\Endpoint; use Apitte\Core\Schema\Schema; // Inside a controller method or decorator: /** @var Endpoint $endpoint — full schema of the matched endpoint */ $endpoint = $request->getAttribute(RequestAttributes::ATTR_ENDPOINT); // Check tags for conditional logic (e.g. skip auth for public routes) if ($endpoint->hasTag('public')) { return $request; } /** @var mixed[] $parameters — raw (pre-mapping) path + query params */ $parameters = $request->getAttribute(RequestAttributes::ATTR_PARAMETERS); /** @var mixed $routerMatch — all matched parts of the URL */ $routerMatch = $request->getAttribute(RequestAttributes::ATTR_ROUTER); ``` -------------------------------- ### Throw Typed API Exceptions in PHP Source: https://context7.com/contributte/apitte/llms.txt Use ClientErrorException and ServerErrorException to throw typed API exceptions. These are automatically serialized into structured JSON error responses by the default SimpleErrorHandler or PsrLogErrorHandler. ```php use Apitte\Core\Exception\Api\ClientErrorException; use Apitte\Core\Exception\Api\ServerErrorException; use Apitte\Core\Annotation\Controller\Method; use Apitte\Core\Annotation\Controller\Path; use Apitte\Core\Annotation\Controller\RequestParameter; use Apitte\Core\Http\ApiRequest; use Apitte\Core\Http\ApiResponse; use Nette\Utils\Json; #[Path("/{id}")] #[Method("GET")] #[RequestParameter(name: "id", type: "int")] public function detail(ApiRequest $request, ApiResponse $response): ApiResponse { $id = $request->getParameter('id'); $user = $this->userRepository->find($id); if ($user === null) { // Produces: {"status":"error","code":404,"message":"User not found"} throw new ClientErrorException('User not found', 404); } try { $data = $this->someService->process($user); } catch (\RuntimeException $e) { // Produces: {"status":"error","code":500,"message":"Processing failed","context":{"detail":"..."}} throw new ServerErrorException('Processing failed', 500, $e); } return $response->writeBody(Json::encode($data)); } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.