Symfony Json Rpc Bundle Fast way to create json rpc microservice, using symfony messenger and laminas json server
- Json Rpc 2.0
- Batch Support
"php": ">=8.0",
"ext-json": "*",
"symfony/framework-bundle": "^5.3" || ^6.0,
"laminas/laminas-json-server": "^3.1"
This package is installable and autoloadable via Composer
composer require insidestyles/json-rpc-bundleAdd package config: json_rpc_api.yaml
json_rpc_api:
handlers:
main:
path: /api
host: ~
serializer: ~
context: ~
logger: ~
error_handler: ~Add Bundle to App
Insidestyles\JsonRpcBundle\JsonRpcBundle::class => ['all' => true],Add route config: json_rpc_api_routing.yaml
_json_rpc_api:
resource: .
type: json_rpc_api
- Add Example HelloWorldApi
Update api interface with JsonRpcApi Annotation
@JsonRpcApi(namespace = "main")
<?php
/**
* @JsonRpcApi(namespace = "main")
*
* @author Fuong <insidestyles@gmail.com>
*/
interface HelloWordJsonRpcApiInterface extends JsonRpcApiInterface
{
public function helloWorld(string $name);
}
- HelloWorld Api with Symfony Messenger:
Add requirement:
composer require symfony/messengerConfig Symfony Messenger https://symfony.com/doc/current/messenger.html
services:
hello_world_api:
class: Insidestyles\JsonRpcBundle\Api\MessageBus\HelloWorldApi
arguments:
- "@messenger.bus.default"
tags:
- {name: json_rpc_api, handler: main}
hello_world_api_handler:
class: Insidestyles\JsonRpcBundle\Handler\HelloWorldHandler
tags:
- {name: messenger.message_handler}
- Add extra api endpoint with jms serializer and default symfony logger
json_rpc_api:
handlers:
main:
path: /api
host: ~
serializer: ~
context: ~
logger: ~
error_handler: ~
auth:
path: /auth
host: ~
serializer: json_rpc_api.serialzier.jms
context: ~
logger: monolog.logger
error_handler: ~- Add custom serializer adapter
composer require jms/serializer-bundleservices:
json_rpc_api.serialzier.custom:
class: Insidestyles\JsonRpcBundle\Server\Adapter\Serializer\CustomSerializer
arguments:
- "@serializer"- Add custom serializer adapter
services:
Insidestyles\\Core\\Exceptions\\ApiErrorHandler:
class: 'Insidestyles\Core\Exceptions\ApiErrorHandler'
tags:
- { name: 'json_rpc_api.error_handler' }<?php
namespace Insidestyles\Core\Exceptions;
use Doctrine\ORM\EntityNotFoundException;
use Insidestyles\JsonRpcBundle\Server\ErrorHandler\Handler\ErrorHandlerInterface;
use Throwable;
class ApiErrorHandler implements ErrorHandlerInterface
{
private const SYSTEM_ERRORS = [
EntityNotFoundException::class,
];
public function parse(Throwable $e): array
{
if (!$this->isSupported($e)) {
return [];
}
return [
'code' => $e->getCode(),
'message' => $e->getMessage(),
'data' => [],
];
}
public function isSupported(Throwable $e): bool
{
return $e instanceof IsExceptionInterface || in_array(get_class($e), static::SYSTEM_ERRORS);
}
}- Enable jms serializer
json_rpc_api:
handlers:
main:
path: /api
host: ~
serializer: json_rpc_api.serializer.jms
context: json_rpc_api.serializer.default_context
logger: ~
error_handler: ~
- Install Symfony Validator and validation middle ware from messenger bus for message validation
framework:
messenger:
buses:
command_bus:
middleware:
- messenger.middleware.validation<?php
namespace Insidestyles\JsonRpcBundle\Message;
use Symfony\Component\Validator\Constraints as Assert;
/**
* @author Fuong <insidestyles@gmail.com>
*/
class HelloWorldMessage
{
/**
* @Assert\NotBlank()
* @var string
*/
private $message;
public function __construct(string $message)
{
$this->message = $message;
}
/**
* @return string
*/
public function getMessage(): string
{
return $this->message;
}
}-
You don't have to extend class AbstractApi if you don't want to use Symfony Messenger. Just implement your own interface that extends JsonRpcApiInterface. See example HelloWorldApi
-
Go to api endpoint http:://localhost/api to see json-rpc methods
-
simple request content
{
"id": 1,
"method": "main.helloWorld",
"params": {
"name": "test"
}
}- batch request content
[
{
"id": 1,
"method": "main.helloWorld",
"params": {
"name": "hello1"
}
},
{
"id": 2,
"method": "main.helloWorld",
"params": {
"name": "hello2"
}
}
]- using remote service with other api endpoints
Add requirement:
composer ocramius/proxy-managerUpdate service
hello.remote_services.hello:
tags:
- name: 'json_rpc_remote_service'
url: '%hello.api_server_url%'
class: Insidestyles\JsonRpcBundle\Sdk\Contract\HelloWordJsonRpcApiInterface
Now you can call api
$container->get('hello.remote_services.hello')->helloWorld('Hi');