Getting Started
Forrst is an internal microservice RPC protocol designed for intra-service communication with per-function versioning, built-in observability, and rich query capabilities.
Installation
Section titled “Installation”Install via Composer:
composer require cline/forrstRequirements
Section titled “Requirements”- PHP 8.5+
- Laravel 12+
- spatie/laravel-data 4.18+
- saloonphp/saloon 3.14+
Laravel Integration
Section titled “Laravel Integration”Publish the configuration file:
php artisan vendor:publish --provider="Cline\Forrst\ServiceProvider"This creates config/rpc.php with server, function, and resource configuration options.
Quick Start
Section titled “Quick Start”1. Create Your First Function
Section titled “1. Create Your First Function”Functions are the core building blocks in Forrst. Create a function class:
<?php
namespace App\Http\Functions;
use Cline\Forrst\Functions\AbstractFunction;
class UserListFunction extends AbstractFunction{ public function __invoke(): array { return User::query() ->select(['id', 'name', 'email']) ->get() ->toArray(); }}2. Configure the Server
Section titled “2. Configure the Server”In config/rpc.php, configure your Forrst server:
return [ 'namespaces' => [ 'functions' => 'App\\Http\\Functions', ],
'paths' => [ 'functions' => app_path('Http/Functions'), ],
'servers' => [ [ 'name' => env('APP_NAME'), 'path' => '/rpc', 'route' => 'rpc', 'version' => '1.0.0', 'functions' => null, // Auto-discover all functions ], ],];3. Make Your First Request
Section titled “3. Make Your First Request”Send a Forrst request using cURL:
curl -X POST http://localhost/rpc \ -H "Content-Type: application/json" \ -d '{ "protocol": { "name": "forrst", "version": "0.1.0" }, "id": "req_001", "call": { "function": "urn:acme:forrst:fn:users:list", "version": "1.0.0", "arguments": {} } }'Response:
{ "protocol": { "name": "forrst", "version": "0.1.0" }, "id": "req_001", "result": [ { "id": 1, "name": "Jane Doe", "email": "jane@example.com" }, { "id": 2, "name": "John Smith", "email": "john@example.com" } ]}Core Concepts
Section titled “Core Concepts”Functions
Section titled “Functions”Functions handle business logic. They extend AbstractFunction and implement __invoke():
class OrderCreateFunction extends AbstractFunction{ public function __invoke(): array { $order = Order::create([ 'user_id' => $this->requestObject->arguments['user_id'], 'total' => $this->requestObject->arguments['total'], ]);
return $order->toArray(); }}Servers
Section titled “Servers”Servers define endpoints that expose functions. Configure via config/rpc.php or extend AbstractServer:
class ApiServer extends AbstractServer{ public function functions(): array { return [ UserListFunction::class, UserGetFunction::class, OrderCreateFunction::class, ]; }
public function extensions(): array { return [ new CachingExtension(cache: cache()->store()), new IdempotencyExtension(), ]; }}Extensions
Section titled “Extensions”Extensions add cross-cutting functionality:
- CachingExtension - HTTP-style caching with ETags
- IdempotencyExtension - Prevent duplicate operations
- DeadlineExtension - Request timeouts
- QueryExtension - Rich filtering and pagination
- RateLimitExtension - Throttle requests
Descriptors
Section titled “Descriptors”Separate discovery metadata from function logic using the #[Descriptor] attribute:
use Cline\Forrst\Attributes\Descriptor;
#[Descriptor(UserListDescriptor::class)]class UserListFunction extends AbstractFunction{ public function __invoke(): array { // Pure business logic }}use Cline\Forrst\Discovery\FunctionDescriptor;use Cline\Forrst\Contracts\DescriptorInterface;
class UserListDescriptor implements DescriptorInterface{ public static function create(): FunctionDescriptor { return FunctionDescriptor::make() ->urn('urn:acme:forrst:fn:users:list') ->version('1.0.0') ->summary('List all users') ->description('Retrieves a paginated list of users with optional filtering'); }}Protocol Discovery
Section titled “Protocol Discovery”Every Forrst server includes forrst.describe for automatic API discovery:
curl -X POST http://localhost/rpc \ -H "Content-Type: application/json" \ -d '{ "protocol": { "name": "forrst", "version": "0.1.0" }, "id": "discover_001", "call": { "function": "urn:cline:forrst:ext:discovery:fn:describe", "version": "1.0.0", "arguments": {} } }'Error Handling
Section titled “Error Handling”Forrst uses structured error responses:
{ "protocol": { "name": "forrst", "version": "0.1.0" }, "id": "req_001", "result": null, "errors": [{ "code": "NOT_FOUND", "message": "User not found", "details": { "user_id": 999 } }]}Throw custom exceptions in your functions:
use Cline\Forrst\Exceptions\FunctionException;
class UserGetFunction extends AbstractFunction{ public function __invoke(): array { $user = User::find($this->requestObject->arguments['id']);
if (!$user) { throw FunctionException::notFound('User not found'); }
return $user->toArray(); }}Testing
Section titled “Testing”Use the post_forrst helper in tests:
use function Cline\Forrst\post_forrst;
test('lists users', function () { $response = post_forrst('urn:acme:forrst:fn:users:list');
$response->assertOk(); $response->assertJsonPath('result.0.name', 'Jane Doe');});
test('creates order with parameters', function () { $response = post_forrst('urn:acme:forrst:fn:orders:create', [ 'user_id' => 1, 'total' => 99.99, ]);
$response->assertOk(); $response->assertJsonPath('result.user_id', 1);});Next Steps
Section titled “Next Steps”- Servers - Configure servers with middleware, extensions, and custom routing
- Functions - Build functions with validation, authentication, and transformers
- Extensions - Add caching, idempotency, rate limiting, and more
- Clients - Create type-safe Forrst clients using Saloon
- Protocol Specification - Deep dive into the Forrst protocol