Skip to content

Quick Start

If you are new to Smithy, the official Smithy quickstart is a good place to learn the IDL before continuing.

Basic builds require only the .NET SDK. NSmithy bundles the Smithy CLI (including a JRE) inside the NuGet package, so no separate Java or Smithy CLI installation is required.

If you enable SmithyGenerateDocs (Sphinx HTML docs), Python 3.11+ must also be on your PATH.

Optionally install CSharpier for formatted generated code. NSmithy runs it automatically after codegen when it is available, and skips it otherwise.

Install the NSmithy project templates (one-time):

Terminal window
dotnet new install NSmithy.Templates

The contracts project owns the Smithy model and distributes it to the server and client.

Terminal window
mkdir HelloWorld && cd HelloWorld
dotnet new sln -n HelloWorld
dotnet new nsmithy-contracts -n HelloWorld.Contracts
dotnet sln add HelloWorld.Contracts

This generates:

HelloWorld.Contracts/
HelloWorld.Contracts.csproj
model/
service.smithy ← starter restJson1 HelloService model
Terminal window
dotnet new nsmithy-server -n HelloWorld.Server --contracts HelloWorld.Contracts --with-docs
dotnet sln add HelloWorld.Server

The --contracts flag adds a ProjectReference to the contracts project. --with-docs enables the Smithy docs and OpenAPI endpoints. Build and run:

Terminal window
dotnet run --project HelloWorld.Server

The server listens on http://localhost:5000. Test it:

Terminal window
curl http://localhost:5000/hello/world
# {"message":"Hello, world!"}

With --with-docs, two documentation UIs are also available:

  • /docs — Smithy-generated reference docs for your model
  • /openapi — interactive Scalar UI backed by a generated openapi.json

See Endpoint Documentation for details.

Terminal window
dotnet new nsmithy-client -n HelloWorld.Client
dotnet sln add HelloWorld.Client

The client template defaults to a Maven contracts reference for production use: it ships a smithy-build.json that pulls the contracts model from a Maven JAR. For local development, switch to the sibling contracts project instead. Delete the client’s smithy-build.json — when no smithy-build.json exists at the project root, NSmithy synthesizes one from the contracts ProjectReference:

Terminal window
rm HelloWorld.Client/smithy-build.json

Then add a ProjectReference to HelloWorld.Client/HelloWorld.Client.csproj:

<ItemGroup>
<ProjectReference Include="../HelloWorld.Contracts/HelloWorld.Contracts.csproj" />
</ItemGroup>

Then run the client with the server still running:

Terminal window
dotnet run --project HelloWorld.Client
# Hello, world!

When you’re ready to distribute, see Distributing Contracts to publish the contracts JAR and switch back to a Maven reference.

Open HelloWorld.Contracts/model/service.smithy. The template generates a minimal restJson1 service with a single operation:

@restJson1
service HelloService {
version: "2006-03-01"
operations: [SayHello]
}
@readonly
@http(method: "GET", uri: "/hello/{name}")
operation SayHello {
input := {
@required
@httpLabel
name: String
}
output := {
@required
message: String
}
}

@restJson1 is the protocol; it controls serialization and HTTP binding behavior. @http binds the operation to a route. @httpLabel maps name to the {name} path segment. All generated code derives from this model; change the model and rebuild to regenerate it.

Running dotnet build invokes the Smithy CLI and generates C# types under obj/. For the model above you get:

public sealed record SayHelloInput(string Name);
public sealed record SayHelloOutput(string Message);

And a handler interface the server must implement:

public interface IHelloServiceHandler
{
Task<SayHelloOutput> SayHelloAsync(
SayHelloInput input,
CancellationToken ct = default);
}

The generated server uses ASP.NET Core minimal API. Program.cs registers your handler and maps the routes:

builder.Services.AddHelloServiceHandler<HelloHandler>();
app.MapHelloServiceHttp();

HelloHandler (also generated as a starter) returns a greeting:

internal sealed class HelloHandler : IHelloServiceHandler
{
public Task<SayHelloOutput> SayHelloAsync(
SayHelloInput input,
CancellationToken cancellationToken = default
) => Task.FromResult(new SayHelloOutput($"Hello, {input.Name}!"));
}

Replace the body with your real logic. The compiler enforces that every operation in the model has an implementation — add an operation to the model and the build breaks until you handle it.

HelloWorld.Client/Program.cs uses the generated typed client:

var client = new HelloServiceClient(new Uri(endpoint));
var response = await client.SayHelloAsync(new SayHelloInput("world"));
Console.WriteLine(response.Message);

The client and server share the same generated input/output types from the contracts project.

All three templates accept --protocol:

ValueProtocol
restJson1aws.protocols#restJson1 (default)
simpleRestJsonalloy#simpleRestJson
rpcv2Cborsmithy.protocols#rpcv2Cbor
grpcalloy.proto#grpc (experimental)

Additional options:

Terminal window
dotnet new nsmithy-server --help
dotnet new nsmithy-contracts --help
dotnet new nsmithy-client --help

For a more complete example with multiple operations, error types, and a working client/server setup, see nsmithy-minimal.