Skip to content

Custom endpoints

Applications and modules add endpoints for everything that is not a record of a resource, e.g. reports or imports. Authentication, rate limits, logging and the OpenAPI description come from Anvil.

use Anvil\Api\ApiEndpoint;
use Anvil\Api\ApiEndpoints;
use Anvil\Api\ApiRequest;
use Anvil\Api\ApiResponse;
final class ReportApi implements ApiEndpoints
{
public function endpoints(): array
{
return [
ApiEndpoint::get('reports/{uuid}')
->permission('reports.view')
->summary('One report')
->handle(fn (ApiRequest $request): ApiResponse => ApiResponse::ok(service('reports')->summary($request->parameter('uuid')))),
ApiEndpoint::post('reports/{uuid}/comments')
->permission('reports.comment')
->rules(['text' => ['required', 'string', 'max_length[2000]']])
->idempotent()
->handle(function (ApiRequest $request): ApiResponse {
$uuid = service('reports')->comment($request->parameter('uuid'), $request->string('text'));
return ApiResponse::created(['uuid' => (string) $uuid]);
}),
];
}
}
  • Paths are relative to /api/v1/. {uuid} matches a UUID, other parameters ({serial}) one segment that the rules check.
  • Every endpoint needs a permission. Add own permissions to $permissions so they can be granted in the role editor and to clients.
  • Only fields with a rule reach the handler (path parameters, query and body). Invalid input is a 422.
  • Writes (POST, PUT, PATCH, DELETE) run in a transaction and are audited (api.request). The body must be a JSON object of at most 1 MB.
  • Answers: ApiResponse::ok($data), created($data, $location), page($list, $next), noContent(), withHeaders([...]); a handler may return an array, which is answered with 200. Throw ApiException for errors (notFound(), conflict(), validation([...]), …). Other exceptions are a generic 500 and logged with the request id.
  • $request->client is the calling client (ApiPrincipal): tenant, UUID, name, permissions. The tenant context, the gate and the audit actor are set to the client while the handler runs.
  • rawInput() hands the unvalidated query and body to the handler ($request->query, $request->body), e.g. when the rules depend on the record. Validate them yourself.
  • The first endpoint of a path and method answers: custom endpoints come before resource endpoints.