For developers
REST Bundle is an adapter over the JsonRpcBundle core: it has no method map, validation or security of its own, it only turns an HTTP request into an RPC call and the result into a REST response. This section is for those who want to understand how it works inside or extend it.
Principles
- The RPC method is the only implementation. REST is one more transport for the same method: no REST controller per method, no second set of DTOs or separate validation.
- Explicit publishing. A method appears in REST only with the Symfony
#[Route]attribute.#[RPC\IgnoreApi]and#[RPC\APIGroups]work the same way as in RPC. - Execution belongs to the RPC core. The bundle builds an
RpcRequestand executes it throughRpcExecutor; parameter conversion,#[RPC\Assertions],#[RPC\Cache],#[RPC\Lock]and therpc.*events behave as for a JSON-RPC call. - Groups and versions come from the RPC service map. Each API group and version has its own REST routes and documents; inherited and removed methods are defined by
#[RPC\Info(version:, removedMethods:)]. - HTTP semantics at the edge. The REST layer is responsible only for what JSON-RPC lacks: paths, HTTP methods, statuses (
404,405,401, …) and the RFC 9457 error format. - Documentation from the same metadata. OpenAPI and Postman are generated from the service map, so they do not diverge from the code.
Components
| Component | Role |
|---|---|
Services\RestServiceMap |
Reads the RPC service map of the current group and builds REST routes from #[Route] attributes (class + method) for each version |
Services\RpcRestRouteLoader |
Route loader of type ufo_rest_api: a route per method and version plus a _method_not_allowed fallback |
Routing\RestRouteImport |
Registers config/router.yaml with the JsonRpcBundle route loader (IApiRouteImport) |
EventDispatcher\RestControllerListener |
After routing, sets the group in ApiGroupContext and directs REST routes to the controller |
EventDispatcher\RestDocLocaleListener |
Documentation language from Accept-Language via RpcLocaleNegotiator |
Services\RestRequestParser |
Finds the route and RPC service, checks the HTTP method and token, creates the carrier |
Services\RpcFromRest |
Carrier (IRpcHttpCarrier): builds an RpcRequest from the path, query, form and body, casts strings to parameter types |
Controller\RestEntryPointController |
Executes the call through RpcExecutor, returns the result or a problem; serves OpenAPI and Postman |
Exceptions\RestErrorNormalizer, Rest*Exception |
Conversion of RPC errors to REST exceptions with HTTP statuses |
DocAdapters\Outputs\OpenApiAdapter, PostmanAdapter |
Build documents from RestServiceMap |
Two phases
Container and route compilation
RPC procedures ──► JsonRpcBundle ServiceMap (for each group and version)
│
config/routes/*.yaml ──► JsonRpcBundle loader ── RestRouteImport recognises config/router.yaml
│ sets the import's group, prefixes the names: <group>_<name>
▼
config/router.yaml
├─ controller routes: documentation (/, /postman, /{ver}, /{ver}/postman), catch-all entry point
└─ ufo_rest_api resource ──► RpcRestRouteLoader ──► RestServiceMap ──► routes from #[Route]
Request: see Lifecycle.
What the bundle does not do
- It has no configuration tree of its own: everything is in
ufo_json_rpc. - It does not support batch, notifications and
$rpc.callbackvia REST. - It does not cache documents with
ETag.
In this section
- Lifecycle: how a REST request passes through the bundle and the RPC core.
- Extensions: events, replacing the parser, reading the REST map, other adapters.
- Testing: running and structure of the bundle's tests.
- Known issues: what does not work as expected and how to work around it.
General guidelines for core extensions are on the JsonRpcBundle For developers page.
Contributing
Found a bug or a mismatch with the documentation? Open an issue in the bundle repository. For behaviour changes, add a functional test: routes, statuses, problem documents and documentation are the REST contract.