storyblok/php-content-api-client
A PHP client for Storyblok's Content Delivery and Management APIs, giving Laravel and plain PHP apps typed access to stories, components, datasources, and assets. Includes request handling, pagination, caching support, and a fluent query builder for headless content.
Architecture fit. The package is a thin, framework-agnostic SDK over Storyblok's Content Delivery API (read-only, CDN-fronted). It speaks PSR-18/PSR-7/PSR-17, so it drops into Laravel's HTTP layer cleanly, but it is not Laravel-native: no service provider, no facade, no config publishing, no Eloquent-style models, no cache integration. That is the central evaluation point — it is a transport-and-DTO library, and every ergonomic decision (caching, invalidation, preview mode, block rendering) is ours to build. In Laravel terms this is a dependency, not an integration; budget for an adapter layer roughly the size of the SDK's public surface.
Integration feasibility. High. composer require storyblok/php-content-api-client, bind a configured Storyblok\Client singleton in a small service provider, source the token from config/storyblok.php + env, and you can fetch a story in an afternoon. The SDK covers the parts of the API we actually need: story/space/datasource/link retrieval, resolve_relations/resolve_links, pagination, version=draft for editorial preview, and the cv cache-version parameter. The friction is in the seams, not the core: PSR-18 client discovery vs. Laravel's Guzzle stack, retry/timeout policy, and typing of the returned Storyblok\Api\Response\* objects which are array-ish and easy to leak into controllers.
Technical risk.
bloks, _uid, component strings, _editable) permeates Blade templates and becomes a de facto schema. This is the most common long-term cost in headless CMS integrations.cv-keyed strategy plus webhook-driven invalidation, editors will publish and see stale pages — an organizational trust problem, not just a technical one.Key questions.
Stack fit. Good for a Laravel monolith or API backend serving Blade/Inertia/Next front ends. It is a read path: pair it with Laravel's cache and queue, and treat Storyblok as the system of record for content, not for transactional data. Do not attempt to mirror content into Eloquent tables in v1 — that reintroduces the sync problem the headless model exists to avoid.
Migration path. Recommended shape:
config/storyblok.php — space ID, public token, preview token, version, cache TTL, webhook secret.StoryblokServiceProvider — bind a configured client; register a StoryblokRepository interface.StoryblokRepository — the only class that touches SDK types; returns our own DTOs/value objects (Story, Blok, Asset).BlokRenderer — maps component strings to Blade components with an explicit allowlist and a graceful fallback, so an unknown block degrades instead of 500-ing.Cache::remember keyed by slug + version + cv + locale, with tags where the store supports them.POST /webhooks/storyblok — signature-verified, queued, bumps the space's cv and flushes tags.noindex, never cached.Migrating from an existing CMS: model content types in Storyblok first, run both systems behind a feature flag per route, and cut over route-group by route-group once redirects and SEO metadata parity are verified.
Compatibility. Works across Laravel 9–12 and PHP 8.1+ in practice; confirm the SDK's PHP floor and that its PSR-18 discovery resolves to the Guzzle version Laravel already ships, otherwise you carry two HTTP stacks. Storyblok is API-versioned and additive; the SDK's breaking changes have historically tracked major versions, so pin with a caret constraint and read changelogs on minor bumps. Testing is straightforward: Http::fake() at the boundary, and a fixture set of captured API payloads to keep the DTO mapping honest without network calls in CI.
Sequencing. (1) Two-day spike: fetch a story, render a block, measure p95. (2) Config + provider + repository + DTOs. (3) Cache decorator and webhook invalidation — before the first content launch, not after. (4) Blok renderer and asset/image helper. (5) Preview mode for editors. (6) Observability and runbook. Do not start with preview; it is the piece most likely to be reworked once the content model settles.
Maintenance. Low dependency churn, but non-zero glue maintenance: we own the provider, repository, renderer, cache strategy, and webhook handler — that is a real, permanent surface, plausibly 600–1,200 lines and a named owner. Because the package is MIT and vendor-published, the worst case is a fork; the realistic case is a quarterly dependency bump and a changelog read.
Support. Community GitHub issues only — no SLA, no vendor support contract from Storyblok for the PHP client specifically. Storyblok's own support covers the platform (space config, API behavior, rate limits), not this library's internals. Escalation for SDK bugs is "we patch and upstream," so plan for one engineer with enough context to do that.
Scaling. The read path scales well: Storyblok serves from CDN, and our cache absorbs the rest. Watch three things — (a) cache hit ratio, because a low ratio turns every page view into an API call and rate limits arrive fast; (b) N+1 story fetching, since relation resolution is not automatic and it is easy to write a loop that calls the API per referenced story; (c) egress on assets, which is Storyblok-side and worth modeling against plan limits before launch, not at the first invoice.
Failure modes.
Cache::remember..env on disk, and alert on 401.Ramp-up. A mid-level Laravel developer reaches productive velocity in roughly one to two days for the basics (fetch, render, cache), and about a week to own the full surface including preview, webhooks, and image handling. The genuine learning curve is not the SDK — it is Storyblok's content-modeling idiom (bloks, _uid, resolve_relations, cv). Budget editorial onboarding separately and earlier than engineering onboarding: most post-launch pain in headless CMS projects originates in content-model decisions made before the first page rendered.
How can I help you explore Laravel packages today?