MapLargeInc.RestClient
4.135.20260718.21250
dotnet add package MapLargeInc.RestClient --version 4.135.20260718.21250
NuGet\Install-Package MapLargeInc.RestClient -Version 4.135.20260718.21250
<PackageReference Include="MapLargeInc.RestClient" Version="4.135.20260718.21250" />
<PackageVersion Include="MapLargeInc.RestClient" Version="4.135.20260718.21250" />
<PackageReference Include="MapLargeInc.RestClient" />
paket add MapLargeInc.RestClient --version 4.135.20260718.21250
#r "nuget: MapLargeInc.RestClient, 4.135.20260718.21250"
#:package MapLargeInc.RestClient@4.135.20260718.21250
#addin nuget:?package=MapLargeInc.RestClient&version=4.135.20260718.21250
#tool nuget:?package=MapLargeInc.RestClient&version=4.135.20260718.21250
MapLarge.RestClient
A strongly-typed .NET client for the MapLarge Server REST API. It provides:
- A single typed entry point (
RestClient) with a property per API area — accounts, tables, queries, imports, layers, reports, workflows, cluster diagnostics, and many more. - Automatic authentication and token refresh.
- Typed request and response models, with helpers for HAL list responses, paging, and long-running (poll-to-complete) requests.
- First-class support for client-certificate (mTLS) servers.
The package multi-targets .NET Standard 2.0 and .NET 8.0. The netstandard2.0 target supports .NET Framework 4.6.1+, .NET Core 2.0+, and .NET 5+; consumers on .NET 8 or later automatically pick up the net8.0 target for access to newer BCL APIs.
Installation
Add the package with the .NET CLI:
$ dotnet add package MapLargeInc.RestClient
Or reference a specific version in your .csproj:
<PackageReference Include="MapLargeInc.RestClient" Version="[package version number]" />
Note: The package id is
MapLargeInc.RestClient; it provides theMapLarge.RestClientassembly and theMapLarge.Rest.*namespaces (so you still writeusing MapLarge.Rest.Client;). If your project isn't configured for the feed it's published to, add it as a package source (via anuget.config, or withdotnet add package MapLargeInc.RestClient --source [feed url]).
Quick start
Construct a client with your server URL and credentials, then call any endpoint through its typed group
property. Every endpoint method is async.
using MapLarge.Rest.Client;
var uri = new Uri("https://your-maplarge-server/");
var creds = new PasswordCredentials("user@maplarge.com", "password");
using var client = new RestClient(uri, creds);
var response = await client.Accounts.ListAsync();
foreach (var account in RestClient.Items(response)) {
Console.WriteLine(account.Code);
}
Tip:
RestClientis thread-safe and designed to be long-lived. Create one per server and reuse it for the lifetime of your process rather than constructing one per call.
Authentication
Pass either password or token credentials to the constructor. Both send the mluser header on every
request, along with mlpass or mltoken respectively.
Password credentials
var creds = new PasswordCredentials("user@maplarge.com", "password");
using var client = new RestClient(uri, creds);
On the first call, the client exchanges your username/password for a bearer token via POST /Auth/Login,
then uses that token for subsequent requests. When the token nears expiry the client re-authenticates
automatically — concurrent callers are coordinated so only one login round-trip is issued.
Token credentials
If you already hold a valid token, use it directly and skip the login round-trip:
var creds = new TokenCredentials("user@maplarge.com", token);
using var client = new RestClient(uri, creds);
You can also supply an explicit lifetime so the client knows when to refresh:
var creds = new TokenCredentials(
"user@maplarge.com", token, lifespan: TimeSpan.FromHours(1), now: DateTimeOffset.UtcNow);
Note: The early-refresh threshold is the constructor's
minRemainingLifeparameter (default0.5) — the fraction of a token's lifetime that must remain before the client proactively re-authenticates. It only applies to credentials that carry a lifetime.
Calling endpoints
Each API area is a typed property on the client, and each operation is an async method returning a
typed model. Path segments are method parameters; request bodies are typed model objects.
// GET /accounts/{account}
Account account = await client.Accounts.GetAsync("myaccount");
// GET /tables/{account}/{name}
TableVersion table = await client.Tables.GetAsync("myaccount", "mytable");
// POST a typed request body
Account created = await client.Accounts.PostAsync(new CreateAccountRequest { /* ... */ });
The available groups mirror the server's REST surface — Accounts, Tables, Queries, Imports,
Layers, Reports, Users, Groups, Workflows, Cluster, Diagnostics, Extensions, and many
more. Every group is a property on RestClient, so IntelliSense lists them all, along with each group's
operations and their parameters.
Tip: Models live in the
MapLarge.Rest.Modelsnamespace. Addusing MapLarge.Rest.Models;to reference request/response types such asQuery,TableVersion, orPageOptionsdirectly.
Working with list responses
List endpoints return a JSON HAL wrapper (RestList<T>) whose records live under an _embedded block,
alongside paging links. Use the static RestClient.Items helper to unwrap them; it returns an empty list
rather than null when the response has no embedded items.
var response = await client.Accounts.ListAsync();
foreach (var account in RestClient.Items(response)) {
Console.WriteLine(account.Code);
}
Paging
When you request a page size, the server returns a Page<T> (a RestList<T> with next/prev links).
Follow _links.next with FollowAsync to walk every page, unwrapping each the same way:
using MapLarge.Rest.Models;
var page = (Page<TableVersion>)await client.Tables.ListAsync("myaccount", new PageOptions { Size = 100 });
while (page != null) {
foreach (var table in RestClient.Items(page)) {
Console.WriteLine(table.Name);
}
page = page.Links.Next != null
? await client.FollowAsync<Page<TableVersion>>(page.Links.Next)
: null;
}
Note:
FollowAsync<T>works with any HAL link the server returns — not just paging. Use it to followImport.Links.Resultafter an import completes,LongRunningTask.Links.Selfwhile polling, or any other_linksentry. It accepts either aLinkor an href string (absolute or relative to the client's base URL).
Long-running requests (fallback-to-poll)
Endpoints that may exceed the server's synchronous request budget can be configured to transparently fall
back to polling. Set FallbackToPollSettings on a RequestOptions; when the server responds
202 Accepted, the client polls the resulting LongRunningTask to completion and returns the final
result as if the call had been synchronous. Supply a ProgressCallback to observe each poll iteration.
using MapLarge.Rest.Models;
var options = new RequestOptions {
FallbackToPollSettings = new FallbackToPollSettings {
FallbackToPoll = true,
PollIntervalSeconds = 1.0,
ProgressCallback = task => Console.WriteLine($"task {task.Id} progress: {task.Progress}"),
},
};
var result = await client.Queries.ResultDirectAsync(query, options);
Polling an import to completion
Import endpoints (Tables.PostAsync, Tables.UploadAsync) start the work and return an Import
immediately — the server responds 202 Accepted while it keeps processing. Poll the import's
Links.Self until Successful has a value, then follow Links.Result for the created table:
using MapLarge.Rest.Models;
var import = await client.Tables.PostAsync("myaccount", "mytable", new CreateTableRequest {
Data = "ID,Lat,Lng,Name\r\n1,0.0,0.0,One\r\n2,10.0,10.0,Two",
Visibility = TableVisibility.Public,
});
var deadline = DateTimeOffset.UtcNow + TimeSpan.FromMinutes(5);
while (!import.Successful.HasValue) {
if (DateTimeOffset.UtcNow >= deadline)
throw new TimeoutException($"Import {import.Id} did not complete within the timeout.");
await Task.Delay(TimeSpan.FromSeconds(1));
import = await client.FollowAsync<Import>(import.Links.Self);
}
var rows = await client.FollowAsync<RestList<TableVersion>>(import.Links.Result!);
Request options
Pass a RequestOptions to any endpoint method to control cluster routing and read-after-write
consistency:
| Property | Purpose |
|---|---|
MlServerFwd |
Pin the request to a specific node within the cluster. |
ProxyToClusterKey / ProxyToDomain |
Execute the request against a different MapLarge cluster (by key or by URL). |
WaitForTransactionId |
Require the processing node to have applied a given transaction before serving the request — chain a read after a write without observing stale state (use the waitForTransactionId response header from the prior mutation). |
FallbackToPollSettings |
Opt in to poll-to-complete handling for long-running requests (see above). |
Note:
RequestOptionsdoes not carry a per-request timeout — .NET'sHttpClienthonors a single client-wide timeout only. For per-request deadlines, pass aCancellationToken:using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30)); var account = await client.Accounts.GetAsync("myaccount", cancellationToken: cts.Token);
Authenticating with a client certificate (mTLS)
When a MapLarge server is configured with RequireClientCertificate=true, callers must present a TLS
client certificate before the application sees the request. Attach the certificate to an HttpClient and
pass it to the RestClient constructor. The mluser / mltoken / mlpass credential headers coexist
with certificate-based TLS authentication — both layers can be required by the server.
using System.Net.Http;
using System.Security.Cryptography.X509Certificates;
using MapLarge.Rest.Client;
var clientCert = new X509Certificate2("client.pfx", "passphrase");
var handler = new HttpClientHandler();
handler.ClientCertificates.Add(clientCert);
// Optional: trust a private CA for the server cert. Skip if the server cert chains
// to a public root already trusted by the OS.
// handler.ServerCertificateCustomValidationCallback = (msg, cert, chain, errors) => /* validate */;
using var http = new HttpClient(handler);
using var client = new RestClient(
new Uri("https://maplarge.example/"),
new PasswordCredentials("user@maplarge.com", "password"),
http);
var accounts = await client.Accounts.ListAsync();
Note: On .NET Framework the same pattern works unchanged. To load the certificate from the Windows certificate store, use
X509StorewithX509FindType.FindByThumbprint.
Caveats
- Reuse a single
HttpClientfor the process lifetime rather than constructing one per request — TLS handshakes are expensive. Dispose theX509Certificate2when you're done with it. ServerCertificateCustomValidationCallbackoverrides the entire chain validation. Use it only to add a trusted private CA; don't blanket-returntruein production.
Common examples
Import a table from a file
Upload one or more files with BinaryContent and Tables.UploadAsync. Like other imports, this returns
an Import you can poll to completion (see Polling an import to completion).
using System.Collections.Generic;
using MapLarge.Rest.Client;
using MapLarge.Rest.Models;
using var stream = File.OpenRead("wildfires.csv");
var file = new BinaryContent(stream, "wildfires.csv", "text/csv");
var import = await client.Tables.UploadAsync(
account: "myaccount",
name: "wildfires",
body: new CreateTableFromFileUploadRequest {
Files = new List<BinaryContent> { file },
PrimaryKeyColumn = "FIREID",
});
Export a table
Tables.Versions.ExportAsync returns a BinaryContent whose Stream holds the exported bytes — copy it
to a file (or any destination stream):
using MapLarge.Rest.Models;
using var export = await client.Tables.Versions.ExportAsync(
account: "myaccount",
name: "wildfires",
version: "latest",
accept: TablesVersionsExportAcceptType.application_geo_json);
using var file = File.Create("wildfires.geojson");
await export.Stream!.CopyToAsync(file);
Run a query (hash & cache)
Query and Layer entities use a hash & cache workflow. Posting a query returns its server-computed
Hash; the server caches results by that hash, so an identical query isn't recomputed. Use
ExistsAsync to check whether a result is available for a hash, then ResultAsync to fetch it.
using MapLarge.Rest.Models;
var query = new Query {
Table = new QueryTable { Name = "myaccount/wildfires" },
Where = new QueryWhereTest {
Column = "CAUSE",
Test = "Equal",
Value = QueryWhereLiteral.Of("Natural"),
},
Take = -1, // -1 = no row limit
};
var posted = await client.Queries.PostAsync(query);
if (await client.Queries.ExistsAsync(posted.Hash)) {
var result = await client.Queries.ResultAsync(posted.Hash);
// DBResult.Data is column-oriented: a map of column name -> list of values.
var ids = result.Data["FIREID"];
var names = result.Data["FIRENAME"];
for (var i = 0; i < ids.Count; i++) {
Console.WriteLine($"{ids[i]}, {names[i]}");
}
}
Render a layer tile
Layers follow the same hash & cache workflow. Post a Layer (a Query plus a Style) to get its
Hash, then render map tiles by hash. TileAsync renders a single XYZ tile and returns the image bytes
as a BinaryContent; it takes a list of hashes, so you can composite several layers into one tile.
using System.Collections.Generic;
using MapLarge.Rest.Models;
var layer = new Layer {
Query = new Query { Table = new QueryTable { Name = "myaccount/wildfires" } },
Style = new Style { DrawShapes = true },
};
// The server caches layers by content and returns a hash. Before rendering with a hash you
// stored earlier, confirm it's still cached with ExistsAsync and re-post if it isn't.
var hash = (await client.Layers.PostAsync(layer)).Hash;
if (!await client.Layers.ExistsAsync(hash)) {
hash = (await client.Layers.PostAsync(layer)).Hash;
}
using var tile = await client.Layers.TileAsync(
zoom: 4, x: 2, y: 6,
format: ImageFormatType.png,
hashes: new List<string> { hash });
using var file = File.Create("tile.png");
await tile.Stream!.CopyToAsync(file);
Note:
Layersalso offersImageAsync(a single static map image at fixed dimensions rather than an XYZ tile), plusClickAsyncandHoverGridAsyncfor interactive lookups at a pixel.
Tip: Full request/response schemas for every endpoint are in the server's Swagger UI (available from the MapLarge server's API documentation).
Error handling
A non-2xx response throws a RestException (namespace MapLarge.Rest.Exceptions) carrying the request
Uri, HTTP Status, raw ResponseText, and parsed Details (a ProblemDetails).
using MapLarge.Rest.Exceptions;
try {
var table = await client.Tables.GetAsync("myaccount", "mytable");
}
catch (RestValidationException ex) {
// HTTP 400 with per-field validation errors.
foreach (var entry in ex.ValidationDetails.Errors) {
Console.WriteLine($"{entry.Key}: {string.Join(", ", entry.Value)}");
}
}
catch (RestException ex) {
Console.WriteLine($"{ex.Status} calling {ex.Uri}: {ex.ResponseText}");
}
RestValidationException(derives fromRestException) is thrown for400responses with aValidationProblemDetailsbody; catch it first when you want per-field messages (ValidationDetails.Errors, a map of field name to messages).AuthenticationExceptionis thrown when login fails; it exposes theStatusCodeand the server'sLoginResponse.
Disposing the client
RestClient implements IDisposable. Dispose it (or wrap it in using) when you're done, to release the
underlying HttpClient it owns. If you pass in your own HttpClient (e.g. for mTLS), the client does
not dispose it — you retain ownership of its lifetime.
Notes
- All endpoint methods are async (
Task/Task<T>). - JSON is serialized via Newtonsoft.Json.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- Newtonsoft.Json (>= 13.0.3)
-
net8.0
- Newtonsoft.Json (>= 13.0.3)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 4.135.20260718.21250 | 121 | 7/18/2026 |
| 4.135.20260717.233243 | 112 | 7/17/2026 |