Website | Source | Docs (2.0) | Examples
Smithy models consist of shapes and traits. Shapes are instances of types. Traits are used to add more information to shapes that might be useful for clients, servers, or documentation.
Smithy supports the following types:
Type | Description |
---|---|
blob | Uninterpreted binary data |
boolean | Boolean value type |
string | UTF-8 encoded string |
byte | 8-bit signed integer ranging from -128 to 127 (inclusive) |
short | 16-bit signed integer ranging from -32,768 to 32,767 (inclusive) |
integer | 32-bit signed integer ranging from -2^31 to (2^31)-1 (inclusive) |
long | 64-bit signed integer ranging from -2^63 to (2^63)-1 (inclusive) |
float | Single precision IEEE-754 floating point number |
double | Double precision IEEE-754 floating point number |
bigInteger | Arbitrarily large signed integer |
bigDecimal | Arbitrary precision signed decimal number |
timestamp | An instant in time with no UTC offset or timezone. |
document | An untyped JSON-like value. |
List | Homogeneous collection of values |
Map | Map data structure that maps string keys to homogeneous values |
Structure | Fixed set of named heterogeneous members |
Union | Tagged union data structure that can take on several different, but fixed, types |
Service | Entry point of an API that aggregates resources and operations together |
Operation | Represents the input, output and possible errors of an API operation |
Resource | An entity with an identity, set of operations, and child resources |
A resource is contained within a service or another resource. Resources have identifiers, operations, and any number of child resources.
model/weather.smithy
$version: "2"
namespace example.weather
/// Provides weather forecasts.
service Weather {
version: "2006-03-01"
resources: [
City
]
}
resource City {
identifiers: { cityId: CityId }
read: GetCity
list: ListCities
}
// "pattern" is a trait.
@pattern("^[A-Za-z0-9 ]+$")
string CityId
Because the Weather service contains many cities, the City resource defines an identifier. Identifiers are used to refer to a specific resource within a service. The "identifiers" property is a mapping of identifier names to the shape to use for that identifier. If the input structure of an operation uses the same names and targeted shapes as the identifiers property of the resource, the structure is automatically configured to work with the resource so that input members of the operation are used to provide the identity of the resource.
Each City has a single Forecast. This can be defined by adding the Forecast resource to the resources property of the City resource.
resource City {
identifiers: { cityId: CityId }
read: GetCity
list: ListCities
resources: [
Forecast
]
}
resource Forecast {
identifiers: { cityId: CityId }
read: GetForecast
}
Child resources must define the exact same identifiers property of their parent, but they are allowed to add any number of additional identifiers if needed. Because there is only one forecast per city, no additional identifiers were added to the identifiers property that isn't present on the City resource.
The state of a resource is represented through its properties. City contains coordinates, and Forecast has a chance of rain represented as a float. Input and output members of resource operations map to resource properties or identifiers to perform updates on or examine the state of a resource.
model/weather.smithy
resource City {
identifiers: { cityId: CityId }
properties: { coordinates: CityCoordinates }
read: GetCity
list: ListCities
resources: [
Forecast
]
}
structure CityCoordinates {
@required
latitude: Float
@required
longitude: Float
}
structure GetCityOutput for City {
$coordinates
}
resource Forecast {
identifiers: { cityId: CityId }
properties: { chanceOfRain: Float }
read: GetForecast
}
structure GetForecastOutput for Forecast {
$chanceOfRain
}
The put, create, read, update, delete, and list properties of a resource are used to define the lifecycle operations of a resource. Lifecycle operations are the canonical methods used to read and transition the state of a resource using well-defined semantics. Defining lifecycle operations helps automated tooling reason about your API.
Let's define the operation used to "read" a City.
model/weather.smithy
@readonly
operation GetCity {
input := for City {
// "cityId" provides the identifier for the resource and
// has to be marked as required.
@required
$cityId
}
output := for City {
// "required" is used on output to indicate if the service
// will always provide a value for the member.
// "notProperty" indicates that top-level input member "name"
// is not bound to any resource property.
@required
@notProperty
name: String
@required
$coordinates
}
errors: [
NoSuchResource
]
}
// "error" is a trait that is used to specialize
// a structure as an error.
@error("client")
structure NoSuchResource {
@required
resourceType: String
}
And define the operation used to "read" a Forecast.
model/weather.smithy
@readonly
operation GetForecast {
// "cityId" provides the only identifier for the resource since
// a Forecast doesn't have its own.
input := for Forecast {
@required
$cityId
}
output := for Forecast {
$chanceOfRain
}
}
Review
Modeling a service should be easy, no matter the interface.
Build APIs your customers will love using the Smithy Interface Definition Language (IDL).
The Smithy IDL provides an intuitive syntax that codifies best practices learned from years of experience building services and SDKs in over a dozen programming languages.
Use Smithy's extensible model validation tools to ensure the quality and consistency of your APIs. Customizable linting, validation, and backwards-compatibility checks integrate with your IDE and CI/CD pipelines so you catch API quality issues before your customers do.
Smithy's build tool integrations and plugin system make it easy to get started generating code from a Smithy model. Use one of the many open-source plugins for Smithy or create your own to make everything from model diagrams to SDKs.
Write your API model once and generate clients, servers, and documentation for multiple programming languages with Smithy's CLI.
main.smithy
$version: "2.0"
namespace com.example
use aws.protocols#restJson1
use smithy.framework#ValidationException
/// Allows users to retrieve a menu, create a coffee order, and
/// and to view the status of their orders
@title("Coffee Shop Service")
@restJson1
service CoffeeShop {
version: "2024-08-23"
operations: [
GetMenu
]
resources: [
Order
]
errors: [
ValidationException
]
}
/// Retrieve the menu
@http(method: "GET", uri: "/menu")
@readonly
operation GetMenu {
output := {
items: CoffeeItems
}
}
coffee.smithy
$version: "2.0"
namespace com.example
/// An enum describing the types of coffees available
enum CoffeeType {
DRIP
POUR_OVER
LATTE
ESPRESSO
COLD_BREW
}
/// A structure which defines a coffee item which can be ordered
structure CoffeeItem {
@required
type: CoffeeType
@required
description: String
}
/// A list of coffee items
list CoffeeItems {
member: CoffeeItem
}
order.smithy
$version: "2.0"
namespace com.example
/// An Order resource, which has an id and descibes an order by the type of coffee
/// and the order's status
resource Order {
identifiers: {
id: Uuid
}
properties: {
coffeeType: CoffeeType
status: OrderStatus
}
read: GetOrder
create: CreateOrder
}
/// Create an order
@idempotent
@http(method: "PUT", uri: "/order")
operation CreateOrder {
input := for Order {
@required
$coffeeType
}
output := for Order {
@required
$id
@required
$coffeeType
@required
$status
}
}
/// Retrieve an order
@readonly
@http(method: "GET", uri: "/order/{id}")
operation GetOrder {
input := for Order {
@httpLabel
@required
$id
}
output := for Order {
@required
$id
@required
$coffeeType
@required
$status
}
errors: [
OrderNotFound
]
}
/// An error indicating an order could not be found
@httpError(404)
@error("client")
structure OrderNotFound {
message: String
orderId: Uuid
}
/// An identifier to describe a unique order
@length(min: 1, max: 128)
@pattern("^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$")
string Uuid
/// An enum describing the status of an order
enum OrderStatus {
IN_PROGRESS
COMPLETED
}
index.ts
import { getCoffeeShopServiceHandler } from "@com.example/coffee-shop-server";
import { IncomingMessage, ServerResponse, createServer } from "http";
import { convertRequest, writeResponse } from "@aws-smithy/server-node";
import { CoffeeShop } from "./CoffeeShop";
// Instantiate our coffee service implementation
const coffeeService = new CoffeeShop();
// Create a service handler using our coffee service
const serviceHandler = getCoffeeShopServiceHandler(coffeeService);
// The coffee shop context object
const ctx = { orders: new Map(), queue: [] };
// Create the node server with the service handler
const server = createServer(async function (
req: IncomingMessage,
res: ServerResponse<IncomingMessage> & { req: IncomingMessage }
) {
// Convert NodeJS's http request to an HttpRequest.
const httpRequest = convertRequest(req);
// Call the service handler, which will route the request to the GreetingService
// implementation and then serialize the response to an HttpResponse.
const httpResponse = await serviceHandler.handle(httpRequest, ctx);
// Write the HttpResponse to NodeJS http's response expected format.
return writeResponse(httpResponse, res);
});
const port = 3001
server.listen(port);
console.log(`Started server on port ${port}...`);
// Asynchronously handle orders as they come in
coffeeService.handleOrders(ctx)
CoffeeShop.ts
import { CoffeeShopService, CoffeeType, CreateOrderServerInput, CreateOrderServerOutput, GetMenuServerInput, GetMenuServerOutput, GetOrderServerInput, GetOrderServerOutput, OrderNotFound, OrderStatus } from "@com.example/coffee-shop-server";
import { randomUUID } from "crypto";
// A context object for holding state in our service
export interface CoffeeShopContext {
// A map for storing order information
orders: Map<string, OrderData>;
// An order queue for handling orders
queue: OrderData[];
}
// An implementation of the service from the SSDK
export class CoffeeShop implements CoffeeShopService<CoffeeShopContext> {
async CreateOrder(input: CreateOrderServerInput, context: CoffeeShopContext): Promise<CreateOrderServerOutput> {
console.log("received an order request...")
const order = {
orderId: randomUUID(),
coffeeType: input.coffeeType,
status: OrderStatus.IN_PROGRESS
}
context.orders.set(order.orderId, order)
context.queue.push(order)
console.log(`created order: ${JSON.stringify(order)}`)
return {
id: order.orderId,
coffeeType: order.coffeeType,
status: order.status
}
}
async GetMenu(input: GetMenuServerInput, context: CoffeeShopContext): Promise<GetMenuServerOutput> {
console.log("getting menu...")
return {
items: [
{
type: CoffeeType.DRIP,
description: "A clean-bodied, rounder, and more simplistic flavour profile.\n" +
"Often praised for mellow and less intense notes.\n" +
"Far less concentrated than espresso."
},
{
type: CoffeeType.POUR_OVER,
description: "Similar to drip coffee, but with a process that brings out more subtle nuances in flavor.\n" +
"More concentrated than drip, but less than espresso."
},
{
type: CoffeeType.LATTE,
description: "A creamier, milk-based drink made with espresso.\n" +
"A subtle coffee taste, with smooth texture.\n" +
"High milk-to-coffee ratio."
},
{
type: CoffeeType.ESPRESSO,
description: "A highly concentrated form of coffee, brewed under high pressure.\n" +
"Syrupy, thick liquid in a small serving size.\n" +
"Full bodied and intensely aromatic."
},
{
type: CoffeeType.COLD_BREW,
description: "A high-extraction and chilled form of coffee that has been cold-pressed..\n" +
"Different flavor profile than other hot methods of brewing.\n" +
"Smooth and slightly more caffeinated as a result of its concentration."
}
]
}
}
async GetOrder(input: GetOrderServerInput, context: CoffeeShopContext): Promise<GetOrderServerOutput> {
console.log(`getting an order (${input.id})...`)
if (context.orders.has(input.id)) {
const order = context.orders.get(input.id)
return {
id: order.orderId,
coffeeType: order.coffeeType,
status: order.status
}
} else {
console.log(`order (${input.id}) does not exist.`)
throw new OrderNotFound({
message: `order ${input.id} not found.`,
orderId: input.id
})
}
}
// Handle orders as they come in (FIFO), marking them completed based on some random
// timing (to simulate a delay)
async handleOrders(context: CoffeeShopContext) {
console.log("handling orders...")
while (true) {
await new Promise(resolve => setTimeout(resolve, Math.random() * 1000 + 1000));
let order = context.queue.shift()
if (order != null) {
order.status = OrderStatus.COMPLETED
console.log(`order ${order.orderId} is completed.`)
}
}
}
}
// A data object to hold order data
interface OrderData {
orderId: string
coffeeType: CoffeeType;
status: OrderStatus;
}
smithy-kotlin
can be used to generate Kotlin SDKsLast modified 23 August 2025