For developers

← For developers

Ashterix

docs: rewrite JsonRpcBundle docs, add SDK docs, move them under docs/api

en/docs/api/json-rpc-bundle/developers/README.md new file +122 āˆ’0

1 + # For developers
2 +
3 + JsonRpcBundle is not a closed "all-in-one" product but a core on which you can build your own solutions. It covers the foundation of any RPC API: receiving requests, the method map, validation, data conversion, security, caching and documentation. Everything beyond that — new transports, protocols, integrations, tools — is meant to be an extension.
4 +
5 + This section is for those who want to understand how the bundle works internally and extend it: in their own project or as a separate package for the community.
6 +
7 + ## Principles the bundle is built on
8 +
9 + **Code is the single source of truth.** API methods are ordinary PHP methods. Names, types, PHPDoc, attributes and validation constraints define both behavior and documentation at once, so the two never diverge.
10 +
11 + **Minimal configuration.** Implementing `IRpcService` is enough — the bundle derives the rest itself: the method map, parameter and result schemas, type conversion. Configuration is needed only where there is a real choice.
12 +
13 + **Transport independence.** A procedure does not know where the request came from: HTTP, the console, a message queue, REST or MCP via an extension. Validation, security, caching and events are the same for all.
14 +
15 + **Open for extension.** Key processing stages are events, and the main mechanisms are interfaces with autoconfiguration: parameter converters, schema generators, method sources, batch strategies, token validators, route loaders. New behavior is added without changing the core.
16 +
17 + **Native Symfony mechanisms.** DI container, EventDispatcher, Messenger, Lock, Validator, Cache, Translation — the bundle does not reinvent its own counterparts, so extensions are written in the usual Symfony ways.
18 +
19 + **Standards compliance.** The protocol is [JSON-RPC 2.0](https://www.jsonrpc.org/specification), the documentation is [OpenRPC](https://spec.open-rpc.org/). Clients and tools that support these standards work with the API without adaptation.
20 +
21 + **Performance without sacrifices.** The method map and metadata are built at container compile time, documentation is cached, and expensive work is moved to after the response has been sent.
22 +
23 + ## What is in this section
24 +
25 + * [Lifecycle](/docs/api/json-rpc-bundle/developers/lifecycle) — how a request passes through the bundle for each entry point and at which moment each event is fired.
26 + * [Extensions](/docs/api/json-rpc-bundle/developers/extensions) — a list of available extensions.
27 + * [Lifecycle events](/docs/api/json-rpc-bundle/functionality/events) — each event in detail, with listener examples.
28 + * [Rules for AI assistants](/docs/api/json-rpc-bundle/developers/ai) — instruction files for Claude Code, Codex, Cursor, Copilot that plug into your existing rules.
29 + * [Known issues](/docs/api/json-rpc-bundle/developers/known_issues/open) — what does not work as expected and how to work around it.
30 +
31 + ## General guidelines for extensions
32 +
33 + 1. **Do not duplicate the core.** An extension should not have its own method map, validation or error format. Methods come from `ServiceMap` / `IServiceHolder`, execution goes through `RpcExecutor`, errors through `rpc.error`. Then `#[RPC\Assertions]`, `#[RPC\Cache]`, `#[RPC\Lock]`, converters and events work for your transport automatically.
34 + 2. **Respect API groups and versions.** Determine the [group](/docs/api/json-rpc-bundle/functionality/groups) from the route or configuration and write it to `ApiGroupContext`; pass the version in `RpcRequest`.
35 + 3. **Check the token yourself.** `RpcExecutor` does not check the token. Use `IRpcSecurity` with an appropriate token holder — this way the same `security.providers` apply as for the main API.
36 + 4. **Register routes via the core import.** Then each import for a separate group gets unique route names.
37 + 5. **Core dependency — `ufo-tech/json-rpc-bundle: ^10.4.2`** or newer, if you use `RpcExecutor` and `IApiRouteImport`.
38 + 6. **Components created at compile time** (`IServiceFiller`, `IConstraintGenerator`, `IParamConvertor`) are instantiated by the bundle itself while building the container. Their constructors may accept only services available at compile time or tagged collections — not scalar parameters.
39 +
40 + ## Extension points
41 +
42 + | What | How | What for |
43 + | --- | --- | --- |
44 + | `Routing\IApiRouteImport` | autoconfiguration, tag `ufo.rpc.route_import` | Load the extension's `router.yaml` separately for each API group: `getResource()`, `getRouteNamePrefixAttribute()` |
45 + | `Routing\RpcYamlFileLoader` / `ApiYamlFileLoader` | inheritance, tag `routing.loader` | Older way of writing a custom route loader |
46 + | `Server\RpcExecutor` | autowiring | `execute(IRpcRequestCarrier): RpcResponse` — execute a request through the full core cycle |
47 + | `Server\RequestPrepare\IRpcRequestCarrier` / `IRpcHttpCarrier` | implementation | Wrapper for your incoming request. `IRpcHttpCarrier::getHttpRequest()` binds `rpc.post_response` to the HTTP request and enables detached execution of notifications |
48 + | `Server\ServiceMap\IServiceHolder` | tag `rpc.service_holder` (manual) | Custom method source or a filter on top of `ServiceMap` |
49 + | `Security\Interfaces\IRpcSecurity`, `IRpcTokenHolder` | alias / implementation | Token check from your source (header, parameter, message) |
50 + | `Security\Interfaces\ITokenValidator` | alias or `security.providers` | Custom token validation |
51 + | `ParamConvertors\IParamConvertor` | autoconfiguration, tag `rpc.param_convertor` | Conversion of custom parameter and result types |
52 + | `...\Reflections\Fillers\AbstractServiceFiller` | autoconfiguration, tag `ufo-rpc.service-iterator` | Reading custom method attributes into `Service` metadata |
53 + | `...\Generate\Interfaces\IConstraintGenerator` | tag `rpc.constraint` | JSON Schema for custom validation constraints |
54 + | `Server\Batch\IBatchStrategy` | autoconfiguration, tag `rpc.batch_strategy` | Custom batch execution strategy |
55 + | `DocAdapters\Outputs\AbstractPostmanAdapter` | inheritance | Postman collection for your transport |
56 + | `Server\RpcLocaleNegotiator` | autowiring | Language selection from `Accept-Language` for custom documentation |
57 + | `translations.paths` | `prependExtensionConfig()` | Translations of method names and descriptions shipped by the extension |
58 + | [Events](/docs/api/json-rpc-bundle/functionality/events) | `#[AsEventListener]` | Behavior around method execution |
59 +
60 + ### Minimal adapter
61 +
62 + ```php
63 + // extension routes for each API group
64 + final class AcmeRouteImport implements IApiRouteImport
65 + {
66 + public function getResource(): string { return __DIR__.'/../../config/router.yaml'; }
67 + public function getRouteNamePrefixAttribute(): string { return '_acme_name_prefix'; }
68 + }
69 +
70 + // extension controller
71 + final class AcmeController
72 + {
73 + public function __construct(
74 + private RpcExecutor $executor,
75 + private IRpcSecurity $security,
76 + ) {}
77 +
78 + public function __invoke(Request $request): JsonResponse
79 + {
80 + $this->security->setTokenHolder(new HttpTokenHolder(/* ... */));
81 + $this->security->isValidApiRequest();
82 +
83 + $response = $this->executor->execute(new RpcFromAcme($request));
84 +
85 + return new JsonResponse(/* serialized $response */);
86 + }
87 + }
88 + ```
89 +
90 + ```php
91 + // translations shipped by the extension
92 + public function prepend(ContainerBuilder $container): void
93 + {
94 + $container->prependExtensionConfig('ufo_json_rpc', [
95 + 'translations' => ['paths' => [__DIR__.'/../../translations']],
96 + ]);
97 + }
98 + ```
99 +
100 + Working examples are in the code of `mcp-bundle` and `rest-bundle` (see [Extensions](/docs/api/json-rpc-bundle/developers/extensions)).
101 +
102 + ## Growing the ecosystem
103 +
104 + We strongly encourage the growth of the ecosystem around the bundle. Every new transport, adapter or tool makes JSON-RPC on Symfony more convenient for everyone.
105 +
106 + **Write extensions.** If you are missing an integration — with a message broker, a protocol, a monitoring system or a client generator — package it separately following the general guidelines above. Tell us about it, and we will add it to the [list of extensions](/docs/api/json-rpc-bundle/developers/extensions).
107 +
108 + **Report problems.** Found a bug or a mismatch with the documentation? Create an issue in the [bundle repository](https://github.com/UFO-Tech/json-rpc-bundle/issues). We describe confirmed problems in the [Known issues](/docs/api/json-rpc-bundle/developers/known_issues/open) section.
109 +
110 + **Propose changes to the core.** If an extension lacks an extension point, that is a signal it should be added to the core. The contribution process is described in [CONTRIBUTING.md](https://github.com/UFO-Tech/json-rpc-bundle/blob/main/CONTRIBUTING.md):
111 +
112 + 1. Fork the repository and create a separate branch.
113 + 2. Before starting, check the issue tracker; if there is no discussion yet, create an issue with your proposal.
114 + 3. Follow the project's code style.
115 + 4. Add tests for new features; all existing tests must pass.
116 + 5. Describe in the pull request what problem is solved or what is added, and be ready for discussion.
117 +
118 + By participating in the project, you agree to follow the [code of conduct](https://github.com/UFO-Tech/json-rpc-bundle/blob/main/CODE_OF_CONDUCT.md).
119 +
120 + :::info
121 + Report security vulnerabilities not through public issues but via the contact in the repository's [SECURITY.md](https://github.com/UFO-Tech/json-rpc-bundle/blob/main/SECURITY.md).
122 + :::