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

Laravel Dropbox Laravel Package

dcblogdev/laravel-dropbox

dcblogdev/laravel-dropbox is a Laravel wrapper for the Dropbox API v2, offering a clean facade for OAuth, file uploads and downloads, folder listing, metadata, and sharing operations. Configure credentials once and manage Dropbox content directly from your Laravel app.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and wire it up (10 minutes):

composer require dcblogdev/laravel-dropbox
php artisan vendor:publish --provider="Dcblogdev\Dropbox\DropboxServiceProvider"
php artisan migrate   # package ships a tokens table migration

Add credentials to .env (names come from config/dropbox.php — read the published file, it is short and is the source of truth for key names):

DROPBOX_CLIENT_ID=
DROPBOX_CLIENT_SECRET=
DROPBOX_REDIRECT_URI="${APP_URL}/dropbox/callback"

Create the Dropbox app first. In the Dropbox App Console pick Scoped access → App folder (least privilege) or Full Dropbox, then paste the exact redirect URI. Choose the scopes you need now — adding scopes later forces every user to re-consent.

Where to look first:

  1. config/dropbox.php — credentials, redirect URI, and any token-model/table settings.
  2. src/Dropbox.php + the Dropbox facade in src/Facades/ — the whole public surface is small, so read it rather than guessing method names.
  3. The migrations directory — confirm the tokens table name before you write queries against it.

First use case — the OAuth round trip. Two routes, two closures: one that redirects the user to the generated authorisation URL, one for the callback that exchanges ?code= for a token and persists it. Immediately after, call the account/profile endpoint and dump the result. If you get an email/display name back, your credentials, scopes, and redirect URI are all correct and everything else is just API calls.

If you're building a single-tenant back-office integration, do this dance once manually, keep the stored refresh token, and never build UI for it.

Implementation Patterns

Wrap the facade, don't sprinkle it. Put a thin DropboxService in app/Services that injects the client and exposes the three or four operations your app actually performs (uploadInvoice, listProjectFiles, createShareLink). Day-to-day you call your own service; in tests you swap it for a fake. Direct facade calls (Dropbox::…) scattered across controllers are the main reason these integrations become untestable.

Token refresh belongs in one place. Dropbox access tokens are short-lived (four hours) and you rely on the refresh token. Either refresh proactively before a batch of calls, or wrap every call in a "catch auth error → refresh → retry once" helper. Never let a 401 surface as a 500 to the user — catch it and redirect them through the connect flow again.

Push file work onto the queue. Uploads and downloads are seconds-to-minutes operations with real failure modes. Pattern:

UploadToDropbox::dispatch($localPath, $dropboxPath)->onQueue('dropbox');

Use the package's chunked/upload-session support (or the underlying SDK directly) for anything over ~150 MB, and stream downloads to a temp file instead of pulling the whole body into a string — a getContent() on a 500 MB video is an OOM kill, not an exception.

Sync with cursors, not full listings. For any recurring sync (scheduled command, ->hourly()), store the cursor returned by the initial list call and use the continue endpoint on subsequent runs. Full re-listing works fine at 50 files and falls over at 50,000 — plus it burns rate limit.

Webhooks over polling where possible. Dropbox will POST to your app when a user's files change. Verify the signature with your app secret, return 200 immediately, and dispatch a job that does the cursor sync. This turns your polling command into a fallback for missed deliveries rather than the primary mechanism.

Multi-tenant. If each of your users connects their own Dropbox, add a user_id/team_id column to the package's token table (or your own wrapper table) and resolve the right token per request. A single global token row works only until the second customer.

Escape hatch. The package is a convenience layer over the HTTP API. For endpoints it doesn't wrap (sharing/create_shared_link_with_settings, files/search_v2, paper, etc.), get the authenticated client from the package and issue the request yourself — don't fork the package for one endpoint.

Gotchas and Tips

v3.0.8 is a compatibility-only release (Laravel 13 added to the composer constraint). There are no API or config changes, so upgrading from v3.0.7 is a composer update dcblogdev/laravel-dropbox and re-run your test suite. Verify your PHP version satisfies Laravel 13's floor before bumping on a shared host, and if you're pinned to a Laravel 13 skeleton make sure the rest of your dependency graph has caught up too — this package being ready doesn't mean everything else is.

config:clear after touching .env. Cached config is the number one cause of "my new client ID isn't being used". In production, remember the deploy must re-cache, not just restart.

Redirect URI must match byte-for-byte — scheme, host, trailing slash, http vs https. Dropbox rejects the mismatch with a generic error page before your callback ever fires, so you get no log entry to debug from.

Scopes are permanent-ish. Changing access type (App folder ↔ Full Dropbox) or adding scopes invalidates existing grants. Users must reconnect. Design for a "reconnect your account" banner from day one rather than discovering this in production.

Development-status apps are constrained. They cap linked users and behave differently from production apps on token longevity. Apply for production access well before launch; it is a review process, not a toggle, and your tokens will need to be re-issued afterwards.

Paths are case-insensitive but stored as typed. Compare using path_lower from API responses, and normalise leading slashes yourself — folder and /folder are not interchangeable in every endpoint.

Pagination is real. List responses return a has_more flag and a cursor regardless of folder size. Loop until has_more is false or you'll silently process the first 2,000 entries forever.

Rate limits return 429 with Retry-After. Honour it with an exponential backoff in queued jobs ($job->release($seconds)), otherwise a large sync cascades into a wall of failures and, if you haven't set tries, drops work permanently.

Revoked tokens are normal, not exceptional. Users revoke app access from their Dropbox settings. Catch the auth exception, mark the token row as disconnected, and route them back through connect — don't retry.

Publishing migrations twice into an app that already has a dropbox_tokens table will collide. Check database/migrations before running vendor:publish on an existing install.

Log the request path and the Dropbox error tag (path/not_found, too_many_write_operations) in your catch block. The SDK exception messages are terse, and too_many_write_operations in particular needs a retry-with-jitter rather than a user-facing error.

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.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle