Custom API methods

Custom API methods

Any application class becomes an API service if it implements the Ufo\JsonRpcBundle\ApiMethod\Interfaces\IRpcService interface. The interface imposes no logic — it only tags the class with ufo.rpc.service, which the bundle uses to build the method map when the container is compiled.

Every public non-static method of such a class (except the constructor) becomes a JSON-RPC method named <class name>.<method>.

<?php

namespace App\Api;

use Ufo\JsonRpcBundle\ApiMethod\Interfaces\IRpcService;
use Ufo\RpcObject\RPC;

#[RPC\Info('Messenger')]
class MessengerApi implements IRpcService
{
    public function __construct(private Mailer $mailer) {}

    /**
     * A method for sending an email message
     * @param string $email The email address
     * @param string $text Message body
     * @param string $subject Optional message subject parameter
     */
    public function sendEmail(string $email, string $text, string $subject = 'Message without subject'): bool
    {
        return $this->mailer->send($email, $subject, $text);
    }
}

This example exposes the method Messenger.sendEmail:

Request

{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "Messenger.sendEmail",
    "params": {
        "email": "user@example.com",
        "text": "Hello!"
    }
}

Response

{
    "jsonrpc": "2.0",
    "id": 1,
    "result": true
}

Parameters can also be passed positionally: "params": ["user@example.com", "Hello!"].

The method map is built when the container is compiled. After adding or changing methods in prod, run bin/console cache:clear.

Naming

Without the attribute, the class name in the API is the short class name (MessengerApi.sendEmail). The #[RPC\Info] attribute sets an alias (Messenger.sendEmail), the separator character and the API version.

Good practice

Treat the class name as a namespace and the method name as an action: Cart.addProduct, Cart.getProducts, Messenger.sendEmail, Messenger.sendSms. This way, methods with the same name in different classes do not conflict (Product.create, User.create), and the names make it clear what the server can do.

Describing methods and parameters

The documentation is built from everything known about the method: names, native types, default values and docblocks. The description of the method and its parameters is taken from PHPDoc, and the exact type comes from @param / @return when the native type is not enough:

/**
 * Creates an order
 * @param OrderItemDto[] $items
 * @return array<string, OrderItemDto>
 */
public function create(array $items, ?string $comment = null): array

A parameter with a null default or any other default value is optional. Types are covered in detail on the Method parameters page, the result in Response format.

The method's name (summary) and description in the documentation can be translated without changing the code — see API documentation.

Deprecated methods

A method with @deprecated in its docblock or with the #[\Deprecated] attribute is marked in OpenRPC as "deprecated": true, but keeps working.

/**
 * @deprecated use Orders.create
 */
public function createOrder(array $items): array

Hiding a method from the API

Mark a public method that must not be an API method with #[RPC\IgnoreApi]: it appears neither in the method map nor in the documentation.

#[RPC\IgnoreApi]
public function rebuildIndex(): void

Services in method arguments

Besides constructor injection, a service can be received directly in a method argument via #[Autowire]. Such an argument is not an RPC parameter: it is not in the documentation, and the client does not pass it.

use Symfony\Component\DependencyInjection\Attribute\Autowire;

public function report(int $id, #[Autowire(service: 'app.report_builder')] ReportBuilder $builder): array

In versions 10.0.1–10.3.0, #[Autowire] in method arguments did not work (fixed in 10.3.1). On these versions, use the constructor.

RPC attributes

Attribute Where Purpose
#[RPC\Info] class Alias, separator, API version, removed methods
#[RPC\APIGroups] class, method Which API groups the method belongs to
#[RPC\Assertions] parameter Validation with Symfony constraints
#[RPC\Param] parameter Explicit conversion of a value into an object
#[RPC\DTO] method, property DTO format for a result or parameter
#[RPC\Cache] method Response caching
#[RPC\CacheRelation] method Invalidation of related cache
#[RPC\Lock] method Preventing concurrent calls
#[RPC\IgnoreApi] method Remove the method from the API

All attributes come from the ufo-tech/rpc-objects package (namespace Ufo\RpcObject\RPC).