Quick start
If you are new to Smithy, the official Smithy quickstart is a good place to learn the IDL before continuing.
Prerequisites
Section titled “Prerequisites”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.
The commands below pass --smithy-cli-package, so the projects reference
NSmithy.SmithyCli, which provides the Smithy CLI and its Java runtime.
Nothing else needs to be installed.
Install the NSmithy project templates (one-time):
dotnet new install NSmithy.TemplatesCreate the projects
Section titled “Create the projects”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.
-
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 HelloWorlddotnet new sln -n HelloWorlddotnet new nsmithy-contracts -n HelloWorld.Contractsdotnet sln add HelloWorld.Contractsmkdir HelloWorld && cd HelloWorlddotnet new sln -n HelloWorlddotnet new nsmithy-contracts -n HelloWorld.Contracts --smithy-cli-packagedotnet sln add HelloWorld.Contracts -
Create the server.
--contracts-projectadds aProjectReferenceto the contracts project.dotnet new nsmithy-server -n HelloWorld.Server \--contracts-project HelloWorld.Contractsdotnet sln add HelloWorld.Serverdotnet new nsmithy-server -n HelloWorld.Server \--contracts-project HelloWorld.Contracts --smithy-cli-packagedotnet sln add HelloWorld.ServerAdd
--with-docsto also serve generated reference docs at/docsand an interactive OpenAPI explorer at/openapi. It requires Python 3.11+ on yourPATH; see Endpoint documentation. -
Run the server. It listens on
http://localhost:5000.dotnet run --project HelloWorld.Servercurl http://localhost:5000/hello/world# {"message":"Hello, world!"} -
Create the client.
--contracts-projectadds aProjectReferenceto 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.Contractsdotnet sln add HelloWorld.Clientdotnet new nsmithy-client -n HelloWorld.Client \--contracts-project HelloWorld.Contracts --smithy-cli-packagedotnet sln add HelloWorld.Client -
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/
- service.smithy starter restJson1
HelloServicemodel
- service.smithy starter restJson1
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.
Walk through the code
Section titled “Walk through the code”The model
Section titled “The model”The template generates a minimal restJson1 service with a single operation:
@restJson1service 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.
Generated types
Section titled “Generated types”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 server handler
Section titled “The server handler”The generated server uses ASP.NET Core minimal API.
Program.cs registers your handler and maps the routes:
builder.Services.AddHelloServiceHandler<HelloHandler>();
app.MapHelloService();The template supplies HelloHandler in Program.cs; this is your implementation,
not a file regenerated during the build:
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.
The generated client
Section titled “The generated client”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.
Template options
Section titled “Template options”All three templates accept --protocol; use the same value for all three
projects:
| Value | Protocol |
|---|---|
restJson1 | aws.protocols#restJson1 (default) |
simpleRestJson | alloy#simpleRestJson |
rpcv2Cbor | smithy.protocols#rpcv2Cbor |
grpc | alloy.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 --helpdotnet new nsmithy-contracts --helpdotnet new nsmithy-client --help