![]() |
VOOZH | about |
dotnet add package Rystem.RepositoryFramework.Api.Client --version 10.0.8
NuGet\Install-Package Rystem.RepositoryFramework.Api.Client -Version 10.0.8
<PackageReference Include="Rystem.RepositoryFramework.Api.Client" Version="10.0.8" />
<PackageVersion Include="Rystem.RepositoryFramework.Api.Client" Version="10.0.8" />Directory.Packages.props
<PackageReference Include="Rystem.RepositoryFramework.Api.Client" />Project file
paket add Rystem.RepositoryFramework.Api.Client --version 10.0.8
#r "nuget: Rystem.RepositoryFramework.Api.Client, 10.0.8"
#:package Rystem.RepositoryFramework.Api.Client@10.0.8
#addin nuget:?package=Rystem.RepositoryFramework.Api.Client&version=10.0.8Install as a Cake Addin
#tool nuget:?package=Rystem.RepositoryFramework.Api.Client&version=10.0.8Install as a Cake Tool
.NET client adapter for APIs generated by Rystem.RepositoryFramework.Api.Server.
It lets you inject IRepository<T, TKey>, IQuery<T, TKey>, or ICommand<T, TKey> locally while the actual work happens over HTTP against a remote repository API.
dotnet add package Rystem.RepositoryFramework.Api.Client
The current package metadata in src/Repository/RepositoryFramework.Api.Client/RepositoryFramework.Api.Client.csproj is:
Rystem.RepositoryFramework.Api.Client10.0.6net10.0Microsoft.Extensions.Http, Microsoft.Extensions.Http.Polly, Polly| Area | Purpose |
|---|---|
WithApiClient(...) |
Register repository/query/command consumers backed by HTTP |
RepositoryClient<T, TKey> |
Concrete client implementation that translates repository calls to HTTP requests |
IApiRepositoryBuilder<T, TKey> |
Configure base address, path, version, server name, and server factory name |
HttpClientRepositoryBuilder<T, TKey> |
Access IHttpClientBuilder and the built-in policy helper |
| Request interceptors | Enrich outgoing HttpClient instances |
| Response interceptors | Inspect responses and optionally retry requests |
| Bearer authorization helpers | Provide token-based request enrichment and optional 401 retry flow |
This package is not a general REST client generator. It is a client for the wire format produced by RepositoryFramework.Api.Server.
That means it expects the server to expose routes and payload shapes like:
Get, Exist, Delete endpoints following repository conventionsQuery/Stream and Batch/Stream endpointsSerializableFilter, BatchOperations<T, TKey>, State<T, TKey>, and Entity<T, TKey> payloadsIf the server follows the matching Rystem API-server conventions, the client can make the remote repository feel local.
builder.Services.AddRepository<Product, int>(repositoryBuilder =>
{
repositoryBuilder.WithApiClient(apiBuilder =>
{
apiBuilder
.WithHttpClient("https://api.example.com")
.ApiBuilder
.WithStartingPath("api")
.WithVersion("v1");
});
});
public sealed class ProductService
{
private readonly IRepository<Product, int> _repository;
public ProductService(IRepository<Product, int> repository)
=> _repository = repository;
public Task<Product?> GetAsync(int id)
=> _repository.GetAsync(id);
}
WithApiClient(...) is available on repository, query, and command builders.
builder.Services.AddRepository<Product, int>(repositoryBuilder =>
{
repositoryBuilder.WithApiClient(apiBuilder =>
{
apiBuilder.WithHttpClient("https://api.example.com");
});
});
builder.Services.AddQuery<Product, int>(queryBuilder =>
{
queryBuilder.WithApiClient(apiBuilder =>
{
apiBuilder.WithHttpClient("https://api.example.com");
});
});
builder.Services.AddCommand<Product, int>(commandBuilder =>
{
commandBuilder.WithApiClient(apiBuilder =>
{
apiBuilder.WithHttpClient("https://api.example.com");
});
});
WithApiClient(...) itself defaults to ServiceLifetime.Scoped.
IApiRepositoryBuilder<T, TKey>| Method | Purpose |
|---|---|
WithHttpClient(string domain) |
Register a named HttpClient; adds https:// automatically when missing |
WithHttpClient(Action<HttpClient>) |
Configure the HttpClient directly |
WithStartingPath(string) |
Override the base path, default api |
WithVersion(string) |
Add a version segment |
WithName(string) |
Override the remote model segment |
WithServerFactoryName(string) |
Target a named repository registration on the server |
WithHttpClient(string domain)apiBuilder.WithHttpClient("localhost:7058");
If the string does not start with http:// or https://, the builder prepends https://.
WithHttpClient(Action<HttpClient>)Use this overload when you want full control.
apiBuilder.WithHttpClient(client =>
{
client.BaseAddress = new Uri("https://api.example.com");
client.Timeout = TimeSpan.FromSeconds(30);
});
ApiClientSettings<T, TKey> builds the final paths from:
StartingPath, default apiVersionName, default typeof(T).NameFactoryNameThe effective base route is:
{startingPath}/{version?}/{name}/{serverFactoryName?}
Examples:
api/Product/Get?key=42api/v2/Product/Get?key=42SuperApi/v2/calamityuser/Get?key=abc@example.comapi/Product/premium/Get?key=42WithName(...) changes the remote model segmentWithServerFactoryName(...) adds the remote server factory segmentname parameter on WithApiClient(..., name: "local") is only the local DI factory name for your client registrationSo a local named client and a remote named server repository are separate configuration knobs.
The client mirrors the server conventions from RepositoryFramework.Api.Server.
For keys that are not treated as JSONable by KeySettings<TKey>:
GetAsync, ExistAsync, DeleteAsync use GET ...?key=...InsertAsync, UpdateAsync use POST ...?key=... with body TFor keys treated as JSONable:
GetAsync, ExistAsync, DeleteAsync send POST with body TKeyInsertAsync, UpdateAsync send POST with body Entity<T, TKey>QueryAsync(...) posts a serialized filter to .../Query/StreamBatchAsync(...) posts BatchOperations<T, TKey> to .../Batch/StreamOperationAsync(...) posts a serialized filter to .../Operation?op=...&returnType=...The client intentionally uses the streaming endpoints for query and batch operations, even when the caller later materializes the results into a list.
RepositoryClient<T, TKey> actually doesThe concrete client implementation maps local repository calls to HTTP like this:
GetAsync → GET or POST depending on key serializationExistAsync → GET or POST depending on key serializationInsertAsync → POSTUpdateAsync → POSTDeleteAsync → GET or POST depending on key serializationQueryAsync → POST to Query/Stream and DeserializeAsyncEnumerable<Entity<T, TKey>>BatchAsync → POST to Batch/Stream and DeserializeAsyncEnumerable<BatchResult<T, TKey>>OperationAsync → POST to OperationThe client expects 200 OK for success. Any other status code becomes an HttpRequestException containing the raw response body.
That is one reason this package works best against the matching Rystem API server rather than arbitrary REST endpoints.
WithHttpClient(...) returns HttpClientRepositoryBuilder<T, TKey>.
repositoryBuilder.WithApiClient(apiBuilder =>
{
apiBuilder
.WithHttpClient("https://api.example.com")
.WithDefaultRetryPolicy();
});
WithDefaultRetryPolicy()Despite the method name, the built-in policy is not a retry policy.
It registers an advanced circuit breaker with these settings:
50%10 seconds1015 secondsIf you want retries, attach your own policy through ClientBuilder.
var retryPolicy = HttpPolicyExtensions
.HandleTransientHttpError()
.RetryAsync(3);
repositoryBuilder.WithApiClient(apiBuilder =>
{
apiBuilder
.WithHttpClient("https://api.example.com")
.ClientBuilder
.AddPolicyHandler(retryPolicy);
});
Request interceptors let you mutate the HttpClient before the repository client starts using it.
Registration options:
services.AddApiClientInterceptor<MyGlobalInterceptor>();
services.AddApiClientSpecificInterceptor<Product, MyProductInterceptor>();
services.AddApiClientSpecificInterceptor<Product, int, MyProductKeyInterceptor>();
Interfaces:
public interface IRepositoryClientInterceptor
{
Task<HttpClient> EnrichAsync(HttpClient client, RepositoryMethods path);
}
public interface IRepositoryClientInterceptor<T> : IRepositoryClientInterceptor { }
public interface IRepositoryClientInterceptor<T, TKey> : IRepositoryClientInterceptor { }
In the current implementation, request interceptors do not run on every repository call.
They run the first time a given RepositoryClient<T, TKey> instance enriches its HttpClient, then that client is reused for the rest of the repository-client lifetime.
So request interceptors are a good fit for:
They are not a good fit for truly per-request values such as a fresh correlation ID on every repository method call.
The execution order is:
Response interceptors run after the HTTP call returns and before the client enforces the final 200 OK success check.
Registration options:
services.AddApiClientResponseInterceptor<MyGlobalResponseInterceptor>();
services.AddApiClientResponseInterceptor<Product, MyProductResponseInterceptor>();
services.AddApiClientResponseInterceptor<Product, int, MyProductKeyResponseInterceptor>();
Interfaces:
public interface IRepositoryResponseClientInterceptor
{
Task<HttpResponseMessage> CheckResponseAsync(
HttpClient client,
HttpResponseMessage response,
Func<HttpClient, Task<HttpResponseMessage>> request);
}
public interface IRepositoryClientResponseInterceptor<T> : IRepositoryResponseClientInterceptor { }
public interface IRepositoryResponseClientInterceptor<T, TKey> : IRepositoryResponseClientInterceptor
where TKey : notnull { }
Typical use cases:
The same ordering applies here: global, then model-specific, then model-plus-key-specific.
The package includes a default bearer authenticator based on ITokenManager.
ITokenManagerpublic sealed class MyTokenManager : ITokenManager
{
public Task<string?> GetTokenAsync()
{
throw new NotImplementedException();
}
public Task<string?> RefreshTokenAsync()
{
throw new NotImplementedException();
}
public async Task EnrichWithAuthorizationAsync(HttpClient client)
{
var token = await GetTokenAsync();
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", token);
}
}
services.AddDefaultAuthorizationInterceptorForApiHttpClient<MyTokenManager>();
services.AddDefaultAuthorizationInterceptorForApiHttpClient<Product, MyTokenManager>();
services.AddDefaultAuthorizationInterceptorForApiHttpClient<Product, int, MyTokenManager>();
services.AddDefaultAuthorizationInterceptorForApiHttpClient<MyTokenManager>(settings =>
{
settings.Scopes = ["api.read", "api.write"];
settings.ExceptionHandler = async (exception, serviceProvider) =>
{
await Task.CompletedTask;
};
});
So automatic 401 Unauthorized refresh-and-retry is available in the global and model-specific registrations, but not fully wired in the model-plus-key-specific registration path.
Also note that AuthenticatorSettings.Scopes exists in the core client package but is not consumed by the core bearer interceptor itself. It becomes meaningful in the companion authentication packages that provide concrete token managers.
RepositoryClient<T, TKey> exposes BootstrapAsync() because the repository abstraction includes it.
However, the current client implementation simply performs:
GET {basePath}/Bootstrap
without sending a key.
The stock API-server bootstrap endpoint is currently wired through the key-binding path, so bootstrap interoperability is less polished than the rest of the client surface. In practice, prefer server-side WarmUpAsync() for startup bootstrap flows.
The API tests register clients like this:
settings.WithApiClient(apiBuilder =>
{
apiBuilder
.WithVersion("v2")
.WithStartingPath("SuperApi");
});
The tests also use the client-side name override to match a server-side WithName<T>(...) route customization:
settings.WithApiClient(apiBuilder =>
{
apiBuilder
.WithName("calamityuser")
.WithVersion("v2")
.WithStartingPath("SuperApi");
});
The web client sample configures a retry policy through ClientBuilder:
var retryPolicy = HttpPolicyExtensions
.HandleTransientHttpError()
.Or<TimeoutRejectedException>()
.RetryAsync(3);
settings.WithApiClient(apiBuilder =>
{
apiBuilder
.WithHttpClient("localhost:7058")
.ClientBuilder
.AddPolicyHandler(retryPolicy);
});
The web client sample also registers the built-in authorization interceptor globally:
builder.Services.AddDefaultAuthorizationInterceptorForApiHttpClient(settings =>
{
settings.Scopes = builder.Configuration["AzureAd:Scopes"]!.Split(' ');
});
| Package | Purpose |
|---|---|
Rystem.RepositoryFramework.Abstractions |
Core repository contracts and DI registration |
Rystem.RepositoryFramework.Api.Server |
The server package this client is designed to call |
Rystem.RepositoryFramework.Api.Client.Authentication.BlazorServer |
Token-manager helpers for Blazor Server |
Rystem.RepositoryFramework.Api.Client.Authentication.BlazorWasm |
Token-manager helpers for Blazor WebAssembly |
If you are continuing through the repository area, this is the package to read right after src/Repository/RepositoryFramework.Api.Server/README.md.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 net10.0 is compatible. net10.0-android net10.0-android was computed. net10.0-browser net10.0-browser was computed. net10.0-ios net10.0-ios was computed. net10.0-maccatalyst net10.0-maccatalyst was computed. net10.0-macos net10.0-macos was computed. net10.0-tvos net10.0-tvos was computed. net10.0-windows net10.0-windows was computed. |
Showing the top 2 NuGet packages that depend on Rystem.RepositoryFramework.Api.Client:
| Package | Downloads |
|---|---|
|
Rystem.RepositoryFramework.Api.Client.Authentication.BlazorServer
Rystem.RepositoryFramework allows you to use correctly concepts like repository pattern, CQRS and DDD. You have interfaces for your domains, auto-generated api, auto-generated HttpClient to simplify connection "api to front-end", a functionality for auto-population in memory of your models, a functionality to simulate exceptions and waiting time from external sources to improve your implementation/business test and load test. |
|
|
Rystem.RepositoryFramework.Api.Client.Authentication.BlazorWasm
Rystem.RepositoryFramework allows you to use correctly concepts like repository pattern, CQRS and DDD. You have interfaces for your domains, auto-generated api, auto-generated HttpClient to simplify connection "api to front-end", a functionality for auto-population in memory of your models, a functionality to simulate exceptions and waiting time from external sources to improve your implementation/business test and load test. |
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 10.0.8 | 5,596 | 5/13/2026 |
| 10.0.7 | 207 | 3/26/2026 |
| 10.0.6 | 433,709 | 3/3/2026 |
| 10.0.5 | 218 | 2/22/2026 |
| 10.0.4 | 231 | 2/9/2026 |
| 10.0.3 | 147,997 | 1/28/2026 |
| 10.0.1 | 209,181 | 11/12/2025 |
| 9.1.3 | 410 | 9/2/2025 |
| 9.1.2 | 764,662 | 5/29/2025 |
| 9.1.1 | 97,928 | 5/2/2025 |
| 9.0.32 | 186,722 | 4/15/2025 |
| 9.0.31 | 5,971 | 4/2/2025 |
| 9.0.30 | 88,927 | 3/26/2025 |
| 9.0.29 | 9,132 | 3/18/2025 |
| 9.0.28 | 321 | 3/17/2025 |
| 9.0.27 | 316 | 3/16/2025 |
| 9.0.26 | 353 | 3/13/2025 |
| 9.0.25 | 52,197 | 3/9/2025 |
| 9.0.21 | 463 | 3/6/2025 |
| 9.0.20 | 19,695 | 3/6/2025 |