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

Hashids Laravel Package

torann/hashids

Laravel wrapper for Hashids that encodes numeric IDs into short, unique, non-sequential strings and decodes them back, helping hide database IDs in URLs and APIs with configurable salt, length, alphabet, and simple Laravel integration.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit torann/hashids is deliberately thin glue — a service provider, a Hashids facade, and a publishable config array wrapping hashids/hashids. Architecturally that's the right shape for the problem it solves: it's an edge/serialization concern, so it lands at the URL and API-resource boundary with no schema change, no data migration, and a cheap revert. A TPM should treat it as a bounded, reversible change inside one context rather than a platform decision.

Two architectural constraints drive everything:

  • It is a translation layer, not a key. The package works cleanly when it maps internal integer PKs to external handles on the way out and back on the way in. It goes wrong the moment it's pushed inward — hashids stored in columns, used in joins, emitted in contracts. That's the failure mode that turns a two-day change into a permanent dependency, so it must be written down as a non-goal up front.
  • Reversibility is the product decision. Given the salt, anyone can decode everything. If the requirement is obfuscation ("stop broadcasting order volume / growth rate"), this fits. If the requirement is unguessability or authorization, this is the wrong tool and ULIDs/UUIDv7 or server-side opaque tokens are the answer. This framing is the single highest-leverage thing to nail before anyone opens a PR — it determines whether this is a days-long add or a redesign.

Also note the ordering constraint: hashids destroys sortability. Any public surface needing range queries or sortable IDs is out of scope by definition.

Integration feasibility Mechanically trivial: composer require, vendor:publish, a salt in an env var read only inside config/hashids.php. The entire public API is encode(), decode(), encodeHex(), decodeHex(), connection(). One engineer, ~1–2 days for the adapter plus the first surface; ~1 week to roll through several surfaces with redirects and tests.

Effort scales with number of surfaces converted, not with package complexity. Budget by counting touchpoints (models, resources, form requests, redirect routes), and reserve the bulk of the estimate for the legacy-URL tail rather than the encoding itself.

Zero listed dependents means no transitive upgrade drag — and also no community validation. Feasibility on Laravel 11/12 is high, but verify the illuminate/* constraint in composer.json against the deployed PHP floor rather than trusting the "Laravel 12 support" changelog line.

Technical risk The dominant risk is misuse, not malfunction. Ranked:

Risk Impact Likelihood Mitigation
Treated as a security control High — IDOR exposure assumed fixed when it isn't Medium-high State "obfuscation, not authorization" in the ticket; authorization stays server-side
Salt rotation without a product plan High — every outstanding link dies at once Medium Written rotation policy + owner + dual-read + comms before adoption
decode() returns [], not null/false Medium — null derefs, 500s High Adapter returning ?int; validate shape in form requests → 422
Salt leaked via repo/logs/stack traces High — full enumeration Low-medium Secret manager, no logging, treat as sensitive config
Fixed-width assumptions (length is a minimum) Medium — breaks at volume in UI/regex/schema Medium Variable-length strings everywhere; no char(10)
env() read at runtime → null after config:cache Medium — works locally, ships Medium Env only in config; boot-time assertion the salt resolves

Vendor risk is nominal in blast radius: ~54 stars, single maintainer, no dependents, but the fallback is a one-file change if the adapter boundary exists. Price it as "assume no upstream support SLA; own the exit."

Key questions

  1. Is the requirement obfuscation or unguessability? (Determines this package vs. ULID/UUIDv7 vs. opaque tokens.)
  2. Enumerated list of surfaces to convert — and, explicitly, the excluded set (admin tooling, service-to-service IDs, join tables).
  3. Who owns the salt: where it lives, who can rotate it, what the user-facing recovery path is, and do we need a dual-read connection during transition?
  4. Does anything downstream assume fixed-width references — partner APIs, char(n) columns, {10} regexes, printed labels?
  5. Do we currently emit raw integer IDs to external API consumers? If so, this is a breaking contract change needing versioning and comms, not a silent swap.
  6. Does the deploy pipeline re-run config:cache after env changes, and do all environments hold identical salt/alphabet per connection?

Integration Approach

Stack fit Native Laravel, auto-discovered, single composer dependency on hashids/hashids plus illuminate support. No infrastructure, no service, no migration. It drops into any 11/12 app as a version bump + a config file + one adapter class, which is exactly the profile you want for a hardening item that shouldn't compete with roadmap work.

Migration path Sequenced by surface risk, not by code layer:

  • Phase 0 — Adapter. A two-method PublicId class (encode(int|string, ?string $connection): string, decode(string, ?string $connection): ?int). Every call site goes through it. This is the reversibility insurance and the only long-term extension point; it's what makes swapping to ULIDs or dropping to the raw library a one-file change.
  • Phase 1 — One surface, highest value / lowest risk. A single public reference: order reference in a URL, share link, tracking number. Encode outbound, decode inbound, validate identifier shape in a form request so bad input is a 422 rather than a 500 three layers down.
  • Phase 2 — Model route keys. A HasPublicId trait overriding getRouteKey() / resolveRouteBinding() on a named list of models. Not all models — every extra encode is indirection with no product payoff.
  • Phase 3 — API resources. Encode in the resource, never emit the raw id alongside the encoded one; shipping both defeats the purpose and someone will build against the integer.
  • Phase 4 — Legacy compatibility. Keep integer URLs alive with a redirect that decodes and 301s to the encoded form. Watch legacy hit counts and 404 rate; retire when the tail flattens. Emails, bookmarks, and cached pages carry the old form for months.

Explicit non-goals: no hashid columns, no schema migrations, no seeded hashids. The one documented exception is a denormalized public_ref for records whose external reference must survive a salt rotation — a deliberate decision, not a convenience.

Compatibility 3.0.6 adds Laravel 12 as a patch, implying no migration cost for teams already on 3.x — treat it as additive to 11 and check the actual constraint rather than inferring from the release note. Teams on 2.x get nothing here and would need to absorb 3.0.0 breaking changes; do not bundle that into a framework upgrade tranche.

Two cross-environment hazards to bake into the plan: salt and alphabet must match across all environments per connection (drift produces staging-passes/prod-404s), and the test suite must pin HASHIDS_SALT / HASHIDS_LENGTH in phpunit.xml with round-trip assertions rather than literal strings — a random per-run salt makes output untestable and a hardcoded hashid breaks on the first rotation.

Sequencing Slot inside an existing Laravel 12 upgrade or an anti-enumeration hardening tranche — not a standalone project. Order of operations is non-negotiable: salt policy written first, then adapter + tests, then one surface in production, then measure, then expand, then legacy redirect, then retire. Gate each phase on a metric (404 rate, legacy URL hits, decode-failure rate) rather than on a calendar date.

Operational Impact

Maintenance Near-zero code maintenance: the wrapper is three files over a stable algorithm. The real carrying cost is config lifecycle — salt in a secret manager, deploy-time config:cache, per-environment parity, and a boot-time assertion that the resolved salt is non-empty and the alphabet is valid (≥16 unique chars, no duplicates). Assign a named owner to the salt. Without an owner, rotation becomes an outage with no on-call playbook.

Support There is no upstream support relationship to lean on: one maintainer, low star count, no dependents. Document the fallback before adoption — call hashids/hashids directly through the adapter, or fork the glue — so it's a known option during an incident rather than a discovery. The BSD-2 licence and tiny surface are what make that acceptable.

Scaling CPU-negligible — pure string arithmetic, no I/O, safe on hot paths at high QPS. The scaling concern is output shape, not throughput: length is a floor that pads and grows with input magnitude, so any fixed-width assumption in a column type, validation regex, or UI layout breaks as volume climbs. Multi-connection is the scaling axis for product surface (short coupon codes vs. longer tenant IDs), not for load.

Failure modes

  • Garbage input → [] → undefined index → 500. Adapter returns ?int; form requests validate shape first.
  • Salt rotation or alphabet change → every outstanding link breaks simultaneously. This is a product event with comms, dual-read via a second named connection during the transition, and a documented recovery path — not a deploy step.
  • Salt leak → all IDs enumerable. Secret manager, never logged, never in stack traces; authorization remains the only real gate.
  • env() evaluated at runtime → silently null post-config:cache. Env lookups confined to config/hashids.php; boot assertion catches it.
  • Connection-name mismatch → silent garbage or 404. Pass the connection explicitly in the adapter; never let a controller guess.
  • Upstream abandonment → no patches for the glue. Documented fork path; the surface is small enough to own outright.

Ramp-up An engineer is productive in under a day; adapter + tests + first surface in one to two. The expensive knowledge isn't the API — it's the failure modes, so put "what hashids is not," "how decode fails," and "what rotation does" in a runbook rather than in tribal memory. Give the team a half-hour walkthrough of that runbook. Support needs something different: a rotation policy and a documented response for a customer whose emailed link 404s post-rotation.

Pilot exit criteria: one surface live; legacy-URL metrics flat over ~4 weeks; decode-failure rate inside threshold; salt in the secret manager with a named owner and a written rotation runbook on file.

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.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings