Skip to content

NSmithy

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.

smithy build 0 errors
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.

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.

GetForecast handler & client — unchanged
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.

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.

Scalar OpenAPI explorer showing the GetForecast endpoint
Scalar explorer · /openapi
Sphinx reference documentation showing the GetForecast operation
Sphinx reference · /docs