Skip to content

NSmithy

NSmithy
Built on the Smithy IDL · smithy.io

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.

REST JSON-RPC CBOR-RPC gRPC +extensible

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.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
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.

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.

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

Get started

The quick start walks through modeling, building, and calling a small service.