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.
Top comments (0)