jcf/geocode
Laravel package that automatically geocodes Eloquent models. Add a trait, define address fields, and jcf/geocode resolves latitude and longitude via Google Maps on save, with caching and configurable API credentials for location-aware apps and maps.
What we are actually evaluating. jcf/geocode is a thin Laravel service-provider wrapper around the Google Geocoding API — address→coordinate and coordinate→address, presumably with a config file for the API key plus a facade. It is not a geospatial platform, not a batch geocoder, and not a provider abstraction. The strategic value is low and the integration cost is low; the real dependency we would be taking on is Google, not this package.
Architecture fit. Good fit for a conventional Laravel monolith or modular monolith where a small number of server-side flows need to resolve an address to a point (store locator seeding, delivery-zone assignment, address normalization on write). Poor fit for anything high-volume, offline, event-driven, or multi-provider: the package likely issues synchronous HTTP calls per request, so it belongs behind a queue or a cached lookup, not in a request path with a user waiting.
Integration feasibility. High. A composer require, a published config, an env var, and a smoke test is realistically 0.5–1 day for a working path. The cost is not integration; it is the surrounding governance work — billing account, API restrictions, quota caps, ToS review, and caching policy.
Technical risk. Concentrated in three places, only one of which is the package itself:
| Risk | Severity | Note |
|---|---|---|
| Google ToS constraints on storing/displaying results | High | Geocoding results are commonly restricted to short-lived caching and to display on a Google map; using them to build a permanent internal address→coordinate table or to render on a non-Google map needs legal sign-off. This is the gating item, not the code. |
| Cost/quota exposure | Medium–High | Usage-based billing with a free monthly tier that Google has repriced before; an uncapped key in a retry loop is a budget incident. Requires per-key quota caps and alerting from day one. |
| Maintainer/adoption risk | Medium | 48 stars, 0 dependents, single maintainer. The release metadata (a 2026-08-12 last release) should be verified before we treat the package as actively maintained. If the wrapper goes stale against Laravel 12/13, we fork or replace — roughly a day of work, because the surface is small. |
| Key handling / observability | Low–Medium | Confirm the API key comes from config/env, is never logged or exposed in error payloads, and that failures surface as typed exceptions rather than silent nulls. |
Key questions for the maintainer or a code read (half a day):
OVER_QUERY_LIMIT / OVER_DAILY_LIMIT / REQUEST_DENIED, or does it throw a generic HTTP exception?Verdict: Conditional Go as a prototype-and-replace component. Adopt behind our own Geocoder interface so the package is swappable in an afternoon; do not let it leak into domain code.
Stack fit. Presumes a standard Laravel app with config caching and env-based secrets. It sits alongside — not inside — our domain layer. The intended shape is: domain code calls App\Contracts\Geocoder; one adapter implementation delegates to jcf/geocode; a second adapter can later delegate to Mapbox, HERE, Pelias/self-hosted Nominatim, or a direct Google HTTP call. This costs us perhaps four extra files and removes most of the lock-in.
Migration path. There is nothing to migrate from if we are greenfield; if we already call Google directly with Http::get(...), the package replaces that call site one-for-one and can be introduced service by service. Because there is no data model or schema attached, rollback is deleting a dependency.
Compatibility. Two axes to pin before committing:
Sequencing.
NullGeocoder and a cached decorator. Domain code never imports the package namespace.Maintenance. Low but non-zero, and it is unowned maintenance — a transitive dependency nobody on the team is motivated to patch. Budget a recurring half-day per quarter to check compatibility with Laravel upgrades and Google API changes. The mitigation is the adapter boundary: worst case is a one-day replacement, not a rewrite.
Support. No commercial support, no SLA, single maintainer. Internal support burden is ours. The failure modes we would actually page on are Google's (quota exhaustion, key revocation, billing suspension), not the package's — so runbooks should be written against Google's console, not the library.
Scaling. The package will not be the bottleneck; Google's per-second quota and our budget will be. Three levers, in order: (1) cache aggressively within ToS limits, keyed on normalized address, since geocoding is highly repetitive; (2) move all non-interactive geocoding to queued jobs with backoff so bursts smooth out; (3) pre-resolve and persist only whatever the ToS permits — if persistent storage is disallowed, we need a cache with a bounded TTL and a refresh job, which is a real architectural constraint, not a detail. Assume one outbound HTTP round trip per uncached lookup, 100–300ms, and size accordingly.
Failure modes to design for now:
REQUEST_DENIED from a misconfigured or unrestricted key → looks like a code bug; alert on it distinctly.not_found state in our DTO.Ramp-up. A mid-level Laravel engineer is productive in under a day; the package's API surface is small enough to read in an hour. The real ramp-up is organizational: whoever owns the Google Cloud billing relationship, and whoever owns the ToS interpretation, need to be named before the first production call. If neither is named, this stalls at exactly the wrong moment — a week before launch, with a key that needs a quota cap.
Bottom line for planning: treat this as a ~1-week initiative (2–3 days engineering, the remainder legal/cloud setup and pilot hardening), with medium budget risk and high compliance risk. Approve the spike; gate production traffic on the ToS answer and the quota caps.
How can I help you explore Laravel packages today?