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

Flarum Api Client Laravel Package

maicol07/flarum-api-client

A PHP client for the Flarum API that simplifies authentication and common forum operations, including discussions, posts, users, tags, and notifications. Works well in Laravel or standalone PHP projects, with a clean wrapper for making API requests and handling responses.

View on GitHub
Deep Wiki
Context7

Getting Started

composer require maicol07/flarum-api-client

There is no service provider, facade, or publishable config — this is a framework-agnostic PHP client that happens to sit nicely inside Laravel. That is the first thing to internalise: you are wiring it up yourself.

Minimum viable setup:

  1. Credentials in config/services.php (not scattered through the codebase):
'flarum' => [
    'url'         => env('FLARUM_URL'),
    'api_key'     => env('FLARUM_API_KEY'),
    'version'     => env('FLARUM_VERSION', '1.8'),
],
  1. A singleton binding in AppServiceProvider::register() so the underlying Guzzle client (and its connection pool / handler stack) is reused across the request:
$this->app->singleton(Client::class, fn () => new Client(
    config('services.flarum.url'),
    config('services.flarum.api_key'),
    config('services.flarum.version'),
));
  1. First use case: read-only listing. Authenticate with the API key, then pull a paginated collection (discussions, posts, users) and dump the result in php artisan tinker. Once you can see raw resources coming back, you understand the shape of everything else.

Where to look first, in order:

  • The client class itself — how it authenticates (API key vs. forUser() impersonation) and how it dispatches requests.
  • src/Resource/ — each Flarum endpoint is a resource object; this is your real API surface.
  • The test suite — it is the most reliable usage documentation, and it shows the exact method chaining for create/update/delete.

Do not start by writing a sync job. Start by proving you can read one discussion with an API key, because auth is the single most common failure point.

Implementation Patterns

Wrap it, don't spray it. Never inject the raw client into controllers. Create a thin FlarumRepository / ForumService that exposes domain methods (publishDiscussion(), suspendUser(), findDiscussionBySlug()) and returns your own DTOs. This gives you one place to change when Flarum's JSON:API shape shifts, and one place to add logging.

API key vs. user session. Two distinct modes, and mixing them up causes subtle bugs:

  • API key — server-to-server work: scheduled imports, moderation tooling, admin dashboards. Scoped to whatever the key's user can do.
  • User impersonation — when you need actions attributed to a real end user (posting on their behalf, permission checks in their context). Authenticate per-request, never cache an impersonated client in a singleton. This is the pattern where you must be most careful about leaking state between queued jobs.

Reads: cache aggressively, writes: queue them. Flarum is a separate service over the network; treat it like one.

$discussions = Cache::remember("flarum.discussions.{$page}", 300, fn () =>
    $this->flarum->discussions()->request()
);

For writes, dispatch a job with retry(3) and exponential backoff rather than calling out inside a web request. A slow or 500-ing forum should degrade your UI, not hang it.

Pagination. Flarum paginates with offset/limit-style parameters on the resource. Wrap traversal in a generator so you can stream large imports without holding everything in memory:

foreach ($this->flarum->discussions()->paginate($perPage = 50) as $discussion) {
    // ...
}

Confirm the exact paginator method on the resource class in your installed version before relying on this — it is the one piece of the API most likely to differ from the README.

Error handling at the boundary. The client surfaces Guzzle exceptions. Catch them in the repository and convert to domain exceptions so the rest of your app never imports GuzzleHttp\Exception\*:

try {
    return $this->flarum->discussions()->request();
} catch (ClientException $e) {   // 4xx
    throw new FlarumRejectedException($e->getResponse()->getBody()->getContents(), previous: $e);
} catch (ConnectException|ServerException $e) {  // network / 5xx
    throw new FlarumUnavailableException(previous: $e);
}

The 4xx body is JSON:API-formatted and usually explains exactly which field or permission failed — log it verbatim, it is worth more than the exception message.

Testing. Laravel's Http::fake() does not intercept this package — it goes through Guzzle directly. Either:

  • inject a Guzzle client built with MockHandler into the client, or
  • bind a fake/mock of your own FlarumRepository in the container and assert on that.

The second is preferable and is another reason for the repository wrapper.

Gotchas and Tips

1.5.4 is the version to pin on PHP 8.4. Prior releases declare implicitly nullable parameters, which are deprecated in PHP 8.4 — you will get a wall of Deprecated: notices in logs and test output on 8.4 until you upgrade. This is the single most actionable item in the release. The package still supports PHP 7.3 (its CI explicitly drops Rector there), so it is safe in older Laravel apps too.

The Guzzle refactor can break your subclasses. 1.5.4 replaced magic __call verb dispatch with Client::request() and removed the internal getConfig() call. If you extended the client, decorated it, or overrode getConfig() to inject headers, that override is now dead code — silently. Move header injection into a Guzzle middleware on the handler stack, which is the supported extension point:

$stack->push(Middleware::mapRequest(fn ($r) => $r->withHeader('X-Trace', $traceId)));

If you only ever used the client (no subclassing), this release is a no-op for you.

Header merging is now implicit. Because getConfig() was dropped, Guzzle's own default-header merge is the only mechanism. If you were relying on the old path to layer headers, verify with a history middleware dump that your headers actually reach the wire before trusting it.

Version string matters. The Flarum version you pass shapes request formatting (pre-1.0 vs. JSON:API endpoints differ meaningfully). Hardcoding it is fine, but put it in config and document why — mismatches produce confusing 404s rather than clear errors.

Auth state is mutable. Impersonation mutates the client object. In long-running queue workers, resolve a fresh instance per job or you will post as the wrong user. This is the nastiest bug class with this package.

Debugging: attach a history middleware or pass Guzzle's debug option to get the full request/response transcript. Do this before theorising about why a write returned 403 — nine times out of ten it is a Flarum permission/group setting, not your code.

No webhooks. Flarum will not push events to you. Polling via Schedule::command(...) is the normal integration path; keep it idempotent and store a high-water mark (last post ID or timestamp) rather than diffing full collections.

Watch upstream rate limits. Bulk writes to the forum API will get you throttled or banned by extensions like flarum/throttle. Serialise writes, add jitter to retries, and never fan out parallel Guzzle promises against a single key.

No config: publish, no facade. If a tutorial suggests Flarum:: — it is describing something else or a wrapper package. Build the binding yourself and keep the surface small.

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.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings