rinodrummer/laravel-istat-geography-api

A package that exposes a filterable API to explore the ISTAT dataset exposed by plin-code/laravel-istat-geography

Maintainers

Package info

github.com/rinodrummer/laravel-istat-geography-api

pkg:composer/rinodrummer/laravel-istat-geography-api

Transparency log

Fund package maintenance!

rinodrummer

Statistics

Installs: 2

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v0.1.1 2026-08-10 14:10 UTC

This package is auto-updated.

Last update: 2026-08-10 14:52:39 UTC


README

Latest Version on Packagist GitHub Tests Action Status GitHub Code Style Action Status Total Downloads

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 on name. 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 whose postal_codes range covers the value, so 00150 finds Roma (00118-00199).
  • include=a,b — eager-loads relations, restricted to the includes listed above
  • per_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

License

The MIT License (MIT). Please see License File for more information.