NSmithy
Smithy code generation for .NET
Typed servers, clients, and API documentation, generated from Smithy models inside your MSBuild workflow.
From model to runtime
Describe operations, shapes, and traits once. dotnet build emits the server stub, the client, and the docs.
weather.smithy source
$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.AddWeatherServiceHandler<WeatherHandler>(); app.MapWeatherService(); // 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
Fill in the typed handler, then register and map it — routing, serialization, and docs are wired for you. A strongly typed async client, generated from the same model.
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.
REQUEST
GET /forecast/Zurich HTTP/1.1 accept: application/json
RESPONSE
HTTP/1.1 200 OK content-type: application/json { "tempC": 18.4, "summary": "Clear skies over Zurich" }
REQUEST
POST / HTTP/1.1 content-type: application/x-amz-json-1.1 x-amz-target: Weather.GetForecast { "city": "Zurich" }
RESPONSE
HTTP/1.1 200 OK content-type: application/x-amz-json-1.1 { "tempC": 18.4, "summary": "Clear skies over Zurich" }
REQUEST
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"
RESPONSE
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 …"
REQUEST
: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"
RESPONSE
: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
Plain REST with JSON bodies and full HTTP semantics — the default for browser-facing APIs. JSON-RPC: every operation is a POST to a uniform endpoint, framed by an operation target. Compact binary CBOR over HTTP — smaller payloads and faster parsing, exact same model. gRPC: length-prefixed Protobuf frames over HTTP/2 — ideal for service-to-service calls.
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.
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
/docs