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

Client Laravel Package

rokka/client

Official PHP client for Rokka, the image processing and delivery service. Upload, store, and manage images, generate dynamic URLs with stack options, and integrate Rokka into PHP or Laravel apps through a simple, PSR-compatible API.

View on GitHub
Deep Wiki
Context7

Getting Started

Rokka is an image-processing CDN. The client is a thin SDK over its REST API: you push source images in, define stacks (named pipelines of operations), and get back URLs that render on the fly.

composer require rokka/client

As of 1.22.0 the package requires PHP ^8.2 and guzzlehttp/guzzle ^7.4.5 / guzzlehttp/psr7 ^2.4.5. PHP 8.0/8.1 and Guzzle 6 are no longer supported, so check your composer.json platform constraint and any Laravel version still pinned to 8.1 before upgrading — this is the only hard breaking change in the release, and it will surface as a resolver error, not a runtime one.

Grab an API key from the Rokka dashboard — it is bound to an organization, and the org name is usually embedded in the key (myorg-a1b2c3…), which is why most methods also take an $org argument.

Where to look first

  1. Rokka\Client\Factory (or Client::create() in newer majors) — the entry point. Factory::getUserClient() gives you the org/stack/image-management surface; older releases also expose Factory::getImageClient() for read-only rendering work.
  2. The Rokka\Client\Core\* DTOs (Stack, Image, SourceImage, SourceImageAlias, Operation) — the client returns typed objects, not arrays, so getHash(), getShortHash(), getUrl() are your accessors.
  3. The README's "Usage" block — it is basically the whole API surface; there is not much else to read. For stack option names specifically, call listStackOptions() (new in 1.22.0) instead of guessing from docs.

First use case: upload → stack → URL

use Rokka\Client\Factory;

$client = Factory::getUserClient(getenv('ROKKA_API_KEY'));
$org    = 'myorg';

// 1. Upload the original
$source = $client->uploadSourceImage(
    fopen(storage_path('app/avatar.jpg'), 'r'),
    'avatar.jpg',
    $org
);
$hash = $source->getHash();          // full SHA-1 style hash
$short = $source->getShortHash();    // what you usually store in the DB

// 2. Define how it should be rendered (idempotent PUT)
$client->createStack($org, 'avatar', [
    'operations' => [
        ['type' => 'resize', 'width' => 200, 'height' => 200, 'mode' => 'fill'],
        ['type' => 'autoformat'],
    ],
]);

// 3. Build the URL
$url = "https://{$org}.rokka.io/avatar/{$short}.jpg";

That URL is now cacheable, resizable-on-demand, and can be overridden per-request with query options (?width=400).

Implementation Patterns

Bind it as a singleton in Laravel. Configure once in config/services.php, resolve through the container, never new it in controllers:

$this->app->singleton(\Rokka\Client\Client::class, fn () => Factory::getUserClient(
    config('services.rokka.key'),
    null,
    ['client' => app(\Psr\Http\Client\ClientInterface::class)] // injectable transport
));

Treat stacks as code, not clicks. Keep stack definitions in config/rokka/stacks.php and push them in a deploy/artisan command. Because createStack is effectively an upsert, re-running it is safe — but only if the stack definition is byte-identical, so keep the array shape stable. In 1.22.0+ you can validate the definitions against listStackOptions() in CI before they ever reach the API, which turns a typo'd operation name into a failing test instead of a broken deploy.

Store hashes, never URLs. Persist short_hash plus a stack name; construct the render URL at read time via a small helper. This is what makes swapping jpg → webp or changing a crop a config change rather than a migration. If you want stable, human-meaningful keys instead of a hash column, use source image aliases (setSourceImageAlias / getSourceImageAlias / deleteSourceImageAlias, new in 1.22.0) — an alias maps to a source image and reads as a friendlier URL segment, but it is an extra API object to keep in sync, so hashes are still the simpler default.

Build URLs through the SDK helpers, not string concatenation:

$client->getStackUrl($hash, 'avatar', 'jpg', ['org' => $org, 'width' => 400]);

Rokka\Client\UriHelper and TemplateHelper are the lighter-weight options for Blade/Twig views where you don't want the full client in scope — they only build URLs, no network. That matters for signing: UriHelper::signUrl() signs locally, while the new signUrlOnServer() (POST /utils/{org}/sign_url) is a network round-trip. Use the latter only when the signing secret must not be in the app's environment at all.

Overriding per render. Stack operations are the defaults; query parameters win. Use this for a srcset loop (?width=320, ?width=640, …) instead of defining a stack per breakpoint.

Integration with the upload flow. Rokka needs the bytes, so UploadedFile → fopen($file->getRealPath(), 'r') → uploadSourceImage(). Do this in a queued job; the round trip is tens to hundreds of milliseconds and you don't want it on the request path for user avatars.

