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 Dub Laravel Package

jeffersongoncalves/laravel-dub

A Laravel integration for the Dub.co link management API that lets you create, update, and organize short links and retrieve click analytics directly from your PHP application using a clean, fluent interface. Simplify branded link sharing and tracking.

View on GitHub
Deep Wiki
Context7

Getting Started

  • Require the package with Composer, then publish its config if you want to change defaults: php artisan vendor:publish --tag="dub-config" (check the package README for the exact tag — the config file is the first thing worth opening).
  • Add the three supported keys to .env:
    DUB_API_KEY=your_dub_api_key
    DUB_API_URL=https://api.dub.co
    DUB_TIMEOUT=10
    
    DUB_API_URL and DUB_TIMEOUT normally never change; only DUB_API_KEY is per-environment. Keep a separate key per environment/workspace so staging traffic doesn't pollute production analytics.
  • First use case: shorten a URL at the point where a user shares something, then read analytics back on a dashboard. A minimal first pass looks like:
    $link = Dub::createLink([
        'url' => $post->publicUrl(),
        'domain' => 'yourdomain.co',
        'key' => 'post-' . $post->id,   // deterministic keys make retries safe
        'tags' => ['post'],
    ]);
    
    Then Dub::analytics($linkId, [...]) to render clicks. Because everything is static, there is nothing to register, inject, or boot — you can call it from a tinker session as soon as the env vars are set, which makes it a fast package to evaluate.
  • If you only need one or two calls (link creation is the 80% case), don't build an abstraction yet. Add one once you hit a second call site or need to test.

Implementation Patterns

  • Wrap the statics in your own thin service/action class. Dub:: is a static client, so it's the one thing in your app you can't swap via the container. A three-method ShortLinkService (create, forModel, stats) pays for itself the first time you need a fake in tests or a second provider.
  • Deterministic keys for anything user-generated. Derive key from your model (post-{id}, invite-{uuid}) rather than letting Dub generate one, so retried jobs and double-submits converge on the same link instead of creating duplicates. Store the returned link id/key/shortLink on your model — the API is the source of truth, but you don't want a network call to render a share button.
  • Bulk imports: chunk, then queue. bulkCreateLinks takes an array, so chunk your collection (100–500 at a time), dispatch one job per chunk, and let the queue absorb partial failures. Log the failures per chunk rather than aborting the whole import.
  • Listing/pagination. listLinks is paginated; drive it from a queued sync job that walks pages and upserts locally, rather than calling it in a request. This is how you keep a local short_links table in sync for search, filtering, and joins.
  • Analytics belong behind a cache. analytics, analyticsByCountry, and analyticsByDevice are read-only and change slowly — cache for 5–15 minutes keyed on link + range, and render dashboards from cache. Never call them per row in a table loop; batch by fetching per-link aggregates in a job and store them in your own table if you need per-row numbers.
  • Anything that writes goes through the queue if it's in a user-facing path. A 429 or a slow response on link creation should degrade to "link pending", not a 500 on a share button. Dispatch the job, show the canonical URL until the short link lands.
  • Update in place, don't recreate. updateLink for destination/UTM/tag changes keeps the short URL and its accumulated analytics intact; deleting and recreating resets history and breaks links already in the wild.
  • Environment/URL switching is config-only, which makes it easy to point a test suite or a local environment at a sandbox workspace — set DUB_API_KEY in phpunit.xml and keep destructive integration tests off production keys.

Gotchas and Tips

  • Static means untestable by default. Confirm whether the package ships a fake (Dub::fake()). If it doesn't, the practical escape hatch is Http::fake() on the underlying Guzzle/Laravel HTTP calls and asserting the outgoing payload — brittle, but workable. This is the strongest argument for wrapping the client early.
  • DubRateLimitException is a package-specific exception, not an Illuminate\Http\Client one. Catch it explicitly before any broad catch (\Throwable); a swallowed 429 looks like a silent data-loss bug. In jobs, release with a delay based on the Retry-After value the exception carries rather than a fixed backoff — the package has already parsed it for you, so use it.
  • Rate limits are per API key/workspace, not per process. Multiple queue workers share the same budget, so 10 workers retrying in lockstep will just re-trigger 429s. Cap concurrency on the Dub queue, or use exponential backoff with jitter on top of Retry-After.
  • DUB_TIMEOUT applies per request. Bulk and list calls are the ones that actually brush up against it; raise it for those jobs if you chunk large payloads. A timeout is not a "didn't happen" signal — the link may have been created server-side, which is another reason to use deterministic keys.
  • env() is only read at config load. After php artisan config:cache in production, changing DUB_API_KEY in .env does nothing until you re-cache. Classic "why is it still hitting the old workspace" moment.
  • Deletes are real. deleteLink removes the link and makes the short URL dead — no soft-delete from your side unless you track it. Prefer deactivating/disabling links when users "remove" a share, and reserve deleteLink for cleanup jobs and test data.
  • Analytics numbers won't match your app's own click tracking. Dub counts at the edge, with its own bot filtering and timezone/day boundaries. Pick one source of truth for reporting and note the difference, otherwise someone will file a bug about a 10% gap.
  • Extension points are limited by design. There's no manager, driver, or event hook in this release — the config keys and the public static methods are the whole surface. If you need retries, caching, or model persistence, build them in your wrapper class rather than expecting the package to grow them.
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.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
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