Smithy and TypeSpec
TypeSpec is the IDL most .NET teams meet first. Microsoft created it to author Azure’s REST API specifications, and OpenAPI is still its primary output. Microsoft’s newer client emitters, including the C# one, read the compiled TypeSpec program directly instead of going through an OpenAPI document. Smithy came from the other direction. AWS created it to generate its SDKs, so the model is designed to be consumed by code generators rather than translated into another description format.
Both can describe the same HTTP API. The differences that matter for a .NET team are where the model lives, how a protocol attaches to it, and what the toolchain needs on the build machine.
The model is data
Section titled “The model is data”A Smithy model has a specified
JSON AST. Traits, including custom
ones, are declared in the model as shapes, and their values are stored in that
AST alongside everything else. Any tool in any language can read it. NSmithy’s
generator is a Java plugin that reads the AST, and the C# it emits carries trait
values into runtime schemas. A custom trait such as @owner("catalog-team")
therefore reaches the running application without a generator change.
Preserving a trait and interpreting it are separate jobs: a new serialization
rule still needs code that understands it.
A TypeSpec program is the in-memory type graph of the TypeScript compiler. Decorators are JavaScript functions, or auto decorators that store their arguments in the compiler’s state, and emitters read that state through the compiler’s JavaScript API while the compiler runs. Nothing outside that process sees the model unless an emitter writes it out, usually as OpenAPI. Organization-wide conventions are consequently npm libraries: decorators, linter rules, and emitters written in TypeScript.
This decides where an organization’s conventions live. In Smithy, the custom
trait, the validator that
enforces it, and the generators that act on it all read the same model data,
and selector-based validators are configured in smithy-build.json rather than
written in code.
Protocol is a binding
Section titled “Protocol is a binding”Modeling contracts shows the pattern: shapes and traits declare the contract, and a separate set of traits binds it to a protocol.
@error("client")structure NoSuchResource { @required resourceType: String}
apply Weather @aws.protocols#restJson1apply NoSuchResource @httpError(404)Replace the two apply lines with apply Weather @smithy.protocols#rpcv2Cbor
and the same operations, types, and errors travel as CBOR RPC instead. A service
can declare both protocols at once, and NSmithy serves them from
one handler set. Smithy’s standard protocols
also ship conformance test suites, and
Protocol status reports NSmithy’s results
against them.
In TypeSpec, HTTP is the standard library rather than one binding among several. Routes, status codes, and header bindings are decorators on the models themselves:
@errormodel NoSuchResource { @statusCode statusCode: 404; resourceType: string;}That is the right design for a language whose primary output is OpenAPI, where the document is the HTTP contract. It also means a TypeSpec model is an HTTP model. The Protobuf emitter reads its own decorators, so a service written for HTTP does not become a gRPC service by changing one service-level annotation. Augment decorators move annotations out of the model declarations, which keeps files tidy but does not change what the model describes.
The toolchain
Section titled “The toolchain”NuGet packages provide the Smithy CLI, a Java runtime, and the generation
plugins. Restoring them is the whole setup, and generation runs inside
dotnet build.
The MSBuild reference describes the packages.
Models are shared as Maven JARs, which every Smithy toolchain can consume; see
Distributing contracts.
TypeSpec runs on Node.js. The compiler and every library and emitter are npm
packages, so a .NET project that uses TypeSpec carries a package.json next to
its .csproj and needs Node on every build machine.
Microsoft.TypeSpec.MSBuild
wires the compiler into the build but expects it to be installed through npm.
Models are shared as npm packages.
The C# emitters are at different stages of maturity.
@typespec/http-client-csharp
generates the Azure SDK for .NET and the OpenAI .NET library;
@typespec/http-server-csharp
is earlier in its development. Both target HTTP. NSmithy is itself in preview,
and Protocol status lists what it covers.
Where TypeSpec is the better fit
Section titled “Where TypeSpec is the better fit”- OpenAPI is the deliverable. The OpenAPI emitter is TypeSpec’s most mature output, and the syntax is designed to make large OpenAPI documents short to write. Smithy can emit OpenAPI too, but as a translation of a model that was not designed around it.
- Azure. The Azure libraries add resource providers, long-running operations, and the conventions Azure services follow.
- Authoring abstractions. Templates, spread, inheritance, and unions express repeated patterns compactly. Smithy’s mixins reuse members and traits but offer no generics or subtyping.
- Several API versions in one model. The versioning library describes multiple versions of a service in one place and emits each of them. Smithy versions a model as a whole and checks compatibility between versions with Smithy Diff.
Choosing
Section titled “Choosing”Choose Smithy when the model is the product: one contract that generates clients and servers for several protocols, carries your own traits through to runtime, and is shared with teams working in other languages through Maven. Choose TypeSpec when an OpenAPI document is the product, when you are building on Azure, or when an existing Node and OpenAPI toolchain is the starting point.