NSmithy
Smithy code generation for .NET
NSmithy generates typed servers, clients, and API documentation for .NET from a single
Smithy model. Code generation runs as part of dotnet build.
From model to runtime
Describe operations, shapes, and traits once. dotnet build emits the server stub, the client, and the docs.
$version: "2" namespace example.weather use aws.protocols#restJson1 @restJson1 service Weather { version: "2024-01-01" operations: [GetForecast] } @readonly @http(method: "GET", uri: "/forecast/{city}") operation GetForecast { input := { @required @httpLabel city: String } output := { @required tempC: Float summary: String } }
using Example.Weather; // generated public class WeatherHandler : IWeatherHandler { public Task<GetForecastOutput> GetForecastAsync( GetForecastInput input, CancellationToken ct = default) { return Task.FromResult(new GetForecastOutput( tempC: 18.4f, summary: $"Clear skies over {input.City}")); } } // register the handler, map the routes + docs builder.Services.AddWeatherHandler<WeatherHandler>(); app.MapWeatherHttp(); // REST routes -> /forecast/{city} app.MapSmithyOpenApi(); // Scalar UI -> /openapi app.MapSmithyDocs(); // Sphinx docs -> /docs
using Example.Weather; // generated IWeatherClient client = new WeatherClient(endpoint); var forecast = await client.GetForecastAsync( new GetForecastInput(City: "Zurich")); Console.WriteLine(forecast.Summary); // Clear skies over Zurich
The protocol is a trait on the model
Handler and client code stay the same. Changing the protocol trait on the service changes the wire format — REST, JSON-RPC, binary CBOR, or gRPC over HTTP/2.
GET /forecast/Zurich HTTP/1.1 accept: application/json
HTTP/1.1 200 OK content-type: application/json { "tempC": 18.4, "summary": "Clear skies over Zurich" }
POST / HTTP/1.1 content-type: application/x-amz-json-1.1 x-amz-target: Weather.GetForecast { "city": "Zurich" }
HTTP/1.1 200 OK content-type: application/x-amz-json-1.1 { "tempC": 18.4, "summary": "Clear skies over Zurich" }
POST /service/Weather/operation/GetForecast HTTP/1.1 smithy-protocol: rpc-v2-cbor content-type: application/cbor A1 # map(1) 64 63 69 74 79 # "city" 66 5A 75 72 69 63 68 # "Zurich"
HTTP/1.1 200 OK smithy-protocol: rpc-v2-cbor content-type: application/cbor A2 # map(2) 65 74 65 6D 70 43 # "tempC" FB 40 32 66 66 66 66 # 18.4 67 73 75 6D 6D 61 72 79 # "summary" 77 43 6C 65 61 72 ... # "Clear skies …"
:method: POST :path: /weather.Weather/GetForecast :scheme: https :authority: api.example.com content-type: application/grpc+proto te: trailers 00 00 00 00 08 # frame: 8 bytes 0A 06 5A 75 72 69 63 68 # field 1 "Zurich"
:status: 200 content-type: application/grpc+proto 00 00 00 00 1E # frame: 30 bytes 0D 66 66 93 41 # tempC = 18.4 12 17 43 6C 65 61 72 ... # summary "Clear …" grpc-status: 0
Part of the Smithy ecosystem
Smithy is the IDL behind AWS's public APIs. The same model works with the official Smithy code generators for other languages.
Protocol agnostic
REST, JSON-RPC, CBOR and gRPC from one model — switch or serve several at once.
MSBuild-native
No separate codegen step. MSBuild runs smithy build, adds .g.cs files, and tracks model changes for incremental builds.
Type-safe contracts
Generated handlers and clients are strongly typed C# records, so the contract is checked at compile time.
Cross-ecosystem
The same contract powers clients in Java, Python, TypeScript, Rust, Swift and more — your .NET service stays interoperable.
Docs included
A Scalar OpenAPI explorer and Sphinx reference are generated at compile time and served as static files.
Extensible
Add protocols and generators through a plugin surface — the model stays the single source of truth.
Generated API documentation
app.MapSmithyOpenApi() mounts an interactive Scalar explorer;
app.MapSmithyDocs() serves the Sphinx reference — both generated straight from the model at build time.
/openapi
/docsGet started
The quick start walks through modeling, building, and calling a small service.