get-stream/stream-laravel
Official Laravel integration for GetStream.io activity feeds. Add scalable news feeds, follow relationships, notification feeds, and real-time updates to Laravel apps with Eloquent model integration, a Stream facade, config publishing, and a Laravel notification channel.
Install the package and wire it into Laravel:
composer require getstream/stream-laravel
php artisan vendor:publish --provider="GetStream\StreamLaravel\StreamServiceProvider"
Register the service provider and facade if your Laravel version does not auto-discover them:
// config/app.php
'providers' => [
GetStream\StreamLaravel\StreamServiceProvider::class,
],
'aliases' => [
'Stream' => GetStream\StreamLaravel\Facades\Stream::class,
],
Set credentials in .env:
STREAM_API_KEY=your_key
STREAM_API_SECRET=your_secret
STREAM_APP_ID=your_app_id
STREAM_REGION=us-east
Then clear cached config:
php artisan config:clear
Look first at config/stream.php, the Stream facade, and the underlying getstream/stream-php client docs. This package is primarily a thin Laravel wrapper: config binding, service provider, and facade access to the Stream PHP client.
A good first use case is writing an activity to a user feed and reading it back:
use GetStream\StreamLaravel\Facades\Stream;
Stream::feed('user', (string) $user->id)->addActivity(
'post',
(string) $post->id,
[
'actor' => 'user:'.$user->id,
'message' => $post->title,
'foreign_id' => 'post:'.$post->id,
'time' => $post->created_at->toIso8601String(),
]
);
$activities = Stream::feed('timeline', (string) $user->id)->getActivities();
If the facade methods differ in your installed version, inspect the resolved client:
dd(app('stream'));
Write activities from domain events, not controllers. Keep feed writes in listeners or jobs so posting, commenting, liking, and following all update Stream consistently.
// app/Listeners/AddPostActivity.php
public function handle(PostCreated $event): void
{
Stream::feed('user', (string) $event->post->user_id)->addActivity(
'post',
(string) $event->post->id,
[
'actor' => 'user:'.$event->post->user_id,
'message' => $event->post->title,
'foreign_id' => 'post:'.$event->post->id,
'time' => $event->post->created_at->toIso8601String(),
]
);
}
Use follow relationships for timelines. A common pattern is a personal user feed plus a timeline feed that follows other users:
Stream::feed('timeline', (string) $user->id)
->follow('user', (string) $followedUser->id);
Stream::feed('timeline', (string) $user->id)
->unfollow('user', (string) $followedUser->id);
Then render timeline:{id} in the UI. For fan-out-on-write, keep feed slugs stable (user, timeline, notification, group) and always cast IDs to strings.
Use foreign_id and time for idempotency. These let you update/remove activities reliably and avoid duplicate entries when jobs retry:
Stream::feed('user', (string) $user->id)
->removeActivity('post:'.$post->id, true);
Queue Stream writes and use afterCommit. Feed calls are network calls; doing them synchronously in model observers can slow requests and create inconsistent state if the transaction rolls back.
class SyncPostToStream implements ShouldQueue
{
use InteractsWithQueue, Queueable;
public $afterCommit = true;
}
Reactions and comments are usually handled through the underlying client’s reactions API. Keep reaction writes in the same queued listener pattern and store your local comment/reaction ID as the activity or reaction foreign_id where possible.
Eloquent integration depends on version. Older releases shipped traits for syncing models to feeds; newer releases are thinner. Before relying on model traits, check the installed package source. If traits are absent, wire created, updated, and deleted model events to queued jobs yourself.
Testing: avoid real Stream calls in tests. Bind a fake client into the container or mock the facade, then assert that expected feed/activity calls were made. Also assert that jobs are dispatched rather than executing network calls inline.
Stream::shouldReceive('feed')
->with('user', '1')
->andReturn($fakeFeed);
Integration tip: keep a small application-level service around the facade for feed naming and payload shaping. That gives you one place to change when the package or stream-php API changes.
getstream/stream-php. Check both package versions with composer show getstream/stream-laravel getstream/stream-php.stream-php compatibility before adopting it in a new Laravel 10/11 project. Because the wrapper is small, replacing the binding with your own client is often straightforward.STREAM_* values, run php artisan config:clear. In production, deploy config changes through your normal cache rebuild.app_id and region must match the Stream app. Mismatched region/app ID usually surfaces as authentication or not-found errors that look like credential problems.(string) at the boundary.foreign_id deliberately. It is your idempotency and deletion key. Establish a convention such as post:{id}, comment:{id}, and reaction:{id}.Stream is a generic alias; if another package uses it, register the full facade class or rename the alias.app('stream'), config('stream'), and a single feed(...)->getActivities() call will quickly distinguish config, auth, and method-signature problems.getstream/stream-php directly if the wrapper lags behind Laravel releases.How can I help you explore Laravel packages today?