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

Google Api Bundle Laravel Package

happyr/google-api-bundle

Symfony bundle that integrates the official Google APIs PHP client. It configures and exposes a ready-to-use Google_Client service, with bundle settings for application credentials, OAuth, and API access, simplifying Google service integration in Symfony applications.

View on GitHub
Deep Wiki
Context7

Getting Started

happyr/google-api-bundle is a Symfony bundle, not a Laravel package. There is no service provider, no facade, and no package auto-discovery. In a Laravel app the practical starting point is to not install the bundle and instead install its underlying dependency directly:

composer require google/apiclient

Use the bundle as a reference implementation for configuration and client/service construction. The files worth reading first are:

  • DependencyInjection/Configuration.php — the config keys the bundle exposes (client_id, client_secret, redirect_uri, developer_key, application_name, scopes, etc.)
  • Resources/config/services.* — the service definitions. The primary service is happyr.google.api.client; as of 2.3.0 there are also service definitions for Drive and Slides, which are the best reference for how the bundle wires a Google\Service\* object onto a configured client
  • README.md — the intended Symfony usage flow

The first use case is standard OAuth:

use Google\Client;

$client = new Client();
$client->setClientId(config('services.google.client_id'));
$client->setClientSecret(config('services.google.client_secret'));
$client->setRedirectUri(config('services.google.redirect'));
$client->setApplicationName(config('app.name'));
$client->setScopes(['https://www.googleapis.com/auth/calendar']);
$client->setAccessType('offline');
$client->setPrompt('consent');

$authUrl = $client->createAuthUrl();

Then exchange the callback code, persist the token, and instantiate a Google service:

$token = $client->fetchAccessTokenWithAuthCode($request->get('code'));
$client->setAccessToken($token);

$calendar = new \Google\Service\Calendar($client);

In Laravel, create a singleton binding in a service provider so the configured client can be injected. Mirror the bundle's config keys in config/services.php and .env.

Implementation Patterns

Use a config-first client factory. Keep Google credential setup in one place, not scattered across controllers. The bundle's main value is exactly this: a preconfigured Google\Client. In Laravel:

$this->app->singleton(Client::class, function () {
    $client = new Client();
    $client->setClientId(config('services.google.client_id'));
    $client->setClientSecret(config('services.google.client_secret'));
    $client->setRedirectUri(config('services.google.redirect'));
    $client->setApplicationName(config('app.name'));
    $client->setScopes(config('services.google.scopes'));
    $client->setAccessType('offline');
    $client->setPrompt('consent');

    return $client;
});

Do not put per-user access tokens on that singleton. Resolve the singleton, clone/reconfigure it, then call setAccessToken() with the current user's token.

Persist the whole token array, not just the access token. Google returns an array with access_token, expires_in, refresh_token, created, and sometimes id_token. Store it as JSON (encrypted cast or encrypted column) and rehydrate it:

$client->setAccessToken($account->google_token); // array or JSON string
if ($client->isAccessTokenExpired()) {
    $client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
    $account->update(['google_token' => $client->getAccessToken()]);
}

Wrap service construction in your own factory. The bundle ships a small, fixed set of services — the client, plus Drive and Slides since 2.3.0 — not one service per Google API. Mirror its pattern and add the APIs you actually use:

class GoogleServiceFactory
{
    public function __construct(private Client $client) {}

    public function calendar(): Calendar
    {
        return new Calendar($this->client);
    }

    public function drive(): Drive
    {
        return new Drive($this->client);
    }

    public function slides(): Slides
    {
        return new Slides($this->client);
    }
}

The Drive/Slides definitions in 2.3.0 are worth reading for scope choices — Drive and Slides both typically need https://www.googleapis.com/auth/drive (or a narrower drive.file), and Slides requires the Drive scope for anything beyond presentation content itself. Copy their scope list rather than guessing.

Queue jobs should carry an identifier, not the client. Serializing Google\Client or Google service objects into jobs is fragile — and the bundle's service definitions do not change that, since they are container singletons in a Symfony request lifecycle. Pass a user/account ID, reload the token in the job, rebuild the client, and execute the API call.

