PHPackages                             qcodr/restate-sdk-laravel - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Framework](/categories/framework)
4. /
5. qcodr/restate-sdk-laravel

ActiveLibrary[Framework](/categories/framework)

qcodr/restate-sdk-laravel
=========================

Laravel integration for the Restate PHP SDK — durable execution (workflows, virtual objects, sagas, durable timers) wired into Laravel's container, routing, and Artisan.

00PHPCI passing

Since Jun 27Pushed todayCompare

[ Source](https://github.com/qcodr/restate-sdk-laravel)[ Packagist](https://packagist.org/packages/qcodr/restate-sdk-laravel)[ RSS](/packages/qcodr-restate-sdk-laravel/feed)WikiDiscussions main Synced today

READMEChangelog (1)DependenciesVersions (1)Used By (0)

Restate SDK for Laravel
=======================

[](#restate-sdk-for-laravel)

[![CI](https://github.com/qcodr/restate-sdk-laravel/actions/workflows/ci.yml/badge.svg)](https://github.com/qcodr/restate-sdk-laravel/actions/workflows/ci.yml)[![codecov](https://camo.githubusercontent.com/60a8093c86f97a731623dcff53c5df0fbfe5d9c31d30d272b507b34354ce9117/68747470733a2f2f636f6465636f762e696f2f67682f71636f64722f726573746174652d73646b2d6c61726176656c2f6272616e63682f6d61696e2f67726170682f62616467652e737667)](https://codecov.io/gh/qcodr/restate-sdk-laravel)[![PHPStan level max](https://camo.githubusercontent.com/b6d441ad4fe8332cb16c72aa27f22cc685181dfd74ae34964afc92c6c1146b3c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048505374616e2d6c6576656c2532306d61782d627269676874677265656e2e737667)](phpstan.neon)[![Mutation testing badge](https://camo.githubusercontent.com/5dfb5bdea825c28106481107e084d0e578edd239c54b232a5b4967001f67fd12/68747470733a2f2f696d672e736869656c64732e696f2f656e64706f696e743f7374796c653d666c61742675726c3d687474707325334125324625324662616467652d6170692e737472796b65722d6d757461746f722e696f2532466769746875622e636f6d25324671636f6472253246726573746174652d73646b2d6c61726176656c2532466d61696e)](https://dashboard.stryker-mutator.io/reports/github.com/qcodr/restate-sdk-laravel/main)[![Latest Stable Version](https://camo.githubusercontent.com/08a5799b092c0ed46b4a92c438d2987407aa352af5ef4a56a64ca0b198670225/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f71636f64722f726573746174652d73646b2d6c61726176656c2e737667)](https://packagist.org/packages/qcodr/restate-sdk-laravel)[![Total Downloads](https://camo.githubusercontent.com/b34eac7150f36c32f50c578e8841580ae1e8bd82677074a36f2e00c4d4de7f39/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f71636f64722f726573746174652d73646b2d6c61726176656c2e737667)](https://packagist.org/packages/qcodr/restate-sdk-laravel)[![PHP Version](https://camo.githubusercontent.com/7b2b67b0435daa94a41608bf7c3d44b8fdd7c374f97c050a4b8010d80537efda/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f7068702d762f71636f64722f726573746174652d73646b2d6c61726176656c2e737667)](https://packagist.org/packages/qcodr/restate-sdk-laravel)[![License](https://camo.githubusercontent.com/798509b4df525f56802b56f8096862487f08023e3d7561c68656f8dab10d0d6e/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4170616368652d2d322e302d626c75652e737667)](LICENSE)

Laravel integration for the [Restate PHP SDK](https://github.com/qcodr/restate-sdk-php) — **durable execution** (workflows, virtual objects, sagas, durable timers, exactly-once side effects) wired into Laravel's container, routing, and Artisan.

Write Restate services as ordinary Laravel classes (with constructor DI), list them in config, and serve them either from inside your app (a request/response route) or over true bidirectional HTTP/2 (`php artisan restate:serve`).

Why
---

[](#why)

Restate adds guarantees Laravel's native tools don't give you for multi-step, long-running, or per-entity-stateful work:

Laravel areaWhat Restate adds**Jobs / multi-step processes**Durable **sagas** — order → payment → inventory → shipping with automatic retries and compensation, exactly-once**Per-entity state** (counters, balances, inventory, rate limits)**Virtual Objects** — single-writer state per key, no `lockForUpdate` / row-lock races**Long-running / human-in-the-loop** (approvals, email verify, reminders)**Durable promises + timers** — wait days for an external event, surviving restarts/deploys**Webhooks / side effects**Exactly-once processing + the outbox pattern via `ctx->run()`**Scheduler**Self-rescheduling durable timers that survive restarts (vs stateless cron)**API orchestration / fan-out**Durable combinators (`select`/`awaitAll`) with retriesRequirements
------------

[](#requirements)

- PHP **8.2+**
- Laravel **12** or **13**
- [`qcodr/restate-sdk-php`](https://github.com/qcodr/restate-sdk-php) (pulled in automatically)
- `amphp/http-server` only if you use `php artisan restate:serve` (bidi)

Installation
------------

[](#installation)

```
composer require qcodr/restate-sdk-laravel
php artisan vendor:publish --tag=restate-config   # publishes config/restate.php
```

The service provider is auto-discovered.

Quick start
-----------

[](#quick-start)

Define a service as a normal Laravel class — constructor dependencies are injected from the container:

```
namespace App\Restate;

use Qcodr\Restate\Sdk\Context\Context;
use Qcodr\Restate\Sdk\Service\Attribute\{Service, Handler};

#[Service]
final class GreeterService
{
    public function __construct(private readonly \App\Services\Mailer $mailer) {}

    #[Handler]
    public function greet(Context $ctx, string $name): string
    {
        // Non-deterministic work (DB, HTTP, mail) goes inside ctx->run() so it runs
        // exactly once and replays from the journal on retries.
        $ctx->run('notify', fn () => $this->mailer->ping($name));

        return "Hello {$name}";
    }
}
```

Register it in `config/restate.php`:

```
'services' => [
    App\Restate\GreeterService::class,
],
```

Point a running Restate runtime at your app and invoke through the ingress:

```
restate deployments register http://localhost:8000/restate --use-http1.1
curl localhost:8080/GreeterService/greet -H 'content-type: application/json' -d '"world"'
# "Hello world"
```

> **One rule for handlers:** they must be deterministic and stateless. Keep every DB / HTTP / mail / random call inside `ctx->run()` (a durable side effect); per-invocation data lives in local variables or Restate state, never in instance properties.

Serving
-------

[](#serving)

**In-app route (default).** The provider mounts a catch-all route at the configured prefix (`restate` by default), served request/response by your normal Laravel stack (FPM, Octane). Register the deployment at `/restate`. Zero extra infrastructure.

**Bidirectional streaming.** For cancellation, signals, and fewer re-invokes on suspension-heavy handlers, run the amphp host instead:

```
composer require amphp/http-server
php artisan restate:serve --port=9080 --workers=8
```

Set config `path` to `null` to disable the in-app route when serving this way.

Calling Restate from Laravel
----------------------------

[](#calling-restate-from-laravel)

The two sections above expose *your* handlers to the runtime. The reverse direction — starting a Restate invocation from ordinary Laravel code (a controller, job, or listener) — goes through the Restate **ingress** with the `RestateClient`:

```
use Qcodr\Restate\Laravel\Client\RestateClient;

public function __construct(private readonly RestateClient $restate) {}

// Call and await the result (request/response):
$greeting = $this->restate->call('GreeterService', 'greet', 'Ada');

// Fire-and-forget — returns the invocation id immediately:
$id = $this->restate->send('OrderWorkflow', 'run', ['orderId' => $orderId], key: $orderId);

// Keyed object/workflow, idempotency, and a durable delayed send:
$this->restate->send('OnboardingService', 'nudge', ['userId' => $id], key: $id, delayMs: 3_600_000);
```

Configure the ingress in `config/restate.php` (`ingress.url`, `RESTATE_INGRESS_URL`; optional `ingress.token`, `RESTATE_INGRESS_TOKEN` → `Authorization: Bearer`). Full recipe — controllers, jobs, listeners, idempotency, delayed send, error handling — in **[docs/usecases/dispatch.md](docs/usecases/dispatch.md)**.

Configuration
-------------

[](#configuration)

`config/restate.php`:

KeyDescription`services`Service / virtual-object / workflow class names exposed by the deployment`path`Route prefix the runtime calls (`null` disables the in-app route)`middleware`Middleware group for the route (default `['api']`)`identity_key``publickeyv1_...` to verify request signatures (needs `ext-sodium`)`ingress.{url,token}`Restate ingress base URL + optional Bearer token for the `RestateClient` (the caller side)`server.{host,port,workers}``restate:serve` bind settings (`workers: 0` = one per CPU)Artisan
-------

[](#artisan)

```
php artisan restate:discover          # list the bound services
php artisan restate:discover --json   # print the raw discovery manifest
php artisan restate:serve             # serve over bidi HTTP/2 (amphp)
```

Use cases
---------

[](#use-cases)

Worked, tested examples live under `tests/Examples/` (proven offline against the SDK — discovery, business logic, and the durable flow over a fake context) with copy-pasteable recipes:

- **[Saga](docs/usecases/saga.md)** — an order-processing workflow (reserve inventory → charge → ship) with automatic **compensation** on failure: a thin `#[Workflow]` over injected, idempotent services. The proof drives a failing payment and asserts inventory is released, shipping never runs, and a `TerminalException` surfaces.
- **[Rate limiter](docs/usecases/rate-limiter.md)** — a per-key token bucket as a `#[VirtualObject]`: single-writer state per key, no `lockForUpdate` / Redis race. The proof drains one key while another stays full, showing per-key isolation.
- **[Dispatch from Laravel](docs/usecases/dispatch.md)** — the *caller* side: start invocations (call-and-await, fire-and-forget `send`, keyed objects/workflows, idempotency, durable delayed send) from a controller, job, or listener via the `RestateClient` over the Restate ingress.
- **[Queue connection](docs/usecases/queue.md)** — dispatch existing `ShouldQueue` jobs on the `restate` connection (`->onConnection('restate')`) to run them **durably** on Restate (exactly-once, durable retries) with no `queue:work` worker.
- **[Validation](docs/usecases/validation.md)** — validate a handler's decoded-array input with Laravel's Validator at the boundary via the `ValidatesInput` trait, throwing a terminal 400 on bad input (the idiomatic answer to the array gotcha below).
- **[Testing](docs/usecases/testing.md)** — `Restate::fake()` + `Restate::assertCalled(...)`/ `assertSent(...)`, the `Bus::fake()` equivalent for Restate dispatches.
- **[Generators &amp; discovery](docs/usecases/discovery.md)** — `php artisan make:restate-service`(`-object`, `-workflow`) plus directory auto-discovery (config `discover`).
- **[Typed clients](docs/usecases/codegen.md)** — `php artisan restate:codegen` generates an IDE-autocompletable client per service (`GreeterClient::fromContext($ctx)->greet(...)`).
- **[Scheduler](docs/usecases/scheduler.md)** — `Schedule::restate('Svc','handler', $payload) ->dailyAt('03:00')` fires durable Restate invocations from Laravel's scheduler.
- **[Observability](docs/usecases/observability.md)** — handler logs flow into Laravel's logging stack (replay-aware), and an optional Telescope watcher tags ingress dispatches.
- **[Auth &amp; tenant](docs/usecases/auth.md)** — re-establish the authenticated user / tenant inside a handler from the headers the runtime forwards (`withAuth`), and forward them on outbound dispatches.

> Several surface a real SDK boundary: `JsonSerde` hands handlers the **decoded array**, not a hydrated object, so a handler's input parameter is `array`/scalar and the value object is built (and validated — see the Validation trait) inside the handler.

Code quality
------------

[](#code-quality)

Mirrors the SDK's strict gate, all offline:

```
make check     # php-cs-fixer + PHPStan (max, with Larastan) + Psalm taint + PHPUnit
```

License
-------

[](#license)

[Apache-2.0](LICENSE)

###  Health Score

20

—

LowBetter than 13% of packages

Maintenance65

Regular maintenance activity

Popularity0

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity11

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/297220555?v=4)[qcodr](/maintainers/qcodr)[@qcodr](https://github.com/qcodr)

---

Top Contributors

[![qcodr](https://avatars.githubusercontent.com/u/297220555?v=4)](https://github.com/qcodr "qcodr (15 commits)")

### Embed Badge

![Health badge](/badges/qcodr-restate-sdk-laravel/health.svg)

```
[![Health](https://phpackages.com/badges/qcodr-restate-sdk-laravel/health.svg)](https://phpackages.com/packages/qcodr-restate-sdk-laravel)
```

###  Alternatives

[laravel/socialite

Laravel wrapper around OAuth 1 &amp; OAuth 2 libraries.

5.7k104.3M832](/packages/laravel-socialite)[laravel/dusk

Laravel Dusk provides simple end-to-end testing and browser automation.

1.9k38.6M289](/packages/laravel-dusk)[pinguo/php-msf

Pinguo Micro Service Framework For PHP

1.7k4.2k](/packages/pinguo-php-msf)[nineinchnick/edatatables

Grid widget for the Yii Framework, wrapper for the DataTables jQuery plugin

173.2k](/packages/nineinchnick-edatatables)[link-cloud/fast-hyperf

LinkCloud Fast Hyperf

241.2k1](/packages/link-cloud-fast-hyperf)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
