Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Symfony Jsonrpc Http Server Laravel Package

yoanm/symfony-jsonrpc-http-server

Symfony integration for yoanm/jsonrpc-server that exposes JSON-RPC 2.0 APIs over HTTP. Configure the server, register method handlers as Symfony services, and handle JSON-RPC requests through the framework.

View on GitHub
Deep Wiki
Context7

Getting Started

This package is the HTTP transport layer of the yoanm JSON-RPC stack: it takes an HTTP request whose body is a JSON-RPC 2.0 payload, hands it to the core server SDK (yoanm/jsonrpc-server-sdk), and turns the resulting JSON-RPC response back into a PSR-7 HTTP response. It does not define your methods, validate params, or authenticate anything — that is the job of your handlers and the sibling packages (yoanm/symfony-jsonrpc-params-validator, etc.).

Minimal path:

composer require yoanm/symfony-jsonrpc-http-server
# you also need a PSR-7 / PSR-17 implementation
composer require nyholm/psr7

Register the bundle manually (there is no Flex recipe for a package this small):

// config/bundles.php
Yoanm\SymfonyJsonRpcHttpServerBundle\JsonRpcHttpServerBundle::class => ['all' => true],

Where to look first, in this order:

  1. The README's config block — it is short and is the only authoritative reference.
  2. vendor/yoanm/symfony-jsonrpc-http-server/src/DependencyInjection/Configuration.php — the real config tree. Key names change between minor versions; read this file rather than trusting blog posts.
  3. vendor/yoanm/jsonrpc-server-sdk/src/ — the Server, method resolution, and error formatting live here, not in this package.
  4. The registered route. If the bundle ships a controller/route, bin/console debug:router | grep -i json tells you the endpoint path immediately.

First use case — a single POST /json-rpc endpoint:

# config/packages/json_rpc_http_server.yaml  (verify key names against Configuration.php)
json_rpc_http_server:
    method:
        'ping':
            service: App\JsonRpc\PingMethod
final class PingMethod
{
    public function __invoke(?array $params = null): string
    {
        return 'pong';
    }
}
curl -s -X POST http://localhost:8000/json-rpc \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","method":"ping","id":1}'
# {"jsonrpc":"2.0","result":"pong","id":1}

If that round-trips, everything else is just adding handler services and mappings.

Implementation Patterns

One invokable service per method. This is the pattern the SDK is built around and it scales best: App\JsonRpc\User\GetMethod, App\JsonRpc\User\CreateMethod. The service id is what you map in config, so the config file becomes a readable API surface / method registry. Keep the class names mirrored in the JSON-RPC method names (user.get → User\GetMethod) and code review of the config is trivial.

Result vs error. Return a plain value (or a serializable DTO) for success — the transport wraps it in {"result": ...}. To return a JSON-RPC error rather than a PHP failure, throw the SDK's JSON-RPC exception with a code and optional data:

throw new JsonRpcException(-32001, 'User not found', ['userId' => $id]);

A plain \RuntimeException typically collapses into the generic -32603 / Internal error, which is what you want for bugs and not for expected domain failures. Decide the convention early: domain exceptions → explicit codes; everything else → internal error.

Validation is a separate concern. Do not hand-roll param checks in the handler. Add yoanm/symfony-jsonrpc-params-validator and declare a schema per method (Symfony Validator constraints on a DTO work well). The invalid-params error (-32602) is then produced by the stack, consistently, before your handler runs — which also keeps your handlers free of defensive isset() noise.

Auth and rate limiting belong in Symfony, not JSON-RPC. Because the entire API is one POST endpoint, a firewall rule on that path, a kernel.request subscriber, or a RequestMatcher guards every method at once. Per-method authorization goes either in the handler or in a decorator around the method resolver; the latter is cleaner if you have many methods and want a single policy hook.

Decorators for cross-cutting concerns. The server is a service, so:

#[AsDecorator(decorates: 'yoanm.jsonrpc.server')] // check the actual service id
final class LoggingServer implements ServerInterface { /* ... */ }

is the idiomatic way to add timing, request logging, metrics, or a correlation id without touching handlers. Decorate the method-resolver/handler service if you want per-method instrumentation instead.

Batch and notifications are free. Send a JSON array as the body to exercise batch mode; send a payload without an id to test notifications. Test both explicitly in your functional suite — clients rely on them and they are the two places where naive implementations break.

Testing. Unit-test handlers directly (new PingMethod()(...)) — they are plain PHP. For end-to-end, a KernelBrowser POST with a JSON body is enough; assert on decoded result/error keys, not on HTTP status (see gotchas). A small collection of .http/curl fixtures checked into the repo pays for itself when upgrading.

Keep the transport thin. If you find yourself reaching into PSR-7 inside a handler, you are fighting the design. Handlers get params, return data, throw domain exceptions. Anything else belongs in a decorator or a subscriber.

Gotchas and Tips

  • HTTP status is almost always 200. JSON-RPC errors are transported in the body (error.code), not the status line. Tests asserting 404 for a missing method or 400 for bad params will fail. Assert on the payload.
  • Do not cast the id. JSON-RPC requires echoing the id exactly; a client sending "id": "abc" must get "abc" back, and 0 is a valid id. If you normalize ids anywhere in a proxy or middleware, clients will silently drop responses.
  • PSR-7 vs HttpFoundation. The package speaks PSR-7. If you write your own controller rather than using the bundle's route, you need a bridge in both directions (symfony/psr-http-message-bridge plus a PSR-17 factory). The classic failure is Unable to create PSR-7 request — that means no PSR-17 implementation is registered, not that your code is wrong.
  • Content-Type matters. Clients that send application/json-rpc or no Content-Type can be rejected or silently mishandled. Be liberal in what you accept if you have heterogeneous clients, and document the expected header.
  • Notifications return an empty body. A batch consisting only of notifications returns no content at all. Any client wrapper that unconditionally json_decodes the body will blow up; handle the empty case.
  • Config is compiled. The method map is turned into container references by a compiler pass, so a typo in a service id fails at cache:clear/container compile time, not at runtime. That is a feature — but it also means bin/console cache:clear after editing the mapping, and it means the handler services must be resolvable by the container (don't map a service id that is defined lazily behind a factory you forgot to register).
  • Don't leak internals in error.data. In debug mode exception messages/traces are helpful; in production they are an information disclosure. Wire an explicit error formatter/mapper and test the production configuration, not just dev.
  • CORS. One POST endpoint means preflight OPTIONS requests and a strict Access-Control-Allow-Origin/-Headers. nelmio/cors-bundle with a path rule is usually less painful than hand-writing a listener.
  • Logging payloads. Request/response bodies can be large and can contain credentials in params. Redact before logging; log the method name and id, not the whole envelope, unless you have a debug flag.
  • Small package, small surface. 13 stars means you are close to the source: pin the version in composer.json, read composer.json constraints against your Symfony version before upgrading, and be prepared to fork or wrap. The upside is that the whole transport is a few hundred lines and the extension points (decorating the server, overriding the error formatter, custom method resolvers) are shallow.
  • Extension points worth knowing: decorate the server service for observability/auth; override the error formatter service for consistent error shapes; add a kernel.response listener only as a last resort (it will see JSON-RPC responses and can easily corrupt them).
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings