
Modern websites often need to display content from multiple platforms on a single homepage. A common example is showing WordPress blog posts or community discussions directly on a Laravel-based site without breaking layout or performance.
In this article, we’ll explain the best and most stable approach to fetch and display Flarum discussions on a Laravel website, using the same concept as WordPress blog fetch, but implemented correctly for production use.
This guide covers:
- Why backend proxy is the best approach
- How Flarum API works
- Step-by-step Laravel + JavaScript integration
- Clean UI rendering with fail-safe handling
Why Not Fetch Flarum Directly from Frontend?
Flarum provides a JSON:API endpoint, so technically you can fetch discussions directly using JavaScript. However, this approach causes multiple problems in real-world projects:
- ❌ CORS issues (very common)
- ❌ API rate limits exposed to users
- ❌ Authentication tokens exposed in frontend
- ❌ No caching → slow homepage
- ❌ API failure breaks UI
Because of this, direct frontend fetch is not recommended for production.
Best Practice: Laravel Backend Proxy (Recommended)
The best and safest approach is:
Laravel Backend → Fetch Flarum API → Convert to simple JSON → Frontend JS renders UI
Benefits:
- ✅ No CORS problems
- ✅ API tokens stay secure
- ✅ Easy caching (faster homepage)
- ✅ Clean error handling
- ✅ Same pattern as WordPress blog fetch
Step 1: Understand the Flarum API Endpoint
Flarum exposes discussions using JSON:API:
GET https://your-flarum-site.com/api/discussions
Common parameters:
page[limit]=9→ number of discussionssort=-createdAt→ latest discussions firstinclude=user,tags→ include author and tags
Example:
/api/discussions?page[limit]=9&sort=-createdAt&include=user,tags
The response is nested and complex, so we will simplify it in Laravel.
Step 2: Configure Flarum in Laravel
Add Flarum configuration in config/services.php:
'flarum' => [
'base_url' => env('FLARUM_BASE_URL'),
'api_token' => env('FLARUM_API_TOKEN'), // optional
],
Add values in .env:
FLARUM_BASE_URL=https://community.example.com
FLARUM_API_TOKEN=
If discussions are public, API token is not required.
Step 3: Create a Laravel Proxy Route
Create a route that your frontend will call instead of calling Flarum directly.
Route::get('/flarum/discussions', [FlarumController::class, 'discussions'])
->name('flarum.discussions');
Step 4: Fetch and Normalize Flarum Data (Controller)
Create a controller that:
- Calls Flarum API
- Extracts only required fields
- Returns clean JSON
- Caches the response
class FlarumController extends Controller
{
public function discussions(Request $request)
{
$limit = min((int) $request->limit ?: 9, 12);
return Cache::remember("flarum_discussions_$limit", now()->addMinutes(5), function () use ($limit) {
$base = rtrim(config('services.flarum.base_url'), '/');
$response = Http::acceptJson()
->get($base . '/api/discussions', [
'page' => ['limit' => $limit],
'sort' => '-createdAt',
'include' => 'user,tags',
]);
if (!$response->ok()) {
return ['success' => false, 'items' => []];
}
$json = $response->json();
$included = collect($json['included'] ?? []);
$users = $included->where('type', 'users')->keyBy('id');
$tags = $included->where('type', 'tags')->keyBy('id');
$items = [];
foreach ($json['data'] as $d) {
$authorId = data_get($d, 'relationships.user.data.id');
$author = $users[$authorId]['attributes'] ?? [];
$items[] = [
'title' => $d['attributes']['title'],
'link' => $base.'/d/'.$d['attributes']['slug'],
'created_at' => $d['attributes']['createdAt'],
'reply_count' => $d['attributes']['replyCount'],
'author' => $author['displayName'] ?? $author['username'],
];
}
return ['success' => true, 'items' => $items];
});
}
}
Step 5: Create Blade Section for Discussions
Add a section similar to WordPress blog cards:
<div id="flarum-discussions" style="display:none;">
<div id="flarum-loading">Loading discussions...</div>
<div id="flarum-grid"></div>
</div>
Step 6: Fetch Discussions Using JavaScript
Now fetch discussions from your Laravel route, not from Flarum directly.
<script>
document.addEventListener("DOMContentLoaded", async () => {
const section = document.getElementById("flarum-discussions");
const loading = document.getElementById("flarum-loading");
const grid = document.getElementById("flarum-grid");
section.style.display = "block";
try {
const res = await fetch("/flarum/discussions?limit=9");
const data = await res.json();
if (!data.success || !data.items.length) throw "No data";
let html = "";
data.items.forEach(d => {
html += `
<div class="discussion-card">
<a href="${d.link}" target="_blank">
<h5>${d.title}</h5>
<p>By ${d.author} • ${d.reply_count} replies</p>
</a>
</div>
`;
});
grid.innerHTML = html;
loading.style.display = "none";
} catch (e) {
section.style.display = "none";
}
});
</script>
Step 7: Fail-Safe Handling (Very Important)
If:
- Flarum is down
- API fails
- Network error occurs
👉 The entire section hides automatically, so your homepage never breaks.
Final Architecture Overview
Frontend JS
↓
Laravel Proxy Route
↓
Flarum JSON API
↓
Laravel Normalization + Cache
↓
Clean JSON to Frontend
Conclusion
If you want to show Flarum discussions on a Laravel website using the same concept as WordPress blog fetch, the Laravel backend proxy approach is the most professional and production-ready solution.
It ensures:
- Better performance
- Strong security
- Clean UI rendering
- No CORS headaches
- Scalable architecture
This pattern works perfectly for:
- Communities
- SaaS dashboards
- Blogs + forums combo
- Multi-platform content sites