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

Facebook Graph Sdk Laravel Package

joelbutcher/facebook-graph-sdk

A Laravel integration for the official Facebook Graph SDK, providing a simple facade and configuration to authenticate users, manage pages, and query Graph API endpoints. Includes support for Facebook Login, access tokens, webhooks, and fluent request building, so you can work with Facebook's API wi...

View on GitHub
Deep Wiki
Context7

Getting Started

Install

composer require joelbutcher/facebook-graph-sdk

Requires PHP 8.x and the cURL extension (the default HTTP handler is cURL-based). In a Laravel app, prefer the companion wrapper which registers the container binding, facade and config for you:

composer require joelbutcher/laravel-facebook-sdk
php artisan vendor:publish --provider="JoelButcher\Facebook\FacebookServiceProvider"

Look here first

You want Look at
Wire up credentials config/facebook.php (Laravel) or the array passed to new Facebook\Facebook([...])
Login flow Facebook\Helpers\FacebookRedirectLoginHelper
Returning user data Facebook\FacebookResponse, GraphNode, GraphEdge, GraphUser
Errors Facebook\Exceptions\* (FacebookSDKException, FacebookResponseException, FacebookAuthenticationException)
Token plumbing Facebook\Authentication\AccessToken, OAuth2Client

Smallest useful call — server-side Graph request with an already-obtained token:

use Facebook\Facebook;

$fb = new Facebook([
    'app_id'                => config('services.facebook.app_id'),
    'app_secret'            => config('services.facebook.app_secret'),
    'default_graph_version' => 'v21.0', // pin explicitly, see Gotchas
]);

try {
    $response = $fb->get('/me?fields=id,name,email', $userToken);
    $user = $response->getGraphUser(); // array access + ->getField('name')
} catch (\Facebook\Exceptions\FacebookResponseException $e) {
    report($e); // Graph-level error (bad token, permissions, rate limit)
} catch (\Facebook\Exceptions\FacebookSDKException $e) {
    report($e); // Local/SDK error (transport, config, signature)
}

First real use case — "Login with Facebook on a server-rendered site": redirect to $helper->getLoginUrl($callback, ['email']), exchange the code in the callback with $helper->getAccessToken(), persist the token, then fetch /me. That single loop exercises the helper, the response objects and both exception families, which is 80% of the package's surface.

Implementation Patterns

Classic redirect OAuth flow

$helper = $fb->getRedirectLoginHelper();
$loginUrl = $helper->getLoginUrl(route('facebook.callback'), ['email', 'public_profile']);

// callback:
try {
    $token = $helper->getAccessToken();
} catch (FacebookResponseException $e)     { /* Graph rejected the exchange */ }
  catch (FacebookSDKException $e)           { /* state mismatch, curl, etc. */ }

if ($token && !$token->isLongLived()) {
    $token = $fb->getOAuth2Client()->getLongLivedAccessToken($token);
}
// store $token->getValue() + getExpiresAt()

Short-lived user tokens last ~1–2 hours; long-lived ones ~60 days. Exchange immediately after login and persist expires_at so you can schedule refreshes rather than discovering the token is dead in production.

Token source matrix — the pattern you pick determines the whole integration:

  • Server-side login → FacebookRedirectLoginHelper (state/CSRF handled for you, stored via the persistent data handler).
  • JS SDK / SPA / mobile → get the token client-side, POST it to your backend, then validate with $fb->debugToken($appToken)->getGraphNode() before trusting it, and/or $fb->getOAuth2Client()->getLongLivedAccessToken($shortToken).
  • System/cron user → $fb->getOAuth2Client()->getLongLivedAccessToken($token) from a manually obtained token, stored in config/vault.
  • Page/Instagram content → exchange the user token for a page token via /me/accounts?fields=id,name,access_token, or $fb->get("/{$pageId}?fields=access_token", $userToken).
  • App-only → $fb->getApp()->getAccessToken() for endpoints the App token can reach (debug, some webhook management).

Service-layer wrapper, not raw Facebook everywhere. Wrap the SDK in one small class that resolves the client, the token for the current tenant/user, and normalizes errors. This keeps token lookup out of controllers and makes the SDK swappable. In Laravel, bind the client as a scoped singleton and expose ->forUser($user) / ->forPage($page) accessors.

Batch requests for dashboards. One round trip beats N:

$batch = [
    'profile' => $fb->request('GET', '/me?fields=id,name,picture'),
    'feed'    => $fb->request('GET', '/me/feed?limit=5'),
    'friends' => $fb->request('GET', '/me/friends?limit=5'),
];
$responses = $fb->sendBatchRequest($batch, $token); // FacebookBatchResponse
foreach ($responses as $key => $response) { /* per-item errors possible */ }

Batch entries can fail individually — always check $response->isError()/catch per item, not just around the whole batch.

Pagination. Graph collections come back as GraphEdge. Iterating it lazily follows next cursors; call setLimit() on the edge or bound your loop — an unbounded foreach over a large edge will paginate forever.

