2026
Area Risk Snapshot
Type in a UK postcode, get an honest summary of recently reported street crime nearby, from police.uk open data.
- Laravel 13
- Livewire 4
- PHP
- SQLite
- PHPUnit
A technical exercise built over two days. Calling two REST endpoints is the easy part. The interesting part is handling external data you don't control, cleanly and honestly.
What it does
You enter a postcode. It is validated and geocoded through postcodes.io, then street-level crime for that point comes back from the police.uk API. That data is turned into a total, a breakdown by category, and a breakdown of what happened to the reports. The same lookup also runs from the command line as php artisan area:snapshot "SW1A 1AA".
The decisions worth looking at
- A service layer with thin callers. Two services own all the upstream work, and the Livewire component and the Artisan command both call them, so the web and CLI share one code path.
- Fail-soft by design. An unknown postcode, a rural area with no crime and an upstream outage all end in a sensible result rather than an error. “API unreachable” and “genuinely quiet area” are kept distinct, because they mean different things.
- Caching that doesn't cache failures. Results are cached per location for six hours, since police data only updates monthly. Failed responses are deliberately not cached, so a brief outage doesn't stick around.
- Two controls for two concerns. Rate limiting protects the public entry point; the cache protects the upstream API from repeat load.
- Honest presentation. The summary is a descriptive count, not a “risk score”, because the data can't support one.
- Tests that never touch the network. The PHPUnit suite fakes HTTP and covers aggregation, the empty-area case and the fail-soft paths.