sajya/server
Sajya Server is a Laravel package for building JSON-RPC 2.0 APIs. Define procedures, validate request parameters, apply middleware, handle batch calls and structured errors, and expose a clean RPC endpoint with minimal configuration and Laravel-native tooling.
composer require sajya/server and publish the config (php artisan vendor:publish --provider="Sajya\Server\ServiceProvider"). You get config/sajya.php, which is the first file to read: it holds the route prefix (default rpc), middleware, optional domain, and the procedure bindings map./rpc by default). Everything—method routing, batching, notifications—goes through that single URL, so there is no per-procedure routing to learn.php artisan make:procedure MathProcedure, then register it in config/sajya.php under bindings (or rely on the documented discovery mechanism if you prefer). A procedure is a class extending Sajya\Server\Procedure with a static $name (e.g. math) and public methods that become callable as math@add.{"jsonrpc":"2.0","method":"math@add","params":{"a":2,"b":3},"id":1} and expect {"jsonrpc":"2.0","result":5,"id":1}. Get that round-trip working (route middleware included) before adding validation, auth, or batches.procedure@method. Keep procedure names short and stable (user, billing, math) because they are part of your public contract; treat procedure@method as an API route name you would not rename casually.params object onto method arguments by name. Type-hint scalars/arrays on the method signature and let PHP coerce; for structured input, accept an array or a dedicated DTO and validate inside the method with the Laravel Validator facade (or a FormRequest-style service class) so failures come back as proper JSON-RPC errors rather than PHP errors.bindings array in config/sajya.php is the source of truth. Group by domain ('user' => UserProcedure::class) and keep the file small; split large APIs into multiple procedures rather than one god-procedure with 40 methods.'middleware' => ['api', 'auth:sanctum', 'throttle:60,1'] in the config, then read the caller via the injected request/auth() inside the procedure. Because it is one endpoint, per-method authorization must be done explicitly inside the method (policies/gates), not via route middleware.Sajya\Server\Exceptions\RpcException (with your own code and data payload) for expected domain failures. Reserve generic exceptions for genuine bugs—they surface as internal errors and are useless to clients. Define a small enum of application error codes and reuse it across procedures; this is the closest thing the package has to a shared error contract.id for fire-and-forget notifications. Use batches when a screen needs several independent lookups, but keep them small—one slow method stalls the whole response.userV2) or a new method rather than mutating an existing one. Bindings make this cheap, and old clients keep working.$this->postJson('/rpc', [...])->assertJsonPath('result', ...)—including a test for each error code and one for an unknown method (-32601). These tests are the main defence against accidental renames of procedure@method.^7.1; on older Laravel versions stay on the prior major/minor line and upgrade deliberately, since this package tracks the framework closely.procedure@helper to the outside world. Make helpers private/protected. This is the single most common mistake with this package.id means no response body. Notifications return an empty 200-ish response. If a client "hangs", check that it sent an id. Conversely, batching notifications with requests returns only results for the requests.bindings are read from config; after editing, run php artisan config:clear in local/dev, and remember config:cache in deploy pipelines or new procedures will 404 with "method not found".IS_INSTANCEOF attribute lookups (7.1.0). Attribute retrieval now matches subclasses, not just exact classes. Practical effect: a custom attribute that extends a package attribute now applies to your methods where it previously did not—handy for building your own annotations, but it can change behavior in code that relied on exact-class matching. If you have custom attributes or override attribute handling, add a test asserting which methods pick them up.api middleware plus an auth middleware that returns HTML redirects will produce a non-JSON-RPC body on 401; configure auth to return JSON (or add a JSON-aware exception handler) or clients will fail to parse the error.-32600 (invalid request), -32601 (method not found) and -32602 (invalid params) each point to a different layer: payload shape, binding name, or method signature.Sajya\Server\Procedure for shared behavior (logging, permission checks) across all your procedures, and centralise error-code mapping in one place rather than throwing ad-hoc codes per method. Those two hooks cover most of what teams end up wanting to customise.How can I help you explore Laravel packages today?