Use service accounts for server-to-server work. For Workspace/domain-wide delegation:

$client->setAuthConfig(storage_path('app/google/service-account.json'));
$client->setScopes(['https://www.googleapis.com/auth/admin.directory.user']);
$client->setSubject('[email protected]');

This is the pattern to use when no end-user OAuth flow is involved — including for Drive/Slides automation against shared drives.

Request incremental scopes. Start with the minimum scopes needed and add more with setScopes() when the user enables a feature. This reduces consent friction and avoids requesting sensitive scopes up front. Drive in particular has restricted scopes that trigger Google verification requirements for public apps.

Test by mocking at the client/service boundary. Bind a fake Google\Client in the container, or inject a Guzzle mock handler into the Google client's HTTP stack where supported. Assert that your code calls the right methods rather than making real Google requests.

Gotchas and Tips

  • It is not a Laravel package. Installing it in Laravel will not register anything useful. There is no happyr.google.api.client service in Laravel's container, and 2.3.0's Drive/Slides services are Symfony-only too. Treat the bundle as documentation/reference only.
  • Maintenance status is mixed, not dead. The 2.3.0 release adding Drive and Slides shows the bundle is still receiving updates, so don't dismiss it outright — but verify the composer.json Symfony and google/apiclient constraints before assuming it fits a modern stack. Even when it does, it is still Symfony-shaped glue; in Laravel use current google/apiclient plus a small in-app wrapper.
  • The 2.3.0 additions are narrow. Drive and Slides do not mean "a service per Google API." Calendar, Gmail, Sheets, and everything else still need to be constructed yourself — read the new definitions as a template, not as a complete catalogue.
  • Class names changed. Old code uses Google_Client and Google_Service_Calendar; modern code should use Google\Client and Google\Service\Calendar. The old names are compatibility aliases and are deprecated. If you copy examples from older bundle docs or issues, translate the names first.
  • The refresh token is easy to lose. Google typically returns refresh_token only on the first consent or when prompt=consent / approval_prompt=force is set. If you do not persist it immediately, the user must re-consent. Always store the full token response.
  • Redirect URI must match exactly. Scheme, host, port, path, and trailing slash must match the Google Cloud Console entry. localhost and 127.0.0.1 are different. This is the most common cause of redirect_uri_mismatch.
  • Access tokens expire in about an hour. Check isAccessTokenExpired() and refresh before the call, not after a 401. Persist the refreshed token; otherwise every request triggers a refresh.
  • Scope strings are exact URLs. A missing or mistyped scope produces a 403 later, not necessarily at consent time. Log granted scopes during development — this matters more now that Drive/Slides are in play, since a missing Drive scope fails deep inside Slides calls.
  • Do not serialize the client. Rebuild it per request/job from config and stored token. Serializing Google client/service objects can break across deployments and library upgrades.
  • Guzzle conflicts are possible. google/apiclient pulls its own Guzzle constraints. If the old bundle pins an older Guzzle, it may conflict with Laravel's HTTP client dependencies. Install the library directly and let Composer resolve modern versions.
  • Service account clock skew and delegation. Domain-wide delegation requires the sub user to be a Workspace admin and the service account client ID to be authorized in the admin console. Clock skew can cause JWT errors; keep servers synced.
  • Handle rate limits and retries. Google APIs return 429/503 under quota pressure; Drive and Slides hit per-user and per-project quotas quickly on bulk operations. Catch Google\Service\Exception, inspect the code, and retry with exponential backoff for idempotent operations.
  • Debug HTTP traffic deliberately. Inject a logging Guzzle handler or configure the underlying HTTP client in local environments. Do not enable verbose request logging in production—tokens and user data can leak into logs.
  • Extension point if you are actually on Symfony: override the happyr.google.api.client service definition (or the Drive/Slides definitions) with your own factory, or decorate them. Do not fork the bundle just to change client construction. In Laravel, skip the bundle and implement the same pattern in a service provider.
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.
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels
wazza/laravel-db-encryption
hokny/laravel-generator
vonsogt/backup
jengo/queues
jeremykenedy/laravel-observability
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums