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

bvtterfly/laravel-hashids

A Laravel wrapper for Hashids that encodes numeric IDs into short, unique, obfuscated strings, keeping database keys hidden in URLs and APIs. It offers simple facades, helpers, and config for salts, lengths, and custom alphabets.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

What this package actually is: a thin Laravel service-provider/facade wrapper around the hashids PHP library. Its job is to turn integer primary keys into short, opaque, reversible strings (/orders/x7Kq2 instead of /orders/1042) and back again. Everything below follows from that one sentence, because the properties a TPM cares about — reversibility, secrecy, durability — are determined by Hashids itself, not by this wrapper.

Architecture fit: Narrow and well-scoped. It sits at the HTTP presentation boundary (route-model binding, URL generation, API resource serialisation) and touches nothing else in the stack. That is the good kind of dependency: shallow, substitutable, one call site per usage. The architectural risk is not in the code shape but in the intent — teams routinely adopt "obfuscate IDs" as an implicit security control. It is not one. Hashids is a reversible encoding keyed by a salt; anyone who obtains the salt can decode every ID you have ever issued, and anyone who guesses the alphabet and minimum length has a much cheaper brute-force surface than with a 64-bit random ID. This package must be framed in the design docs as a cosmetic/anti-scraping measure, with authorisation enforced separately and rate limiting in front of any endpoint that will decode a user-supplied string.

Integration feasibility: Technically trivial — one Composer require, config publish, auto-discovered provider. Organisationally harder, for one reason that dominates everything else: the repository is archived and the last release was 2023-03-05 (v1.1.0, adding PHP 8.2 / Laravel 10). 28 stars and a score of 0.23 confirm this is a low-traffic package with no meaningful community. An archived package cannot ship a Laravel 11/12 compatibility bump, cannot patch a vulnerability, and will not accept your PR. Feasibility is therefore not "can we install it" (yes) but "are we willing to own it" (a decision, not a spike).

Technical risk:

  • Abandonment is the headline risk, not a footnote. Archived plus a ~18-month-plus gap since the last release means every framework upgrade after Laravel 10 is a fork-and-patch exercise for us. On a 3-year product horizon, that cost is near-certain.
  • Salt is a secret with a blast radius. It lives in a publishable config file, which makes it easy to commit to git, copy into a shared wiki, or ship in a container image. If it leaks, every ID in every log, email, sitemap, and outbound integration becomes trivially decodable — and rotating it breaks every previously issued URL (see Operational Impact).
  • Salt/minLength/alphabet are encoding parameters, not tunables. Changing any of them re-encodes the entire ID space. There is no migration path for URLs already in the wild.
  • Multiple connections multiply the failure surface. Per-connection salts mean the same database row can present three different public identifiers depending on which binding was used. Misconfiguration is silent — you get a valid-looking hash that decodes to the wrong record or nothing.
  • Hashids itself carries known caveats around very large integer sets and collision behaviour under short minimum lengths. Set minLength deliberately; treat "short" as a UX choice with a correctness floor.
  • Laravel 10 is the declared ceiling. No evidence of support beyond it. The package will become the long pole in your next framework upgrade.
  • Low signal from the ecosystem. 28 stars means few eyes on the code, few documented failure reports, and little chance someone else has already found the bug you are about to hit.

Key questions:

  1. Ownership: are we prepared to fork this into a first-party package (or vendor it) and own it indefinitely? If the answer is no, do not adopt it on any long-lived surface.
  2. Threat model: what property are we buying — "IDs don't leak volume/growth", "URLs look nicer", or "IDs are unguessable"? Only the first two are honest answers for Hashids. If it is the third, stop and use UUIDv7/ULID plus authorisation.
  3. Durability contract: are the URLs we emit with this package expected to be valid forever (emails, invoices, sitemaps, partner links)? If so, what is the salt-rotation policy, and who signs off on breaking every historical link?
  4. Blast radius: is the salt managed in a secret store with rotation, or does it live in a committed .env? Has anyone checked whether it already appears in log output?
  5. Exit cost: how many call sites would need to change to swap this for a first-party encoder? If the answer is "not yet knowable", that is the first task, not a question.
  6. Alternative evaluated? Is there a maintained Laravel Hashids binding with a comparable API, or is in-house (~100 LOC around hashids/hashids) cheaper than the fork liability?

Integration Approach

Stack fit: Good, provided the placement is right. The package belongs in one adapter, not scattered. Concretely: a first-party interface such as PublicIdEncoder with encode(int $id): string / decode(string $publicId): ?int, implemented by a class that wraps this package. Controllers, API resources, and route-model binding depend on the interface only. If we fork later — which, given archived status, is a when rather than an if — we change one bind() in one service provider and the application is untouched. Everything below assumes that adapter exists.

Migration path (there is nothing to migrate from, so this is really an adoption path):

  1. Prove it on one surface. Pick a single low-consequence resource (e.g. an internal admin detail page) and expose its public ID there. Do not touch customer-facing permalinks, invoices, or anything already emailed.
  2. Define the interface first, wire the implementation behind it, then let the vendor package be a detail of the implementation. This sequencing is what makes the archived-repo risk survivable.
  3. Publish and pin the config. minLength chosen deliberately (8+ is a reasonable floor for a public-facing surface); salt generated from a CSPRNG; config values read via config(), never env() at runtime.
  4. Add the decode guard rails before you expose decode at all. Decoding a user-supplied string is an unauthenticated lookup primitive. Any route that decodes must be authenticated, rate-limited, and must 404 on failure — never 500 on a malformed hash.
  5. Only after 1–4 are proven, extend to additional resources. Stop and re-evaluate if the surface list grows past a handful; that is the signal you now have a dependency you cannot fork casually.

Compatibility: Two things to verify in composer.json, cheaply, before anything else — the php constraint and the illuminate/* range. If they are ^10.0-only, this package is a hard gate on your Laravel 11 upgrade and must be scoped as such in the roadmap, not discovered during it. Also confirm behaviour on a clean install, not just composer update on an existing tree — archived packages often fail only in the fresh-install path.

Sequencing (TPM view): Do the adapter and the one-surface pilot now, while the package is still installable on your current framework. The alternative — deferring to "when we do Laravel 11" — bundles a framework upgrade with an abandoned dependency, and the two failure modes are indistinguishable during debugging. If the framework upgrade is within one or two quarters, the honest sequencing is: adopt nothing, build the adapter with a plain in-house Hashids implementation instead, and skip the abandoned wrapper entirely.

Operational Impact

Maintenance: The package is archived, so upstream maintenance is zero by definition — not "slow", not "at risk", ended. Every future fix is ours. The practical consequences are: no framework-compat releases, no security patches, no upstream bug confirmation. If we adopt it, we are adopting a small piece of source code we now maintain. Price that in: it is perhaps 150 lines plus a config file, and forking it into a first-party package is a day's work — which also happens to be the cheapest mitigation available and probably the right shape of decision regardless. The MIT licence permits it.

Support: None. There is no maintainer to escalate to, no SLA, no security contact, no issue triage — verified archived repositories take no issues or PRs. Our support obligations therefore become fully internal, and any incident caused by the salt, the alphabet, or a decode edge case lands on our on-call, not theirs. Make sure whoever accepts that risk knows they are accepting it.

Scaling: Runtime cost is negligible — Hashids is pure arithmetic and string manipulation, microseconds per call, no I/O, no state. Cache it in an accessor if you call it in a loop, but it is not a performance concern. The real scaling concerns are two:

  • Organisational: every resource that gets a public ID is another place the encoding contract is depended upon. Grow the surface slowly.
  • Configuration coupling: per-connection salts mean horizontal scale-out requires the same config everywhere. This is handled by normal config management, but a container rolled with a stale image and an old salt will produce public IDs that are valid-looking and wrong. That is worse than an error — it is silent data confusion.

Failure modes (ranked by how badly they hurt):

  1. Salt rotation or loss. Rotating the salt re-encodes the entire ID space. Every previously issued URL — emails, invoices, PDFs, sitemaps, partner API responses, anything a customer bookmarked — resolves to the wrong record or 404s. The salt is now a piece of long-lived infrastructure, and nobody should be allowed to change it without an approved migration plan. Loss is worse still: your public identifiers become permanently undecodable. Back it up alongside your other secrets, and record it in the runbook.
  2. Salt disclosure. Silent and irreversible. All public IDs become enumerable to whoever has it. Mitigations are upstream: secret store, no logging of the config, no committing .env, and a detection story (rate-limit anomalies on decode endpoints) because you will not get a clean alert.
  3. Misuse as an authorisation control. Someone writes if ($encoder->decode($id) === $record->id) and treats it as access control. It is not — it is a lookup, not a permission check. Any endpoint that decodes an arbitrary string must still check ownership and authorise the caller.
  4. Multi-connection drift. The wrong connection binding produces a valid-looking hash for the wrong tenant or database. Enforce the connection per call site explicitly rather than relying on a default.
  5. Framework-upgrade lockout. Archived package plus Laravel 11+ equals either a fork or a rushed replacement, and it will surface mid-upgrade when the blast radius is largest. The adapter from Integration Approach is the mitigation; without it, this becomes an emergency.
  6. Minimum-length erosion. Someone "tidies" minLength down to 4 because the URLs look nicer. Short hashes mean a smaller output space, which makes enumeration cheap even without the salt. Treat config changes here as schema changes: reviewed, tested, never incidental.

Ramp-up: Low for engineers — the API is two method calls and the config is three keys; a competent Laravel developer is productive in under an hour. The ramp-up that actually costs money is product and security alignment, not engineering: agreeing the durability contract for public URLs, agreeing what this does and does not protect against, and agreeing who owns the fork. Budget half a day of TPM/security/eng time for that conversation before any code is written. The engineering is a sprint task; the decision is the deliverable. Until that conversation happens, treat this package as acceptable for a contained, internal, low-consequence surface behind a first-party adapter — and unacceptable as a load-bearing identifier scheme for customer-facing permalinks, which is the opposite of what most teams reach for it for.

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.
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast