Country, state, and city data for other applications. Use it in either of two ways:
- Install the package in a Laravel app and query
Country,State, andCityin-process. - Run this repo as an HTTP service and let any other system call
/api/countries,/api/states, and/api/cities.
A Laravel app can also keep using Eloquent locally, or call a remote Continental service through Continental\Client.
- PHP 8.2+
- Composer
- Laravel 11 or 12 (when installed as a package)
- SQLite, MySQL, or PostgreSQL
Until the package is on Packagist, require it from GitHub:
composer config repositories.continental vcs https://github.com/aaron-oluk/continental.git
composer require aaron-oluk/continental:dev-mainThe service provider is auto-discovered. Then:
php artisan continental:install --seedThat publishes config/continental.php, runs the geo migrations, and loads 246 countries, 4,121 states, and tens of thousands of cities.
To load data later:
php artisan continental:seeduse Continental\Models\City;
use Continental\Models\Country;
use Continental\Models\State;
$kenya = Country::where('shortname', 'KE')->first();
$states = $kenya->states;
$nairobi = State::where('country_id', $kenya->id)->where('name', 'Nairobi')->first();
$cities = $nairobi->cities;If you only want the models and not the HTTP routes, set CONTINENTAL_ROUTES=false.
Clone this repository and run it as a Laravel app:
composer install
cp .env.example .env
php artisan key:generate
touch database/database.sqlite
php artisan migrate
php artisan continental:seed
php artisan serveOther backends, frontends, and mobile apps can then call the JSON API. CORS is open for api/* by default.
Optional shared secret:
CONTINENTAL_API_TOKEN=your-tokenCallers send Authorization: Bearer your-token or X-Continental-Token: your-token.
| Method | Path | Description |
|---|---|---|
GET |
/api/countries |
All countries. Query: search, shortname |
GET |
/api/countries/{id} |
One country |
GET |
/api/countries/{id}/states |
States in a country |
GET |
/api/states |
Paginated states. Query: country_id, search, per_page |
GET |
/api/states/{id} |
One state (includes country) |
GET |
/api/states/{id}/cities |
Cities in a state. Query: search, per_page, all=1 |
GET |
/api/cities |
Paginated cities. Query: state_id, country_id, search, per_page |
GET |
/api/cities/{id} |
One city (includes state) |
Examples:
curl http://127.0.0.1:8000/api/countries?shortname=US
curl http://127.0.0.1:8000/api/countries/231/states
curl http://127.0.0.1:8000/api/states/3919/cities?per_page=50Health check: GET /up.
The homepage at / is a country → state → city picker that uses these endpoints.
POST, PUT, and DELETE are off by default. Enable them with CONTINENTAL_ALLOW_WRITES=true.
Point another Laravel app at a running Continental instance:
CONTINENTAL_REMOTE_URL=https://geo.example.com
CONTINENTAL_API_TOKEN=your-tokenuse Continental\Client;
$client = app(Client::class);
$countries = $client->countries(['search' => 'Kenya']);
$states = $client->countryStates($countryId);
$cities = $client->stateCities($stateId, ['per_page' => 50]);| Env var | Default | Purpose |
|---|---|---|
CONTINENTAL_ROUTES |
true |
Register HTTP routes |
CONTINENTAL_PREFIX |
api |
URL prefix |
CONTINENTAL_API_TOKEN |
empty | Require a bearer token when set |
CONTINENTAL_ALLOW_WRITES |
false |
Enable create/update/delete routes |
CONTINENTAL_PER_PAGE |
50 |
Default page size for states and cities |
CONTINENTAL_REMOTE_URL |
empty | Base URL for Continental\Client |
Country 1──* State 1──* City
| Table | Fields |
|---|---|
countries |
id, shortname, name, phonecode |
states |
id, name, country_id |
cities |
id, name, state_id |
src/ Package code (provider, models, API, HTTP client)
config/continental.php Package config
database/geo/migrations/ Country / state / city tables
database/geo/seeders/ Geographic seed data
routes/continental.php HTTP API routes
resources/views/index.blade.php Demo picker (this app)
php artisan test