Housekeeping / ops methods. The 1.22.0 additions round out the admin surface nicely and are all worth wrapping in small artisan commands rather than calling ad hoc:

  • setSourceImageName — rename a source image without re-uploading (good for tidying a media library or matching a renamed model).
  • getUserMetadata / getUserMetadataField — read back user metadata; handy for reconciling what the app stored vs. what Rokka holds.
  • getBilling — pull monthly usage for an org; useful for a dashboard or a cost-anomaly alert when staging starts burning production quota.
  • setOrganizationOptions — replace all org options in one request; complements the existing single-option setter, so prefer it when applying a full options array from config (same "byte-identical or don't bother" discipline as stacks).
  • downloadSourceImagesAsZip — bulk export, capped at 300 source images per call. Chunk your IDs; it is not a "download everything" button.

Errors. The client throws Rokka\Client\ClientException (and PSR-18 transport exceptions underneath). Catch it, inspect the response body — Rokka returns a JSON { "error": { "code", "message" } } that the exception message often flattens badly. Enable the debug option in the constructor to attach request/response to the exception during development.

Gotchas and Tips

  • PHP 8.2 is now mandatory. 1.22.0 dropped 8.0/8.1 and Guzzle 6. If you're on Laravel 10 with PHP 8.1, or on an old guzzlehttp/guzzle ^6 pinned by another package, the upgrade fails at resolve time. Upgrade PHP first, then the client.
  • API surface moved between majors. v1 (getImageClient / getUserClient, array-ish returns) and the Jane-generated v2 (Client::create, typed DTOs, $client->getStacks()->create(...)) differ enough that most Stack Overflow snippets won't compile. Pin the major in composer.json and read the changelog before upgrading.
  • Full hash vs short hash. getHash() returns the long hash used in API calls; URLs take getShortHash(). Mixing them gives a 404 that looks like a permissions problem.
  • Two silent endpoint migrations in 1.22.0. copySourceImage now uses documented POST /sourceimages/{org}/{hash}/copy instead of the legacy COPY verb, and getSourceImagesWithBinaryHash uses GET /sourceimages/{org}/binaryhash/{binaryHash} instead of ?binaryHash=. Signatures and behaviour are unchanged, but if you log/trace request verbs or allow-list methods in an outbound proxy, expect POST/GET where you previously saw COPY. Nothing in your calling code needs to change.
  • Cache purges are a paid feature — and one of them is a loaded gun. deleteSourceImageCache (single image) and deleteStackCache (stack, or * for the entire organization) are new, billable, and irreversible. Never expose them on a route a user can hit directly, and treat '*' as a manual, break-glass operation behind an explicit confirmation flag.
  • CDN staleness after stack edits — now fixable. Changing a stack's operations invalidates at the edge asynchronously; a "nothing changed" report right after a deploy is almost always cache, not a failed API call. Verify via the API (getStack) before debugging the render, then purge deliberately with deleteStackCache.
  • keepHash is a footgun with a safety rail. Passing $options['keepHash' => true] to setDynamicMetadata / deleteDynamicMetadata keeps the image hash stable — great for late metadata fixes — but every downstream cache (CloudFront, memcached, browser) keeps serving stale renders until you purge manually. It is mutually exclusive with deletePrevious; passing both throws a LogicException.
  • Binary vs JSON responses. Fetching a source image returns bytes; fetching metadata returns JSON. The same endpoint switches on Accept. If you get a GuzzleHttp\Psr7\Stream where you expected an object, check the header you sent.
  • Large uploads. Don't file_get_contents() a 20 MB photo — pass a stream resource. The client forwards it straight to the transport.
  • Rokka never deletes the CDN copy on deleteSourceImage — the source is gone, but previously rendered URLs can still be served from cache until TTL. Plan for that if you rely on deletion for moderation, and reach for deleteSourceImageCache (if you're entitled to it) rather than waiting out the TTL.
  • Retries belong in the transport, not your code. Inject a PSR-18 client wrapped with retry-on-429/5xx middleware; the SDK has no built-in backoff. The same is true for signUrlOnServer, which is one more endpoint that can 429.
  • Testing. Inject a mock PSR-18 client via the options array and assert on the request URI — the whole client is transport-agnostic, so no network and no fixtures needed. 1.22.0 ships MockHandler-based coverage (tests/ImageApiTest.php) for the new and migrated endpoints that you can crib from.
  • autoformat pays for itself — it negotiates WebP/AVIF from Accept and saves meaningful bytes; add it to every stack rather than special-casing formats.
  • Rate limits are per-key, not per-org. If you run staging and production against the same key, staging uploads will eat production quota.
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.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle