rinodrummer / laravel-istat-geography-api
A package that exposes a filterable API to explore the ISTAT dataset exposed by plin-code/laravel-istat-geography
Package info
github.com/rinodrummer/laravel-istat-geography-api
pkg:composer/rinodrummer/laravel-istat-geography-api
Fund package maintenance!
Requires
- php: ^8.3
- illuminate/contracts: ^12.0||^13.0
- plin-code/laravel-istat-geography: ^1.3
- spatie/laravel-package-tools: ^1.16
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/pint: ^1.14
- nunomaduro/collision: ^8.8
- orchestra/testbench: ^11.0.0||^10.0.0
- pestphp/pest: ^4.0
- pestphp/pest-plugin-arch: ^4.0
- pestphp/pest-plugin-laravel: ^4.0
- phpstan/extension-installer: ^1.4
- phpstan/phpstan-deprecation-rules: ^2.0
- phpstan/phpstan-phpunit: ^2.0
- spatie/laravel-ray: ^1.35
This package is auto-updated.
Last update: 2026-08-10 14:52:39 UTC
README
A read-only HTTP API over the Italian geography dataset (regions, provinces and municipalities) that
plin-code/laravel-istat-geography imports from ISTAT.
That package gives you the models and the import commands; this one exposes them as searchable, filterable and paginated JSON endpoints — either on the routes it registers for you, or wherever you decide to put them.
GET /municipalities?search=Tori&filter[postal_code]=10121&include=province&per_page=25
Installation
You can install the package via composer:
composer require rinodrummer/laravel-istat-geography-api
This package builds on plin-code/laravel-istat-geography,
which Composer installs for you. Its migrations are publish-only, so publish them, migrate, and import the dataset
before calling the API:
php artisan vendor:publish --tag="istat-geography-migrations"
php artisan migrate
php artisan geography:import
See the upstream README for the import options and for keeping the data up to date (geography:update).
You can publish the config file with:
php artisan vendor:publish --tag="istat-geography-api-config"
This is the contents of the published config file:
return [ 'routes' => [ 'prefix' => null, 'middlewares' => ['api', 'throttle:60,1'], 'name' => 'istat-geography-api', ], ];
The name key prefixes the route names, so the endpoints are reachable as
route('istat-geography-api.regions.index'). Set it to null to register them unnamed.
With that config in place the endpoints are registered for you. Set routes to null to take over registration
yourself — see Registering the routes yourself.
Caching
The package does not cache anything on its own, and deliberately so: the responses are cheap to produce, while the
data changes only when you re-run the upstream import. The caching that pays off here happens in front of the API,
and Laravel already ships it — add its cache.headers middleware to the group:
'middlewares' => ['api', 'throttle:60,1', 'cache.headers:public;max_age=86400;etag'],
Browsers and CDNs then stop asking, and conditional requests come back as 304 Not Modified. Keep max_age shorter
than the interval at which you refresh the dataset, or bump it when you run geography:import — nothing invalidates
those cached responses for you.
Usage
The package registers three read-only endpoints:
| Endpoint | Filters | Includes |
|---|---|---|
GET /regions |
istat_code |
provinces |
GET /provinces |
region_id, code, istat_code |
region, municipalities |
GET /municipalities |
province_id, istat_code, bel_code, postal_code |
province |
All of them accept the same query parameters:
search=— partial, case-insensitive match onname. Results are ranked: exact match first, then names starting with the term, then names merely containing it, alphabetical within each group.filter[field]=value— exact match, restricted to the filters listed above.filter[postal_code]also matches municipalities whosepostal_codesrange covers the value, so00150finds Roma (00118-00199).include=a,b— eager-loads relations, restricted to the includes listed aboveper_page=— page size, defaults to 15
Anything not in the allow-lists is silently ignored rather than rejected.
GET /municipalities?search=Tori&filter[province_id]=...&include=province&per_page=25
Registering the routes yourself
If the single configured group is not enough — different middleware per resource, a versioned prefix, a separate
admin area — set routes to null in the config and call the facade from your own route files instead. The
endpoints are registered wherever you call it, so they inherit the prefix, middleware and domain of the surrounding
group:
use RinoDrummer\LaravelIstatGeographyApi\Facades\IstatGeographyApi; Route::prefix('api/v1')->middleware('api')->group(function () { IstatGeographyApi::routes(); });
Pass false to leave a resource out of that group, and name: to name the routes it registers. The same resource
can be registered in several groups, as long as each group gets its own name prefix:
// Public: only municipalities, rate limited. Route::prefix('api')->middleware(['api', 'throttle:60,1'])->group(function () { IstatGeographyApi::routes(regions: false, provinces: false, name: 'api'); }); // Admin: everything, behind auth. Route::prefix('admin')->middleware(['api', 'auth:sanctum'])->group(function () { IstatGeographyApi::routes(name: 'admin'); });
That gives you route('api.municipalities.index') and route('admin.municipalities.index'). Leave name: out to
register the routes unnamed — two groups sharing a route name would collide.
Testing
composer test
Changelog
Please see CHANGELOG for more information on what has changed recently.
Credits
- Gennaro Landolfi
- All Contributors
- Daniele Barbaro, for the upstream
plin-code/laravel-istat-geographypackage
License
The MIT License (MIT). Please see License File for more information.