Nera.Client
1.0.0
dotnet add package Nera.Client --version 1.0.0
NuGet\Install-Package Nera.Client -Version 1.0.0
<PackageReference Include="Nera.Client" Version="1.0.0" />
<PackageVersion Include="Nera.Client" Version="1.0.0" />
<PackageReference Include="Nera.Client" />
paket add Nera.Client --version 1.0.0
#r "nuget: Nera.Client, 1.0.0"
#:package Nera.Client@1.0.0
#addin nuget:?package=Nera.Client&version=1.0.0
#tool nuget:?package=Nera.Client&version=1.0.0
Nera.Client
Unified Client SDK for the entire Nera platform services.
Provides typed HTTP clients, gRPC clients for IAM and Quota services, Options-based configuration, and resilient communication.
Table of Contents
- Overview
- Installation
- Quick Start
- Configuration
- Service Clients
- Architecture
- Extending (Adding a New Service)
- API Reference
Overview
Nera.Client is the common client library that acts as a bridge between services in the Nera platform. Instead of each project writing its own HTTP/gRPC client to call other services, everyone uses Nera.Client to get:
- ✅ Typed HTTP clients for REST services with full request/response models
- ✅ gRPC IAM clients for User, Token, Session, Organization, and AuditLog services
- ✅ Quota Service clients (HTTP + gRPC) with fail-open, caching decorator, and well-known resource constants
- ✅ Endpoint, API version, and timeout configuration via the Options Pattern
- ✅ Automatic resilience (retry, circuit breaker, timeout) via
Microsoft.Extensions.Http.Resilience - ✅ IHttpClientFactory — managed connection pool, no socket exhaustion
- ✅ Structured logging for every request
- ✅ Consistent error handling with
NeraResult<T>wrapper - ✅ IAM enums, constants, DTOs, and entity models ready to use
Project Structure
Nera.Client/
├── Abstractions/
│ └── BaseNeraClient.cs # Base HTTP client with GET/POST/PUT/DELETE
├── Common/
│ ├── ApiResponse.cs # NeraResult<T> response envelope
│ ├── NeraClientException.cs # Custom exception for client errors
│ └── NeraJsonDefaults.cs # Shared JSON serializer options
├── Configuration/
│ ├── NeraClientOptions.cs # Root options (timeout, API version, services)
│ ├── ServiceEndpointOptions.cs # Per-service HTTP endpoint config
│ └── MenuServiceOptions.cs # Menu-specific options
├── DependencyInjection/
│ ├── NeraClientBuilder.cs # Fluent builder: AddMenuService(), AddAllIamServices()
│ └── ServiceCollectionExtensions.cs # AddNeraClient() extension
├── Services/
│ ├── Menu/ # HTTP REST client
│ │ ├── IMenuServiceClient.cs
│ │ ├── MenuServiceClient.cs
│ │ └── Models/
│ └── IAM/ # gRPC clients (merged from nera-iam-common)
│ ├── Protos/ # 17 proto files (token, user, session, org, audit, etc.)
│ ├── Interfaces/ # ITokenGrpcClient, IUserGrpcClient, etc.
│ ├── Clients/ # TokenGrpcClient, UserGrpcClient, etc.
│ ├── Configuration/ # IamServiceOptions
│ ├── Models/ # BaseModels, EntityModels (UserInfo, OrgInfo, etc.)
│ ├── DTOs/ # Contract DTOs for all IAM domains
│ ├── Enums/ # UserStatus, RoleType, TokenType, etc.
│ ├── Constants/ # IamConstants (ServiceNames, CacheKeys, ErrorCodes, etc.)
│ ├── Helpers/ # GrpcResult / GrpcServiceResponse
│ ├── Caching/ # IOrganizationCacheService + warmup
│ └── Extensions/ # OrganizationCacheExtensions
│ └── Quota/ # Quota Service clients (merged from nera-quota-service)
│ ├── Protos/ # quota_service.proto
│ ├── Clients/ # QuotaHttpClient, QuotaGrpcClient, CachedQuotaClient
│ ├── Configuration/ # QuotaServiceOptions
│ ├── DTOs/ # QuotaDtos (check, consume, release, usage, admin DTOs)
│ └── Constants/ # WellKnownResourceTypes, WellKnownTenants
├── GlobalUsings.cs
├── Nera.Client.csproj
└── README.md
Installation
NuGet Package (once published)
dotnet add package Nera.Client
Project Reference (development)
dotnet add reference ../Nera.Client/Nera.Client.csproj
Quick Start
1. Add configuration to appsettings.json
{
"NeraClient": {
"DefaultTimeout": 30,
"DefaultApiVersion": "v1",
"Services": {
"Menu": {
"BaseUrl": "https://menu-service.nextera.io",
"ApiVersion": "v1",
"Timeout": 15
}
},
"IAM": {
"GrpcGatewayUrl": "http://localhost:8081"
},
"Quota": {
"BaseUrl": "http://quota-service:8080",
"GrpcUrl": "http://quota-service:5101",
"TimeoutSeconds": 5,
"FailOpen": true
}
}
}
2. Register in Program.cs
using Nera.Client.DependencyInjection;
var builder = WebApplication.CreateBuilder(args);
// Register Nera Client with Menu, IAM, and Quota services
builder.Services.AddNeraClient(builder.Configuration, client =>
{
client.AddMenuService();
client.AddAllIamServices();
// Quota via HTTP (REST)
client.AddQuotaService();
// Or Quota via gRPC (high-performance)
// client.AddQuotaGrpcService();
});
3. Inject and use (HTTP — Menu)
using Nera.Client.Services.Menu;
public class MyController : ControllerBase
{
private readonly IMenuServiceClient _menuClient;
public MyController(IMenuServiceClient menuClient)
{
_menuClient = menuClient;
}
[HttpGet("navigation")]
public async Task<IActionResult> GetNavigation()
{
var result = await _menuClient.GetPublicMenuTreeAsync(
project: "nera-admin",
platform: "web");
if (!result.Success)
return StatusCode(result.StatusCode, result.Error);
return Ok(result.Data);
}
}
4. Inject and use (gRPC — IAM)
using Nera.Client.Services.IAM.Interfaces;
public class AuthService
{
private readonly ITokenGrpcClient _tokenClient;
private readonly IUserGrpcClient _userClient;
public AuthService(ITokenGrpcClient tokenClient, IUserGrpcClient userClient)
{
_tokenClient = tokenClient;
_userClient = userClient;
}
public async Task<TokenResponse> ValidateTokenAsync(string token)
{
return await _tokenClient.ValidateTokenAsync(token);
}
public async Task<UserResponse> GetUserAsync(string userId)
{
return await _userClient.GetUserAsync(userId);
}
}
Configuration
Options Pattern
Nera.Client uses the .NET Options Pattern for all configuration. All options can be bound from IConfiguration or overridden in code.
Options Structure
| Class | Section | Description |
|---|---|---|
NeraClientOptions |
NeraClient |
Root config — default timeout, default API version |
ServiceEndpointOptions |
NeraClient:Services:{Name} |
Per-service — BaseUrl, ApiVersion, Timeout, ApiKey, Headers |
MenuServiceOptions |
NeraClient:Services:Menu |
Menu-specific — extends ServiceEndpointOptions with DefaultProject, DefaultPlatform |
IamServiceOptions |
NeraClient:IAM |
IAM gRPC — GrpcGatewayUrl (or ENV_GRPC_GATEWAY_URL env var) |
QuotaServiceOptions |
NeraClient:Quota |
Quota — BaseUrl, GrpcUrl, TimeoutSeconds, FailOpen, EnableCaching, CacheDurationSeconds |
Overriding Options in Code
// Option 1: Inline at registration
builder.Services.AddNeraClient(builder.Configuration, client =>
{
client.AddMenuService(options =>
{
options.BaseUrl = "https://menu.internal.nextera.io";
options.DefaultProject = "nera-admin";
options.DefaultPlatform = "web";
options.Timeout = 10;
});
});
// Option 2: PostConfigure
builder.Services.PostConfigure<MenuServiceOptions>(options =>
{
options.DefaultProject = "my-project";
});
appsettings.json
{
"NeraClient": {
"DefaultTimeout": 30,
"DefaultApiVersion": "v1",
"Services": {
"Menu": {
"BaseUrl": "https://menu-service.nextera.io",
"ApiVersion": "v1",
"Timeout": 15,
"DefaultProject": "nera-admin",
"DefaultPlatform": "web",
"ApiKeyHeader": "X-Api-Key",
"ApiKey": "your-api-key-here",
"Headers": {
"X-Custom-Header": "value"
}
}
},
"IAM": {
"GrpcGatewayUrl": "http://localhost:8081"
},
"Quota": {
"BaseUrl": "http://quota-service:8080",
"GrpcUrl": "http://quota-service:5101",
"TimeoutSeconds": 5,
"FailOpen": true,
"EnableCaching": false,
"CacheDurationSeconds": 30
}
}
}
Environment Variables
Configuration can be overridden via environment variables (standard .NET convention):
# HTTP service overrides
export NeraClient__DefaultTimeout=60
export NeraClient__Services__Menu__BaseUrl=https://menu.staging.nextera.io
export NeraClient__Services__Menu__ApiVersion=v2
# IAM gRPC gateway override
export ENV_GRPC_GATEWAY_URL=http://iam-gateway:8081
# Or via .NET config convention:
export NeraClient__IAM__GrpcGatewayUrl=http://iam-gateway:8081
# Quota service overrides
export ENV_QUOTA_BASE_URL=http://quota-service:8080
export ENV_QUOTA_GRPC_URL=http://quota-service:5101
# Or via .NET config convention:
export NeraClient__Quota__BaseUrl=http://quota-service:8080
export NeraClient__Quota__GrpcUrl=http://quota-service:5101
Per-Environment (appsettings.{Environment}.json)
// appsettings.Development.json
{
"NeraClient": {
"Services": {
"Menu": {
"BaseUrl": "https://localhost:57472"
}
}
}
}
// appsettings.Production.json
{
"NeraClient": {
"Services": {
"Menu": {
"BaseUrl": "https://menu-service.nextera.io"
}
}
}
}
Service Clients
Menu Service
IMenuServiceClient provides typed methods for all Menu Service endpoints.
Admin Endpoints (require authorization)
| Method | Endpoint | Description |
|---|---|---|
CreateMenuItemAsync |
POST /api/v1/menus |
Create a new menu item |
GetMenuItemsAsync |
GET /api/v1/menus |
Get paginated, filterable list |
GetMenuItemByIdAsync |
GET /api/v1/menus/{id} |
Get item detail by ID |
GetMenuItemByCodeAsync |
GET /api/v1/menus/code/{code} |
Get item by hierarchical code |
UpdateMenuItemAsync |
PUT /api/v1/menus/{id} |
Update item (optimistic locking) |
DeleteMenuItemAsync |
DELETE /api/v1/menus/{id} |
Delete item and its descendants |
GetMenuTreeAsync |
GET /api/v1/menus/tree |
Get full menu tree (admin view) |
GetMyMenuTreeAsync |
GET /api/v1/menus/me |
Tree filtered by current user's IAM roles |
ToggleActiveAsync |
POST /api/v1/menus/{id}/toggle-active |
Toggle active/inactive |
MoveMenuItemAsync |
POST /api/v1/menus/{id}/move |
Move item to new parent/position |
BulkReorderAsync |
PUT /api/v1/menus/reorder |
Reorder multiple items at once |
BulkUpdateAsync |
PUT /api/v1/menus/bulk-update |
Bulk activate/deactivate items |
Public Endpoints (anonymous, no auth required)
| Method | Endpoint | Description |
|---|---|---|
GetPublicMenuTreeAsync |
GET /api/v1/public/menus |
Public menu tree (active items only) |
Usage examples
// Inject IMenuServiceClient
private readonly IMenuServiceClient _menuClient;
// 1. Get the public menu tree (localised to Vietnamese)
var publicMenu = await _menuClient.GetPublicMenuTreeAsync(
project: "nera-admin",
platform: "web");
// 2. Get the menu for the current user (filtered by IAM roles)
var myMenu = await _menuClient.GetMyMenuTreeAsync(project: "nera-admin");
// 3. Create a new menu item
var createResult = await _menuClient.CreateMenuItemAsync(new CreateMenuItemRequest
{
Label = "Dashboard",
Url = "/dashboard",
Icon = "dashboard",
Active = true,
Project = "nera-admin",
Platform = "web",
DisplayOrder = 1
});
if (createResult.IsSuccess)
{
var newItem = createResult.Data;
Console.WriteLine($"Created: {newItem!.Id} - {newItem.Code}");
}
// 4. Paginate and filter
var items = await _menuClient.GetMenuItemsAsync(new GetMenuItemsRequest
{
Page = 1,
PageSize = 10,
Active = true,
SearchTerm = "dashboard"
});
// 5. Update a menu item (optimistic concurrency)
var updateResult = await _menuClient.UpdateMenuItemAsync(itemId, new UpdateMenuItemRequest
{
Label = "Updated Dashboard",
Url = "/dashboard/v2",
Version = currentVersion, // Must match server version
Active = true
});
// 6. Bulk operations
var reorderResult = await _menuClient.BulkReorderAsync(new BulkReorderMenuItemsRequest
{
Items = new List<ReorderItem>
{
new() { Id = item1Id, DisplayOrder = 1 },
new() { Id = item2Id, DisplayOrder = 2 },
new() { Id = item3Id, DisplayOrder = 3 }
}
});
Response Handling
All methods return a NeraResult<T> that mirrors the Nera API envelope
({ status, message, data, _metadata, error }):
var result = await _menuClient.GetMenuItemByIdAsync(id);
if (result.IsSuccess)
{
// result.Data contains the response payload
var item = result.Data!;
Console.WriteLine($"Menu: {item.Label}");
// Pagination info (for list endpoints) is in the metadata
var pagination = result.Metadata?.Pagination;
}
else
{
// result.Error.Code — application error code (e.g. "MENU_NOT_FOUND")
// result.Error.Message — localised error message (per X-Language header)
// result.Message — top-level status message
Console.WriteLine($"[{result.Error?.Code}] {result.Error?.Message}");
}
IAM Services (gRPC)
The IAM gRPC clients (merged from nera-iam-common) provide typed access to all Identity & Access Management services via gRPC.
Available Clients
| Interface | Description | Key Methods |
|---|---|---|
IUserGrpcClient |
User management | GetUser, GetUserByEmail, GetUserByUsername, ValidatePassword, CreateUser, UpdateUser, DeleteUser, GetUserRoles, GetUserPermissions |
ITokenGrpcClient |
Token operations | GenerateTokens, ValidateToken, RefreshToken, RevokeToken, GetTokenInfo |
ISessionGrpcClient |
Session management | CreateSession, GetSession, ValidateSession, GetUserSessions, UpdateActivity, ExtendSession, TerminateSession |
IOrganizationGrpcClient |
Organization management | GetOrganization, GetByDomain, GetBySubdomain, CreateOrganization, UpdateOrganization, DeleteOrganization, IsActive, GetAll, VerifyMembership, SecuritySettings |
IAuditLogGrpcClient |
Audit logging | LogAuthActivity, GetAuditLogById, GetAuditLogs |
Registration Options
// Register ALL IAM clients at once
builder.Services.AddNeraClient(builder.Configuration, client =>
{
client.AddAllIamServices();
});
// Or register individual clients
builder.Services.AddNeraClient(builder.Configuration, client =>
{
client.AddIamUserService();
client.AddIamTokenService();
client.AddIamSessionService();
// AddIamOrganizationService(), AddIamAuditLogService()
});
// Override gRPC gateway URL
builder.Services.AddNeraClient(builder.Configuration, client =>
{
client.AddAllIamServices(options =>
{
options.GrpcGatewayUrl = "http://iam-gateway:8081";
});
});
// Add Organization cache service (with optional warmup)
builder.Services.AddNeraClient(builder.Configuration, client =>
{
client.AddAllIamServices();
client.AddOrganizationCacheService(warmupOnStartup: true);
});
Configuration
The IAM gRPC gateway URL is resolved in this order:
NeraClient:IAM:GrpcGatewayUrlfromappsettings.jsonENV_GRPC_GATEWAY_URLenvironment variable- Default:
http://localhost:8081
{
"NeraClient": {
"IAM": {
"GrpcGatewayUrl": "http://localhost:8081"
}
}
}
Note: gRPC uses a separate port (8081) from HTTP (8080) to avoid conflicts.
Usage Examples
using Nera.Client.Services.IAM.Interfaces;
// Token validation
public class TokenValidator
{
private readonly ITokenGrpcClient _tokenClient;
public TokenValidator(ITokenGrpcClient tokenClient)
=> _tokenClient = tokenClient;
public async Task<bool> IsValidAsync(string token)
{
var result = await _tokenClient.ValidateTokenAsync(token);
return result.IsValid;
}
}
// User lookup
public class UserLookup
{
private readonly IUserGrpcClient _userClient;
public UserLookup(IUserGrpcClient userClient)
=> _userClient = userClient;
public async Task<UserResponse?> FindByEmailAsync(string email)
{
return await _userClient.GetUserByEmailAsync(email);
}
}
// Organization domain resolution (with cache)
using Nera.Client.Services.IAM.Caching;
public class TenantResolver
{
private readonly IOrganizationCacheService _cache;
public TenantResolver(IOrganizationCacheService cache)
=> _cache = cache;
public async Task<Guid?> ResolveOrgIdFromDomain(string domain)
{
return await _cache.GetOrgIdByDomainAsync(domain);
}
}
IAM Models, DTOs & Enums
All IAM shared types are available under Nera.Client.Services.IAM.*:
| Namespace | Contents |
|---|---|
Nera.Client.Services.IAM.Models |
UserInfo, OrganizationInfo, RoleInfo, PermissionInfo, TokenInfo, SessionInfo, MfaChallengeInfo, BaseResponse<T>, PaginatedResponse<T>, entity models |
Nera.Client.Services.IAM.DTOs |
Contract DTOs for all domains: LoginRequestDto, GenerateTokensRequestDto, CreateUserRequestDto, CreateSessionRequestDto, GetAuditLogsRequestDto, etc. |
Nera.Client.Services.IAM.Enums |
UserStatus, RoleType, PermissionLevel, OrganizationStatus, SessionStatus, MfaMethod, TokenType, AuthProviderType, etc. |
Nera.Client.Services.IAM.Constants |
IamConstants — ServiceNames, CacheKeys, ExpirationTimes, SystemRoles, SystemPermissions, Headers, ErrorCodes |
Nera.Client.Services.IAM.Helpers |
GrpcResult / GrpcServiceResponse — factory helpers for gRPC responses |
Quota Service (HTTP / gRPC)
The Quota Service client (merged from nera-quota-service/Quota.Client + Quota.Contracts) provides typed access to the Nera Quota Service for resource quota management.
Interface: IQuotaServiceClient
| Method | Description |
|---|---|
CheckQuotaAsync |
Check if a quota allows the requested amount (read-only) |
BatchCheckQuotaAsync |
Check multiple quotas in a single request |
ConsumeQuotaAsync |
Consume quota (atomic check + increment) |
ReleaseQuotaAsync |
Release previously consumed quota |
GetUsageSummaryAsync |
Get usage summary for an organization |
Two Transport Options
| Client Class | Transport | Best For |
|---|---|---|
QuotaHttpClient |
REST/HTTP | External consumers, simple integrations |
QuotaGrpcClient |
gRPC (HTTP/2) | Internal microservices, high-perf low-latency |
CachedQuotaClient |
Decorator | Wraps either client with distributed caching |
Registration
builder.Services.AddNeraClient(builder.Configuration, client =>
{
// Option A: HTTP/REST transport
client.AddQuotaService();
// Option B: gRPC transport (high-performance)
// client.AddQuotaGrpcService();
// Both support inline options override:
// client.AddQuotaService(options =>
// {
// options.BaseUrl = "http://quota-service:8080";
// options.FailOpen = true;
// options.EnableCaching = true;
// options.CacheDurationSeconds = 60;
// });
});
When EnableCaching = true, the client is automatically wrapped with CachedQuotaClient which caches CheckQuota and GetUsageSummary results via IDistributedCache.
Configuration
URL resolution order:
- HTTP:
NeraClient:Quota:BaseUrl→ENV_QUOTA_BASE_URL→http://localhost:8080 - gRPC:
NeraClient:Quota:GrpcUrl→ENV_QUOTA_GRPC_URL→http://localhost:5101
{
"NeraClient": {
"Quota": {
"BaseUrl": "http://quota-service:8080",
"GrpcUrl": "http://quota-service:5101",
"TimeoutSeconds": 5,
"FailOpen": true,
"EnableCaching": false,
"CacheDurationSeconds": 30
}
}
}
Usage Examples
using Nera.Client.Services.Quota;
using Nera.Client.Services.Quota.DTOs;
using Nera.Client.Services.Quota.Constants;
public class PhotoUploadService
{
private readonly IQuotaServiceClient _quotaClient;
public PhotoUploadService(IQuotaServiceClient quotaClient)
=> _quotaClient = quotaClient;
public async Task<bool> CanUploadAsync(Guid orgId)
{
// Check if the org has quota remaining for monthly photos
var result = await _quotaClient.CheckQuotaAsync(
orgId, WellKnownResourceTypes.PhotosMonthly);
return result.IsAllowed;
}
public async Task RecordUploadAsync(Guid orgId, int photoCount)
{
// Consume quota atomically
var result = await _quotaClient.ConsumeQuotaAsync(new ConsumeQuotaRequest
{
OrgId = orgId,
ResourceType = WellKnownResourceTypes.PhotosMonthly,
Amount = photoCount,
Source = "photo-upload-service"
});
if (!result.Success)
throw new InvalidOperationException($"Quota exceeded: {result.ErrorMessage}");
}
public async Task RollbackUploadAsync(Guid orgId, int photoCount)
{
// Release quota on failure
await _quotaClient.ReleaseQuotaAsync(new ReleaseQuotaRequest
{
OrgId = orgId,
ResourceType = WellKnownResourceTypes.PhotosMonthly,
Amount = photoCount,
Source = "photo-upload-service"
});
}
public async Task<UsageSummaryResponse> GetOrgUsageAsync(Guid orgId)
{
return await _quotaClient.GetUsageSummaryAsync(orgId);
}
}
Fail-Open Behavior
When FailOpen = true (default) and the Quota Service is unreachable:
CheckQuotaAsync→ returnsIsAllowed = true(allows the request)ConsumeQuotaAsync→ returnsSuccess = trueReleaseQuotaAsync→ silently logs the failureGetUsageSummaryAsync→ returns empty resources list
When FailOpen = false, all check/consume operations return failure when the service is down.
DTOs & Constants
| Namespace | Contents |
|---|---|
Nera.Client.Services.Quota.DTOs |
QuotaCheckRequest/Response, BatchQuotaCheckRequest/Response, ConsumeQuotaRequest/Response, ReleaseQuotaRequest/Response, UsageSummaryResponse, ResourceUsageDto, plus admin DTOs (TenantDto, PlanDto, SubscriptionDto, etc.) |
Nera.Client.Services.Quota.Constants |
WellKnownResourceTypes (api_requests_daily, storage_gb, photos_monthly, etc.) and WellKnownTenants (photoflow, photoqa) |
Architecture
Resilience
Nera.Client uses Microsoft.Extensions.Http.Resilience (built on Polly) to automatically handle:
- Retry — automatically retries transient failures (HTTP 408, 429, 5xx)
- Circuit Breaker — opens the circuit when a service consistently fails
- Timeout — configurable per-service timeout
- Rate Limiter — concurrency limiter to avoid overwhelming downstream services
HttpClient Lifecycle
Uses the IHttpClientFactory pattern:
- Connection pool is managed automatically
- No socket exhaustion
- Automatic DNS refresh
- Each service client receives its own
HttpClientinstance via the typed client pattern
Error Handling
Request → HttpClient → HTTP Response
├── 2xx → Deserialize NeraResult<T> envelope → IsSuccess = true
├── 4xx/5xx → Deserialize NeraResult<T> (structured error) → IsSuccess = false
└── Network/timeout → NeraClientException
- Any HTTP response →
NeraResult<T>with the Nera envelope (status,message,data,_metadata,error) - Deserialization failure →
NeraClientExceptioncarrying the raw response body - Network / timeout →
NeraClientExceptionwrapping the inner exception
Extending (Adding a New Service)
To add a new service client (e.g. User Service), follow this pattern:
1. Options class
// Configuration/UserServiceOptions.cs
public class UserServiceOptions : ServiceEndpointOptions
{
public const string ServiceName = "User";
// Service-specific options here
}
2. Models (Request/Response)
// Services/User/Models/UserRequests.cs
// Services/User/Models/UserResponses.cs
3. Interface + Implementation
// Services/User/IUserServiceClient.cs
public interface IUserServiceClient
{
Task<NeraResult<UserResponse>> GetUserByIdAsync(Guid id, CancellationToken ct = default);
// ...
}
// Services/User/UserServiceClient.cs
public class UserServiceClient : BaseNeraClient, IUserServiceClient
{
protected override string ServiceName => "UserService";
// Implement using GetAsync<T>, PostAsync<TReq, TRes>, etc.
}
4. Register in the Builder
// Add to NeraClientBuilder.cs
public NeraClientBuilder AddUserService(Action<UserServiceOptions>? configure = null)
{
// Same pattern as AddMenuService()
}
5. Usage
services.AddNeraClient(configuration, builder =>
{
builder
.AddMenuService()
.AddUserService();
});
API Reference
NeraClientOptions
| Property | Type | Default | Description |
|---|---|---|---|
DefaultTimeout |
int |
30 |
Default request timeout (seconds) |
DefaultApiVersion |
string |
"v1" |
Default API version |
Services |
Dictionary<string, ServiceEndpointOptions> |
{} |
Per-service configuration |
ServiceEndpointOptions
| Property | Type | Default | Description |
|---|---|---|---|
BaseUrl |
string |
"" |
Base URL of the service |
ApiVersion |
string? |
null |
API version override (falls back to default) |
Timeout |
int? |
null |
Timeout override in seconds (falls back to default) |
DefaultLanguage |
string? |
null |
BCP-47 tag sent as X-Language header (e.g. "en", "vi") |
ApiKeyHeader |
string? |
null |
API key header name |
ApiKey |
string? |
null |
API key value |
Headers |
Dictionary<string, string> |
{} |
Additional default headers |
MenuServiceOptions (extends ServiceEndpointOptions)
| Property | Type | Default | Description |
|---|---|---|---|
DefaultProject |
string? |
null |
Default project scope for menu queries |
DefaultPlatform |
string? |
null |
Default platform scope for menu queries |
IamServiceOptions
| Property | Type | Default | Description |
|---|---|---|---|
GrpcGatewayUrl |
string |
"" |
gRPC gateway URL. Resolves via config → ENV_GRPC_GATEWAY_URL → http://localhost:8081 |
QuotaServiceOptions
| Property | Type | Default | Description |
|---|---|---|---|
BaseUrl |
string |
"" |
HTTP base URL. Resolves via config → ENV_QUOTA_BASE_URL → http://localhost:8080 |
GrpcUrl |
string |
"" |
gRPC URL. Resolves via config → ENV_QUOTA_GRPC_URL → http://localhost:5101 |
TimeoutSeconds |
int |
5 |
Request timeout in seconds |
FailOpen |
bool |
true |
Allow requests when Quota Service is unreachable |
EnableCaching |
bool |
false |
Enable distributed caching for check/usage results |
CacheDurationSeconds |
int |
30 |
Cache TTL in seconds (when caching is enabled) |
NeraResult<T>
| Property | Type | Description |
|---|---|---|
Status |
string |
"Success" or "Fail" |
Message |
string |
Top-level status message (localised) |
Data |
T? |
Response payload — present when IsSuccess |
Metadata |
NeraMetadata? |
Trace id, processing time, pagination |
Error |
NeraError? |
Structured error — present when Status == "Fail" |
IsSuccess |
bool |
Convenience: Status == "Success" |
NeraError
| Property | Type | Description |
|---|---|---|
Code |
string |
Application error code (e.g. "MENU_NOT_FOUND") |
Message |
string |
Localised error description |
ErrorDetails |
Dictionary<string,object>? |
Field-level validation detail map |
NeraMetadata
| Property | Type | Description |
|---|---|---|
TraceId |
string? |
Distributed trace identifier |
ProcessingTime |
long |
Server-side processing time (ms) |
Timestamp |
DateTime |
Server UTC timestamp |
Pagination |
NeraPagination? |
Pagination info on list endpoints |
License
Internal — Nextera Platform
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net9.0 is compatible. 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. |
-
net9.0
- Google.Protobuf (>= 3.29.3)
- Grpc.Net.Client (>= 2.67.0)
- Microsoft.Extensions.Caching.Abstractions (>= 9.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 9.0.0)
- Microsoft.Extensions.Hosting.Abstractions (>= 9.0.0)
- Microsoft.Extensions.Http (>= 9.0.0)
- Microsoft.Extensions.Http.Resilience (>= 9.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 9.0.0)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 9.0.0)
- System.Text.Json (>= 9.0.0)
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 |
|---|---|---|
| 1.0.0 | 121 | 3/8/2026 |
| 1.0.0-dev0.2 | 63 | 3/8/2026 |
| 1.0.0-dev0.1 | 60 | 3/8/2026 |