Bundle configuration

Bundle configuration

This page describes version 10.x. Configuration for earlier versions is available in the version selector at the top of the page.

All configuration lives in config/packages/ufo_json_rpc.yaml. No section is required: if a section is omitted, the default values apply.

# config/packages/ufo_json_rpc.yaml
ufo_json_rpc:
    batch:
        enabled: true
        process: true
        process_count: 2

    notification:
        process: false
        process_count: 2

    translations:
        paths: []

    cache:
        ttl: 60
        prefix: 'rpc_cache'
        # pool: 'cache.app'

    security:
        protected_api: true
        protected_doc: false
        token_name: 'Ufo-RPC-Token'
        clients_tokens:
            - '%env(resolve:UFO_API_TOKEN)%'
        # providers: ...

    docs:
        project_name: 'My Project'
        project_description: ''
        project_version: null
        async_dsn_info: false
        validations:
            symfony_asserts: false

    async: []

The security block

Protects the API with an access key (token) passed in an HTTP header.

Parameter Type Default Description
protected_api bool true Check the token for RPC requests (HTTP, CLI, async)
protected_doc bool false Check the token for documentation GET requests
token_name string Ufo-RPC-Token Name of the header carrying the token. In the console, it is the --<token_name in lowercase> option
clients_tokens string[] [] List of valid tokens for the base validator
providers map — Separate validators for API groups
ufo_json_rpc:
    security:
        protected_api: true
        protected_doc: false
        token_name: 'Ufo-RPC-Token'
        clients_tokens:
            - '%env(resolve:UFO_API_TOKEN)%'
            - '%env(resolve:UFO_API_TOKEN_OLD)%'   # multiple keys, for rotation
HTTP header naming requirements

HTTP header names are not strictly regulated with respect to capitalization, because HTTP headers are case-insensitive. However, there are some common practices and standards that are usually followed for better readability and consistency:

  • Capitalization: HTTP header names are usually written in CamelCase, where each word starts with a capital letter, e.g. Content-Type, User-Agent, Accept-Encoding. This does not affect how headers are processed technically, but makes them easier to read.
  • Uniqueness: Each header must have a unique name within a single HTTP request or response. The same name cannot be used for different headers in the same request or response.
  • Special headers: Some headers are used specifically to control caching behavior (Cache-Control), security (Strict-Transport-Security), authentication (Authorization), etc.
  • RFC norms: Header requirements are governed by RFC documents, which define the standards for Internet protocols. For example, general headers and their usage are described in RFC 7231.
Do not store tokens in code

Pass tokens via environment variables (.env.local, Symfony secrets) instead of hardcoding them in YAML.

A missing token returns error -32401 (HTTP 401), an invalid one returns -32403.

Custom token validator

If a token list does not fit your case (for example, the token is a user key stored in the database), implement Ufo\JsonRpcBundle\Security\Interfaces\ITokenValidator and register it as the base validator:

use Ufo\JsonRpcBundle\Security\Interfaces\ITokenValidator;
use Ufo\RpcError\RpcInvalidTokenException;

final class UserTokenValidator implements ITokenValidator
{
    public function __construct(private ApiKeyRepository $keys) {}

    public function isValid(string $token): true
    {
        if (!$this->keys->isActive($token)) {
            throw new RpcInvalidTokenException();
        }

        return true;
    }
}
# config/services.yaml
services:
    Ufo\JsonRpcBundle\Security\Interfaces\ITokenValidator:
        alias: App\Security\UserTokenValidator

The validator checks all protected entry points that have no dedicated provider assigned, including the documentation when protected_doc: true.

Providers for API groups (providers)

Each provider sets exactly one of service (a service implementing ITokenValidator) or clients_tokens (a separate list of keys), plus a non-empty api_groups:

ufo_json_rpc:
    security:
        clients_tokens:
            - '%env(resolve:UFO_API_TOKEN)%'
        providers:
            internal:
                api_groups: [internal]
                clients_tokens:
                    - '%env(resolve:UFO_INTERNAL_API_TOKEN)%'
            public:
                service: App\Security\UserTokenValidator
                api_groups: [public]

Groups without a provider are checked by the base validator. See API groups for details.

The docs block

Parameters of the OpenRPC document.

Parameter Default Description
project_name My Project API name in the documentation and the Postman collection
project_description '' API description
project_version null Version of your project (not to be confused with API versions)
async_dsn_info false Show the parameters of asynchronous transports in the documentation (x-ufo.transport)
validations.symfony_asserts false Add the list of Symfony constraints to parameters (x-ufo-assertions)

The cache block

Response cache for methods with #[RPC\Cache] and the documentation cache.

Parameter Default Description
ttl 60 Default entry lifetime, in seconds
prefix rpc_cache Cache namespace
pool null Cache pool service to use instead of the local file pool rpc.cache.pool
ufo_json_rpc:
    cache:
        pool: 'cache.app'   # e.g. a shared Redis

ttl and prefix apply only to the default pool. On multiple servers, use a shared pool so that #[RPC\CacheRelation] invalidation works on all of them.

The batch block

Processing of batch requests.

Parameter Default Description
enabled true false: batch requests are rejected with a single -32600 error
process true true: each item runs in its own ufo:rpc:process child process (isolation and parallelism); false: items run one by one in the current process (inline)
process_count 2 How many child processes run concurrently (1–10)

For short methods, inline mode (process: false) is roughly 250 times faster than process mode.

The notification block

Execution of notifications and $rpc.callback.

Parameter Default Description
process false true: run in a detached ufo:rpc:process process instead of the HTTP worker
process_count 2 Detached processes running concurrently (1–10); when all are busy, execution happens inline

Enable notification.process only for rare long-running notifications: each detached process boots the Symfony kernel (~200–300 ms CPU), so under load this is slower than regular execution.

The translations block

Translations of method names and descriptions for the documentation live in the application's translations/rpc.<locale>.yaml. paths adds directories with the same structure, for example from extensions:

ufo_json_rpc:
    translations:
        paths:
            - '%kernel.project_dir%/vendor/acme/rpc-extension/translations'

See API documentation for details.

The async block

List of asynchronous transports. Each item has a type and an arbitrary config (name, dsn and additional keys):

ufo_json_rpc:
    async:
        - type: 'amqp'
          config:
              name: 'rpc_async'
              dsn: '%env(resolve:RPC_TRANSPORT_DSN)%'
        - type: 'mercure'
          config:
              name: 'rpc_socket'
              dsn: '%env(resolve:MERCURE_PUBLIC_URL)%'
              topics_prefix: 'rpc.event.'

Items with type: amqp are registered as Symfony Messenger transports named name. See Asynchronous processing for details.