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.

Install the .NET 10 SDK. Code generation runs the Smithy CLI, which the projects either find on your PATH or get from NuGet. The choice applies to every step on this page.

Install Smithy CLI 1.73.0 or later so that smithy is on your PATH, with Homebrew, Scoop, pixi, or the official installer. See Installing the Smithy CLI.

Install the NSmithy project templates (one-time):

dotnet new install NSmithy.Templates

This walkthrough keeps the contracts, the server, and the client in one solution, with project references between them. A client in another repository consumes the published contracts instead; see Distributing contracts.

  1. Create the contracts project. It owns the Smithy model. The server and client reference it and generate their own C# types during the build.

    mkdir HelloWorld && cd HelloWorld
    dotnet new sln -n HelloWorld
    dotnet new nsmithy-contracts -n HelloWorld.Contracts
    dotnet sln add HelloWorld.Contracts
  2. Create the server. --contracts-project adds a ProjectReference to the contracts project.

    dotnet new nsmithy-server -n HelloWorld.Server \
    --contracts-project HelloWorld.Contracts
    dotnet sln add HelloWorld.Server

    Add --with-docs to also serve generated reference docs at /docs and an interactive OpenAPI explorer at /openapi. It requires Python 3.11+ on your PATH; see Endpoint documentation.

  3. Run the server. It listens on http://localhost:5000.

    dotnet run --project HelloWorld.Server
    curl http://localhost:5000/hello/world
    # {"message":"Hello, world!"}
  4. Create the client. --contracts-project adds a ProjectReference to the contracts project, from which the client generates its types. In another terminal, from the solution directory:

    dotnet new nsmithy-client -n HelloWorld.Client \
    --contracts-project HelloWorld.Contracts
    dotnet sln add HelloWorld.Client
  5. Run the client with the server still running:

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

The solution now looks like this:

  • DirectoryHelloWorld/
    • HelloWorld.slnx
    • DirectoryHelloWorld.Contracts/
      • HelloWorld.Contracts.csproj
      • smithy-build.json
      • Directorymodel/
    • DirectoryHelloWorld.Server/
      • HelloWorld.Server.csproj references the contracts project
      • Program.cs handler implementation and route mapping
    • DirectoryHelloWorld.Client/
      • HelloWorld.Client.csproj references the contracts project
      • Program.cs calls the generated client

When you’re ready to share the contracts with clients in other repositories, see Distributing contracts to publish them as a NuGet package or Maven JAR.

The template generates a minimal restJson1 service with a single operation:

HelloWorld.Contracts/model/service.smithy
@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 cancellationToken = default);
}

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

HelloWorld.Server/Program.cs
builder.Services.AddHelloServiceHandler<HelloHandler>();
app.MapHelloService();

The template supplies HelloHandler in Program.cs; this is your implementation, not a file regenerated during the build:

HelloWorld.Server/Program.cs
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
var client = new HelloServiceClient(new Uri("http://localhost:5000"));
var response = await client.SayHelloAsync(new SayHelloInput("world"));
Console.WriteLine(response.Message);

The client and server generate their input/output types independently from the same Smithy model. The client handles serialization and HTTP communication.

All three templates accept --protocol; use the same value for all three projects:

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

By default the projects run the Smithy CLI on PATH. --smithy-cli-package makes them reference the NSmithy.SmithyCli package instead; see Smithy CLI.

--contracts-project makes the server or client reference a contracts project in the same solution. Without it, the server owns its model, and the client references the published contracts as chosen by --contracts-package (maven, the default, or nuget).

Additional options:

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