Article

Building Location-Based Search with Spring Boot and Elasticsearch: From API Request to Geo-Distance Query
Learn how to build a geospatial search API using Spring Boot and Elasticsearch — covering API design, location validation, geo-distance queries, distance sorting, geo_point vs geo_shape, and clustering results for map visualization at scale.
Modern applications often need to answer questions such as:
Which users are within 500 meters of a location?
Which devices are near a specific coordinate?
Which events happened inside a particular geographic area?
How can we search millions of location records efficiently?
These are examples of geospatial search.
In this article, we look at how to build a location-based REST API using Spring Boot and Elasticsearch. The API accepts a latitude, longitude, and search radius, validates the request, and uses an Elasticsearch geo-distance query to find records within that radius.
The goal is not only to describe the API, but to understand what happens from the moment a request reaches the backend until location-based results are returned — including how to sort those results, when to reach for a different field type, and how to keep a map usable once results grow into the thousands.
1. The Problem
Imagine an application storing location records — a user ID, a timestamp, and a coordinate. A client wants to ask: find all records within 500 meters of this point. The backend needs to receive the request, validate the coordinates and radius, build a geospatial query, send it to Elasticsearch, and return the results.
2. What Is Geospatial Search?
Geospatial search means searching data based on geographic location rather than an exact match — asking "what's near this point?" instead of "what equals this value?" The point is represented as a latitude/longitude pair, and the search radius can be expressed in meters, kilometers, or miles.
3. Why Elasticsearch?
Elasticsearch supports a geo_point field type built for exactly this — representing a coordinate in a way that geographic queries can operate on directly, so the application never has to pull every record and calculate distances manually.
4. geo_point vs geo_shape
Elasticsearch actually offers two field types for geographic data:
geo_point represents a single coordinate — a dot on the map. Right for discrete locations: a user's position, a device's last reading, a store's address.
geo_shape represents an area or boundary — polygons, zones, regions. Right for "is this point inside this delivery zone?" rather than "how far apart are two points?"
Most "find things near me" features only need geo_point. geo_shape is for problems involving regions, not points.
5. Elasticsearch Mapping
Before writing the API, Elasticsearch needs to know the location field holds geographic coordinates — done by declaring it as geo_point in the index mapping, rather than treating latitude and longitude as unrelated numeric fields.
A common mistake: latitude and longitude are easy to mix up. Define and document the coordinate order consistently throughout the application.
6. Designing the API
A clean API exposes only what the client needs: a POST /api/search/nearby endpoint accepting latitude, longitude, and radius, returning a count and a list of matching records with their distance from the search point. The frontend doesn't need to know how Elasticsearch works — just request in, results out.
7. The Request Shape
The incoming request needs latitude, longitude, and radius as nullable values (rather than primitives) so missing fields can be detected and rejected cleanly, rather than silently defaulting to zero.
8. Validating Latitude and Longitude
Latitude must fall between -90 and 90; longitude between -180 and 180. Reject anything outside these ranges — and reject missing values — before the request reaches Elasticsearch.
9. Validating the Radius
The radius must be a positive number, and it's worth imposing a sensible maximum (based on the application's actual use case, not an arbitrary number) to prevent overly broad searches from returning excessive results.
10. The Elasticsearch Geo-Distance Query
Elasticsearch's geo_distance query returns documents whose location falls within a given distance of a specified point — the core mechanism the whole API is built around.
11. distance_type: arc vs plane
Elasticsearch can calculate that distance two ways:
arc accounts for the earth's curvature — more accurate, slightly more expensive. The default, and the right choice for long-range search.
plane treats the earth as flat — faster, with negligible accuracy loss at short range. A reasonable choice for local/city-scale search (a few hundred meters to a few kilometers).
The right choice depends on how far apart the compared points typically are.
12. Keeping Search Logic Out of the Controller
Separate responsibilities: the controller handles HTTP requests, the service handles validation and query logic, and the Elasticsearch client handles communication with the search engine. This keeps the controller small and the logic testable.
13. Sorting by Distance
Rather than retrieving all matches and sorting them afterward in application code, Elasticsearch can compute the distance between the search point and each document as part of the request itself, returning results nearest-first. This keeps the sorting work inside the search engine, which is both faster and avoids pulling unnecessary data into memory.
14. Returning Distance to the Client
When distance-based sorting is used, Elasticsearch attaches the computed distance to each result alongside the sort order — so the API doesn't need a separate calculation step. The backend extracts that value and includes it in the response next to the record's other fields.
15. Testing the API
Once implemented, the endpoint can be tested with Postman, curl, or a frontend application — sending a request with latitude, longitude, and radius, and confirming the response returns the expected nearby records sorted by distance.
16. Handling Invalid Requests
A production API shouldn't return a generic server error for bad input. An out-of-range latitude or a negative radius should produce a clear, specific client error explaining what was wrong — this makes the API easier for other developers to use correctly.
17. Performance Considerations
As the dataset grows from thousands to millions of records, a few things matter more:
Use the correct
geo_pointmapping — without it, geospatial queries don't work as expected.Return only the fields the client needs, not the full document.
Paginate or cap large radius searches rather than returning unbounded result sets.
Combine the geographic filter with other filters (time range, status, category) to narrow the search before it runs, rather than after.
18. Common Geospatial Search Mistakes
Reversing latitude and longitude — one of the easiest mistakes to make; define and document the order clearly.
Using the wrong field type — coordinates should be mapped as
geo_point, not unrelated numeric fields.No input validation — invalid coordinates reaching the search layer produce confusing downstream errors.
Returning too many records — a large radius can match huge numbers of documents; always paginate or cap results.
Mixing units — be explicit about meters vs. kilometers vs. miles in field names and API contracts.
19. Extending the API
Time-based search: combine geographic filtering with a start/end time range — useful when location data is continuously generated.
Frontend visualization: the API can power a map, sending the user's location, calling the search endpoint, and displaying nearby records with the ability to adjust the radius.
20. Clustering Results for Map Visualization
When a search returns thousands of points, plotting each one individually becomes cluttered and slow to render. Clustering solves this by grouping nearby points into a single marker (with a count) at low zoom levels, then breaking clusters apart into individual points as the user zooms in. Elasticsearch supports this pattern by bucketing documents into geographic grid cells and returning counts per cell — the frontend renders one marker per cluster instead of one per record, which is the standard approach for map-based visualizations at scale.
21. A Production-Oriented Architecture
Frontend → REST API (Spring Boot) → Validation and Business Logic → Elasticsearch (geo_point) → Geographic Results. Each layer has a clear responsibility: the frontend doesn't need to know how Elasticsearch works, and Elasticsearch doesn't need to know how results are displayed.
Conclusion
A geospatial API doesn't have to be complicated. The fundamental flow is: coordinates and radius in, validation, an Elasticsearch geo-distance query, sorted results out. Once this pattern is understood — along with when to reach for geo_shape instead of geo_point, how distance_type trades accuracy for speed, and how clustering keeps large result sets usable on a map — the same approach extends naturally to location tracking, device monitoring, nearby search, and geospatial analytics.