$edge = $fb->get('/me/posts?limit=100', $token)->getGraphEdge();
do {
    foreach ($edge as $node) { /* GraphNode */ }
} while ($edge = $fb->next($edge) && $guard-- > 0);

Webhooks are not the SDK's job. The SDK gives you SignedRequest for signed-request style callbacks (deauthorize/data-deletion), but for X-Hub-Signature-256 you verify manually in middleware:

$expected = 'sha256=' . hash_hmac('sha256', $request->getContent(), $appSecret);
abort_unless(hash_equals($expected, $request->header('X-Hub-Signature-256')), 403);

Do this before JSON decoding and before any queue dispatch.

Queue the slow stuff. Graph calls are 100–800 ms; run feed publishes, insights pulls and page syncs in jobs. Store the access token (encrypted cast) on the model, pass the model ID into the job, resolve the token inside handle().

Testing. Swap the HTTP layer: implement Facebook\HttpClients\FacebookHttpClientInterface (or inject a fake) to return canned JSON, and use Facebook\PersistentData\InMemoryPersistentDataHandler so the login helper doesn't touch $_SESSION. Assert on getDecodedBody() rather than scraping raw strings.

Observability. Log every failure with $e->getCode(), $e->getErrorSubcode(), $e->getErrorUserTitle() and the raw $response->getBody(). Attach $response->getLastResponseHttpHeaders() when debugging rate limits.

Gotchas and Tips

This is a community fork, not the Meta-maintained repo. The API is frozen at the v5 design; treat it as a stable, well-understood client rather than something that will grow new features. Audit composer.lock and pin a version — "0 dependents" means you are the de facto QA. If you need typed models, DTOs or first-class Laravel integration, budget for a thin adapter on top.

default_graph_version is mandatory and always goes stale. Never rely on a default; drive it from env/config and schedule a quarterly review — Meta retires versions roughly two years after release, and a retired version returns opaque (#100) errors rather than a warning. Pin the version in one place and reference it in the same PR that updates the changelog.

setDefaultAccessToken() is mutable global state. It's convenient but leaks across requests in Laravel Octane, FrankenPHP and long-running queue workers. Either pass the token to every call explicitly, or reset it in middleware/handle() on each unit of work. This is the number one source of "user A saw user B's data" bugs.

Persistent data handler defaults to $_SESSION. Fine for web login, broken in CLI, jobs and tests. Use InMemoryPersistentDataHandler for tests and a custom handler (cache/database-backed) if you need state to survive across a redirect that lands on a different node. Note the CSRF state is single-valued: two login tabs open at once will invalidate each other's callback.

app_secret must be present in production. With Meta's "Require App Secret" / appsecret_proof enforcement on, every call without it fails. The SDK computes appsecret_proof automatically when the secret is configured — so a missing app_secret shows up as a confusing OAuth error, not a config error.

Token errors are the bulk of your bug reports. Learn the codes: 190 (invalid/expired token, subcodes 458/463/467), 4/17/32/613 (rate limiting), 100 (bad params/field), 200 (permission). Map 190 to "re-authenticate" in your UI, everything else to "log and alert".

Rate limits are per-app and per-user. Inspect x-app-usage / x-business-use-case-usage headers (returned via getLastResponseHttpHeaders()) and back off before you're throttled. A naive insights sync across many pages will hit this fast; stagger jobs.

GraphNode vs GraphEdge vs raw arrays. getGraphNode() throws if the payload isn't a single object; getGraphEdge() throws if it isn't a collection. Use getDecodedBody() whenever you're unsure, and remember only requested fields are returned — ?fields= omissions silently yield null, not an error. getProperty() throws on a missing field while array access returns null; pick one style and stay consistent.

GraphEdge iteration performs HTTP calls. Lazy pagination is great ergonomics and a terrible surprise in a foreach. Always bound the loop with a limit or a guard counter.

Redirect helper + output before redirect = broken login. Any whitespace, dump(), or debug output before $helper->getLoginUrl() / redirect() produces "headers already sent". Use framework redirects and never echo in the login path.

Handle every callback branch. Users cancel (error=access_denied, error_reason=user_denied) and the helper can return null rather than throwing. Both need a redirect back with a flash message, otherwise you get 500s from a bored user clicking "Cancel".

Testing the login flow is the annoying part: state, sessions and redirects. The pragmatic pattern is two tests — one that asserts the login URL contains the right scopes/state, and one that fakes a token exchange + /me response through your HTTP handler — rather than driving the whole redirect dance.

Extension points, in order of usefulness: custom FacebookHttpClientInterface (mock, proxy, retry, logging), custom PersistentData\PersistentDataInterface (state storage), custom PseudoRandomStringGeneratorInterface (rarely needed; random_bytes is fine on PHP 8), and GraphNodeFactory/GraphEdge-level normalization if you want your own DTOs. Subclassing Facebook\Facebook to add forUser()/forPage() helpers is a clean, low-risk seam — much better than scattering token juggling across controllers.

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.
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core