InterfaceRpc.Client 3.0.0

dotnet add package InterfaceRpc.Client --version 3.0.0
                    
NuGet\Install-Package InterfaceRpc.Client -Version 3.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="InterfaceRpc.Client" Version="3.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="InterfaceRpc.Client" Version="3.0.0" />
                    
Directory.Packages.props
<PackageReference Include="InterfaceRpc.Client" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add InterfaceRpc.Client --version 3.0.0
                    
#r "nuget: InterfaceRpc.Client, 3.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package InterfaceRpc.Client@3.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=InterfaceRpc.Client&version=3.0.0
                    
Install as a Cake Addin
#tool nuget:?package=InterfaceRpc.Client&version=3.0.0
                    
Install as a Cake Tool

InterfaceRpc

Turn a C# interface into an HTTP API and a typed client, generated at compile time.

.NET License InterfaceRpc.Service InterfaceRpc.Client

Write an interface once and share it. The server gets ASP.NET Core minimal API endpoints for every method, and the client gets an implementation that calls them over HTTP. Both are generated when you build.

✨ Features

  • ⚑ No runtime reflection. Source generators write the endpoints and the client during the build.
  • 🧩 Built on the platform. Minimal APIs, IHttpClientFactory, dependency injection and System.Text.Json. Nothing custom to learn.
  • πŸ” Standard authorization. [Authorize], [AllowAnonymous], policies and route group conventions all work.
  • πŸ›‘οΈ Errors at compile time. Contracts that can't work over HTTP fail the build with a clear message.
  • 🌐 Plain HTTP and JSON. Any language or tool can call your service.
  • βœ‚οΈ Trim and AOT friendly. Both libraries are annotated for trimming and native AOT.

πŸš€ Quick start

1. Install

Package Add it to
InterfaceRpc.Service The ASP.NET Core app that hosts the service
InterfaceRpc.Client Any app that calls the service
dotnet add package InterfaceRpc.Service --version 3.0.0
dotnet add package InterfaceRpc.Client --version 3.0.0

2. Define a contract

Put the interface in a class library that both apps reference. It can target netstandard2.0.

public interface IGreeter
{
    Task<string> GreetAsync(string name);
}

3. πŸ–₯️ Host it

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddScoped<IGreeter, Greeter>();

var app = builder.Build();
app.MapRpcService<IGreeter>();   // POST /GreetAsync
app.Run();

4. πŸ’» Call it

builder.Services.AddRpcClient<IGreeter>(c => c.BaseAddress = new Uri("https://api.example"));

// Then inject IGreeter anywhere:
var message = await greeter.GreetAsync("Rush");

That's it. Calling GreetAsync on the client sends POST /GreetAsync to the server.

πŸ” How it works

            IGreeter (shared contract library)
             β”‚                              β”‚
  AddRpcClient<IGreeter>()     MapRpcService<IGreeter>()
             β”‚                              β”‚
             v                              v
   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  POST  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
   β”‚ generated client   β”‚ ─────> β”‚ generated endpoint β”‚
   β”‚ HttpClient + JSON  β”‚ <───── β”‚ minimal API + DI   β”‚
   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  JSON  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         client app                    server app

The generators run in the project that calls AddRpcClient<T>(), RpcClient.Create<T>() or MapRpcService<T>(), so the interface can live in any referenced assembly. Generated code registers itself when your app starts, so the library finds it without reflection.

πŸ’‘ Tip: To read the generated code, expand Dependencies β€Ί Analyzers in Visual Studio, or add <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles> to your project file.

πŸ“‘ Wire format

Nothing is hidden: each method is an ordinary JSON endpoint.

POST /GreetAsync HTTP/1.1
Content-Type: application/json

{ "name": "Rush" }
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

"Hello, Rush!"
Topic Behavior
Route POST {prefix}/{MethodName}. Method names ignore case.
Request body A JSON object with one property per parameter. Omitted for methods without parameters.
Missing arguments Take the parameter's default value.
CancellationToken Not sent. The service receives HttpContext.RequestAborted.
Result 200 with the JSON result (null for a null result). void, Task and ValueTask methods return 204.
Errors Malformed requests get a 400 or 415 problem details response. The client throws HttpRequestException with StatusCode set.

βš™οΈ Configuration

Server

I want to… Do this
Put the endpoints under a prefix app.MapRpcService<IGreeter>("/rpc")
Require auth for every method app.MapRpcService<IGreeter>().RequireAuthorization()
Protect or open up one method [Authorize(Roles = "Admin")] or [AllowAnonymous] on the interface method
Add rate limiting, tags, CORS… Chain it: MapRpcService returns the RouteGroupBuilder
Change JSON settings builder.Services.ConfigureHttpJsonOptions(o => ...)

The implementation is resolved from the request's services on every call, so any lifetime works.

πŸ“ Note: Any ASP.NET Core attribute on the interface or its methods ([Authorize], [AllowAnonymous], [EnableRateLimiting], [Tags]…) becomes endpoint metadata. [Authorize] can only be applied to methods. To cover a whole interface, use .RequireAuthorization() or a subclass of AuthorizeAttribute whose AttributeUsage allows interfaces.

Client

I want to… Do this
Set the server address AddRpcClient<IGreeter>(c => c.BaseAddress = new Uri(...))
Add auth headers, logging, retries Chain handlers: .AddHttpMessageHandler<BearerTokenHandler>(), .AddStandardResilienceHandler()
Change JSON settings .ConfigureRpcClient(o => o.JsonSerializerOptions.Converters.Add(...))
Skip dependency injection RpcClient.Create<IGreeter>(new HttpClient { BaseAddress = ... })
builder.Services.AddRpcClient<IGreeter>(c => c.BaseAddress = new Uri("https://api.example/rpc"))
    .AddHttpMessageHandler<BearerTokenHandler>()
    .AddStandardResilienceHandler()
    .ConfigureRpcClient(o => o.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()));

πŸ“ Note: Synchronous interface methods block on the asynchronous pipeline, so every message handler still runs. Prefer Task-returning methods where you can.

Native AOT

Add a JsonSerializerContext for your contract types on both sides, with ConfigureHttpJsonOptions on the server and ConfigureRpcClient on the client.

βœ… Supported contracts

Works:

  • Return types void, T, Task, Task<T>, ValueTask and ValueTask<T>
  • Parameters of any type System.Text.Json can serialize, including in, params and default values
  • CancellationToken parameters
  • Methods inherited from base interfaces

Doesn't work (and fails the build):

  • Overloaded methods (methods are routed by name)
  • Generic methods, ref and out parameters, ref structs such as Span<T>
  • Properties, events and IAsyncEnumerable<T>

Compiler diagnostics

Problems are reported at the AddRpcClient, RpcClient.Create or MapRpcService call.

ID Severity Meaning
IRPC001 ❌ Error The type argument is not an interface.
IRPC002 ❌ Error A member can't be called remotely (see the list above).
IRPC003 ❌ Error Two methods share a name. Overloads aren't supported.
IRPC004 ⚠️ Warning The type argument is a generic type parameter, so no code can be generated at that call.
IRPC005 ❌ Error Generated code can't access the interface (for example, a private nested interface).
IRPC006 ⚠️ Warning A tuple type is used. System.Text.Json serializes tuples as {} unless IncludeFields is enabled; use a record.

⬆️ Upgrading from 2.x

⚠️ Important: Version 3 changes the wire format (arguments are now a named JSON object), so upgrade clients and services together.

2.x 3.x
app.UseRpcService<T>(o => o.ServiceFactory = ...) Register T with dependency injection, then app.MapRpcService<T>()
RpcServiceOptions.Prefix app.MapRpcService<T>("/prefix")
AuthorizationScope.Required app.MapRpcService<T>().RequireAuthorization()
AuthorizationScope.AdHoc with [Authorize] on the implementation [Authorize] on the interface methods
AuthorizationHandler ASP.NET Core authorization policies
RpcClient<T>.Create(url) services.AddRpcClient<T>(...) or RpcClient.Create<T>(httpClient)
RpcClientOptions.Extensions DelegatingHandlers via AddHttpMessageHandler
RpcClient.SetAuthorization / SetAuthorizationHeaderAction A DelegatingHandler, or HttpClient.DefaultRequestHeaders
SerializerDotNet (JSON, Protobuf) System.Text.Json

πŸ› οΈ Building from source

dotnet build
dotnet test
dotnet pack -c Release   # writes both packages to ./nupkg/<version>

The Examples folder has a demo service and a console client. Start InterfaceRpcDemoService, then run InterfaceRpcDemoClient.

The version comes from <Version> in Directory.Build.props. Building updates the install commands in this README to match.

πŸ’¬ Feedback

Found a bug or have an idea? Open an issue.

πŸ“„ License

Apache 2.0

Product Compatible and additional computed target framework versions.
.NET 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 is compatible.  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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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
3.0.0 35 9/26/2026
2.2.3 1,222 9/14/2022
2.2.2 668 9/14/2022
2.2.1 665 9/13/2022
2.2.0 669 9/13/2022
2.1.2 1,143 6/10/2021
2.1.1 1,222 9/10/2020
2.1.0 1,553 10/30/2019
2.0.0 847 9/26/2019
2.0.0-n 909 7/11/2019
2.0.0-m 649 7/11/2019
2.0.0-l 678 7/11/2019
2.0.0-k 691 7/10/2019
2.0.0-j 654 7/10/2019
2.0.0-i 668 7/10/2019
2.0.0-h 686 6/23/2019
2.0.0-g 689 6/20/2019
2.0.0-f 692 6/19/2019
2.0.0-e 643 6/18/2019
2.0.0-d 662 6/18/2019
Loading failed