Google's Places API (New) and Routes API are good APIs. Calling them from .NET by hand is plumbing: JSON bodies, a mandatory field mask header on every request, SCREAMING_SNAKE_CASE enums, durations like "1234s", and status codes to decode.
AarxMaps does that plumbing for you. Version 1.0 is out on NuGet, MIT licensed. Here's one real flow: find a place, then drive there.
Install
dotnet add package AarxMaps.Places dotnet add package AarxMaps.Routes You need .NET 10 and a Google Cloud API key with both APIs enabled (and billing on).
Register
dotnet user-secrets init dotnet user-secrets set "AarxMaps:ApiKey" "YOUR_API_KEY" builder.Services.AddAarxMaps(o => builder.Configuration.GetSection("AarxMaps").Bind(o)); builder.Services.AddAarxMapsPlaces(); builder.Services.AddAarxMapsRoutes(); Config is validated at startup. Forget AddAarxMaps and the error names the missing call. Then inject IPlacesService and IRoutesService.
Find a place
var searchRequest = new TextRequestBuilder() .WithTextQuery("Space Needle, Seattle") .WithPageSize(1) .Build(); var search = await places.Search.SearchByTextAsync(searchRequest, ct); if (search.Places.Count == 0) return; var place = search.Places[0]; Builders validate as you go: a blank query or an out-of-range page size throws MapsInvalidRequestException before anything is sent.
Get there
Waypoints take an address, a coordinate or a place ID, so the search result plugs straight in:
var origin = new WaypointBuilder() .FromLocation(new LatLng(47.6097, -122.3422)) .Build(); var destination = new WaypointBuilder() .FromPlaceId(place.PlaceId) .Build(); var request = new RouteRequestBuilder() .From(origin) .To(destination) .WithTravelMode(TravelMode.Drive) .Build(); var result = await routes.Directions.GetRoutesAsync(request, ct: ct); var route = result.Routes .FirstOrDefault(r => r.Labels.Contains(RouteLabel.DefaultRoute)); Console.WriteLine($"{place.DisplayName}: {route?.DistanceMeters} m, {route?.Duration}"); Duration is a TimeSpan. The builder knows Google's per-mode rules too, so an arrival time on a driving route fails at Build(), not as a 400.
When things go wrong
Every exception derives from MapsApiException:
| Exception | When |
|---|---|
MapsInvalidRequestException | Builder rejected a value, or Google returned 400 |
MapsApiAuthException | 401/403: bad key or API not enabled |
MapsNotFoundException | 404, e.g. an unknown place ID |
MapsRateLimitException | 429, with RetryAfter |
Each one carries the status code and Google's error body. Cancellation stays a plain OperationCanceledException.
Caching in one line
builder.Services.AddAarxMapsMemoryCache(o => o.AbsoluteExpiration = TimeSpan.FromMinutes(10)); It covers every module. Need Redis? Use AddAarxMapsDistributedCache. Cache failures are logged and fall through to Google, never thrown. Mind Google's terms on how long you may cache.
Design choices
- Validate up front: mistakes surface at
Build(), not as cryptic 400s. - Field masks handled: each method sends what it maps. Need more?
GetDetailsRawAsyncandGetRoutesRawAsynctake your own field list. - Cancellation everywhere: every async method passes your token to the HTTP call.
- Escape hatch:
IMapsApiClientcalls endpoints the modules don't cover, with the same auth, errors and caching.
What's not there yet
No built-in retries, minimal search results (ID, name, types), and some request options aren't exposed yet. Every gap and its workaround is on Known limitations.
Try it
- Docs: https://aarexus.github.io/AarxMaps/
- GitHub: https://github.com/Aarexus/AarxMaps
- NuGet: https://www.nuget.org/packages/AarxMaps.Places
Feedback and issues welcome, especially on the API shape.
Drafted with AI assistance, reviewed by the author.
