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

Strapi Laravel Client Laravel Package

mbvienasbaitas/strapi-laravel-client

Laravel client for the Strapi headless CMS REST API. Query content with a fluent builder supporting filters, pagination, sorting and relation population, plus response caching through Laravel's HTTP client and cache.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the package and publish its config:

composer require mbvienasbaitas/strapi-laravel-client
php artisan vendor:publish --tag=strapi-config   # if the package ships a config

The config file (typically config/strapi.php) is the first place to look. You need two values almost always:

// .env
STRAPI_URL=https://cms.example.com
STRAPI_TOKEN=your-api-token-here
  • STRAPI_URL — the base URL of your Strapi instance, without a trailing slash and without /api (the client appends that).
  • STRAPI_TOKEN — a Strapi API token generated under Settings → API Tokens. For read-only content delivery, create a read-only token scoped to the content types you actually expose. Don't reuse an admin JWT.

Most usage goes through the facade:

use MbvienasBaitas\Strapi\Client\Facades\Strapi;

$posts = Strapi::collection('posts')->get();

(Exact method names — collection() vs from(), get() vs all() — should be confirmed against the package README; the surface is small, so a five-minute read of src/ is faster than guessing.)

First use case: rendering a blog index. Fetch a paginated list of posts, populate the cover media field and author relation, then hand the result to a Blade view or Inertia page. That single flow exercises config, auth, filtering, populate and pagination — if it works, the rest of the package almost certainly will too.

Before writing feature code, do one Http::fake()-free smoke test via php artisan tinker:

Strapi::collection('posts')->get();

If that returns data, your token scopes and base URL are correct. If it 403s, the problem is Strapi-side permissions, not Laravel.

Implementation Patterns

Wrap the client in a repository/service. Even for a small project, don't call Strapi::collection() directly from controllers. You want one class per content type:

final class PostRepository
{
    public function published(int $page = 1): array
    {
        return Cache::remember("strapi.posts.{$page}", 600, fn () =>
            Strapi::collection('posts')
                ->populate(['cover', 'author'])
                ->sortBy('publishedAt', 'desc')
                ->paginate($page, 12)
                ->get()
        );
    }
}

This gives you a single place to add caching, retries, fallbacks and response normalization — all of which you will want eventually.

Cache aggressively, invalidate via webhook. Strapi is a network hop; treating it like a database on every request will dominate your TTFB. Cache the shaped response (not the raw Strapi envelope), then add a Strapi lifecycle webhook that hits a Laravel endpoint which flushes the relevant cache keys:

Route::post('/hooks/strapi', function (Request $request) {
    abort_unless($request->header('X-Webhook-Secret') === config('services.strapi.hook_secret'), 403);
    Cache::tags(['strapi'])->flush();
    return response()->noContent();
});

Note Cache::tags() only works with taggable stores (Redis, Memcached, array) — not the file driver.

Normalize media URLs in one place. Strapi returns relative URLs on media objects (/uploads/foo.jpg). Prefix with config('strapi.url') in a resource/transformer, not in Blade templates scattered across the app. Also handle the formats sub-object (thumbnail, small, medium, large) once, centrally.

Use Laravel's HTTP client features via the package, or wrap it. If the client exposes the underlying pending request, you get ->timeout(), ->retry() and middleware for free. If it doesn't, a decorator around the repository is the cleanest place to add resilience:

try {
    return $this->repo->published();
} catch (ConnectionException $e) {
    report($e);
    return Cache::get('strapi.posts.1', []); // stale fallback
}

Test with Http::fake(). The package is a thin wrapper over Laravel's HTTP client, so faking Strapi responses is straightforward and gives you fast, hermetic tests:

Http::fake([
    '*/api/posts*' => Http::response(['data' => [...], 'meta' => [...]]),
]);

Keep a couple of real recorded Strapi payloads in tests/fixtures/ — the response envelope is the part most likely to bite you.

Gotchas and Tips

Strapi v4 vs v5 response shape. Strapi 4 wraps everything in { data: { id, attributes: {...} } }. Strapi 5 flattens entities so attributes live at the top level. This package targets the v4 shape; if you upgrade Strapi, expect to rewrite your transformers. Pin your Strapi major version alongside the client.

populate is not recursive by default. populate=* gets one level. Deeper relations require explicit syntax (populate[author][populate][avatar]=*), and the package's builder may only expose the simple form. If a nested relation comes back as an empty object, that's almost always a populate depth issue, not a permissions issue.

403 vs 404 confusion. An unpublished or permission-denied content type returns 403 in Strapi, not 404. Check the token's per-content-type permissions in the admin before debugging Laravel. Also remember that find($id) on a draft entry returns 404 unless you have preview/publication permissions.

Default page size is 25, max is 100. If you request =500 you'll silently get 100 and the meta.pagination block will be the only clue. Read meta.pagination.pageCount rather than assuming your request succeeded.

No meta on single-entry endpoints. find() returns bare data, while get() returns data + meta + sometimes error. Don't write one generic unwrapper that assumes both shapes; handle them separately in your repository.

Rate limits and timeouts. Strapi behind a reverse proxy can be slow on cold caches, and the default HTTP timeout will eventually bite you. Set an explicit timeout (5–10s) and consider ->retry(2, 200) for transient 5xx. Do not retry 403/404.

Trailing slashes and base paths. STRAPI_URL=https://cms.example.com/ produces //api/... and some proxies reject it. Likewise, if Strapi is mounted under a subpath (/cms), verify the client respects it — most thin wrappers just concatenate strings.

Trailing config cache. After changing .env values, run php artisan config:clear. This is the single most common "the package is broken" false alarm.

Low-maintenance package — insulate yourself. With ~10 stars and a small maintainer surface, treat this as a thin convenience layer rather than load-bearing infrastructure. The repository/decorator pattern above means swapping it for Http::withToken(...)->get(...) is a one-file change. That's cheap insurance.

Extension points to know. If the package exposes a Strapi singleton in the container, you can swap the binding in a service provider for tests or for a decorated client. Look for a StrapiClient interface or a facades directory in src/ — that's where overrides go. If none exists, keep your repository interface in your own codebase and bind that, not the package class.

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.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console