1. The Smithy model#

The Smithy model describes the Smithy semantic model and the files used to create it. Smithy models are used to describe services and data structures.

1.1. Smithy overview#

Smithy is a framework that consists of a semantic model, file formats used to define a model, and a build process used to validate models and facilitate model transformations.

Figure 1.1: Smithy framework concepts#
                ┌────────────────┐ part of          ┌────────────────┐
                │                │╲                ╱│                │
                │ Semantic Model │─○──────────────○─│   Model File   │
                │                │╱                ╲│                │
                └────────────────┘                  └────────────────┘
                                           split into       ╲│╱
                                                             ○
                                                             │
                ┌────────────────┐                           ┼
                │JSON AST (.json)│────────┐         ┌────────────────┐
                └────────────────┘        │         │                │
                                          ├────────▷│ Representation │
                ┌────────────────┐        │         │                │
                │ IDL (.smithy)  │────────┘         └────────────────┘
                └────────────────┘
Semantic model
The in-memory model used by tools. The semantic model may be serialized into one or more model file representations.
Model File

A file on the file system, in a particular representation. The model files that make up a semantic model MAY be split across multiple files to improve readability or modularity, and those files are not required to use the same representation. Model files do not explicitly include other model files; this responsibility is left to tooling to ensure that all necessary model files are merged together to form a valid semantic model.

One or more model files can be assembled (or merged) together to form a semantic model.

Representation

A particular model file format such as the Smithy IDL or JSON AST. Representations are loaded into the semantic model by mapping the representation to concepts in the semantic model.

  • Smithy IDL: a human-readable format that aims to streamline authoring and reading models.
  • JSON AST: a machine-readable JSON-based format.

1.2. The semantic model#

Smithy's semantic model is an in-memory model used by tools. It is independent of any particular serialized representation. The semantic model contains metadata and a graph of shapes connected by shape IDs.

Figure 1.2: The semantic model#
                                      ┌───────────────┐
                                      │Semantic Model │╲
                                      ├───────────────┤─○────────┐
                                      │metadata?      │╱         │
                                      │               │          │
                                      │               │          │
                                      └───────────────┘          │
                                              ┼     ┼ prelude    │
                                              │     ○────────────┘
                                              ○
                                       shapes╱│╲
    ┌───────────────┐                 ┌───────────────┐
    │ Applied Trait │╲          shape │  «abstract»   │
    ├───────────────┤─○──────────────┼│     Shape     │            ┌───────────────┐
    │               │╱                ├───────────────┤            │    ShapeID    │
    │               │                 │               │            ├───────────────┤
    │               │╲     applied-to │               │         id │namespace      │
    │               │─○──────────────┼│               │┼──────────┼│shape_name     │
    │               │╱traits          │               │            │member_name?   │
    └───────────────┘                 └───────────────┘            └───────────────┘
Shape
Shapes are named data definitions that describe the structure of an API. Shapes are referenced and connected by shape IDs. Relationships between shapes are formed by members that target other shapes, properties of shapes like the input and output properties of an operation, and applied traits that attach a trait to a shape.
Shape ID
A shape ID is used to identify shapes defined in a model. For example, smithy.example#MyShape, smithy.example#Foo$bar, and Baz are all different kinds of shape IDs.
Trait
Traits are specialized shapes that form the basis of Smithy's meta-model. Traits are applied to shapes to associate metadata to a shape. They are typically used by tools to influence validation, serialization, and code generation.
Applied trait
An applied trait is an instance of a trait applied to a shape, configured using a node value.
Model metadata
Metadata is a schema-less extensibility mechanism used to associate metadata to an entire model.
Prelude
The prelude defines various simple shapes and every trait defined in the core specification. All Smithy models automatically include the prelude.

1.3. Model metadata#

Metadata is a schema-less extensibility mechanism used to associate metadata to an entire model. For example, metadata is used to define validators and model-wide suppressions. Metadata is defined using a node value. The following example configures a model validator:

$version: "2"
metadata validators = [
    {
        name: "EmitEachSelector"
        id: "OperationInputName"
        message: "This shape is referenced as input but the name does not end with 'Input'"
        configuration: {
            selector: "operation -[input]-> :not([id|name$=Input i])"
        }
    }
]

1.3.1. Metadata conflicts#

When a conflict occurs between top-level metadata key-value pairs, the following conflict resolution logic is used:

  1. If both values are arrays, the values of both arrays are concatenated into a single array.
  2. Otherwise, if both values are exactly equal, the conflict is ignored.
  3. Otherwise, the conflict is invalid.

Given the following two Smithy models:

model-a.smithy#
$version: "2"
metadata "foo" = ["baz", "bar"]
metadata "qux" = "test"
metadata "validConflict" = "hi!"
model-b.smithy#
$version: "2"
metadata "foo" = ["lorem", "ipsum"]
metadata "lorem" = "ipsum"
metadata "validConflict" = "hi!"

Merging model-a.smithy and model-b.smithy produces the following model:

$version: "2"
metadata "foo" = ["baz", "bar", "lorem", "ipsum"]
metadata "qux" = "test"
metadata "lorem" = "ipsum"
metadata "validConflict" = "hi!"

1.4. Node values#

Node values are JSON-like values used to define metadata and the value of an applied trait.

Figure 1.3: Node value types#
┌─────────────────┐                     ┌─────────────┐
│ Semantic Model  │                     │Applied Trait│
└─────────────────┘                     └─────────────┘
  │                                            │
  │                                            │
  │                                            ┼ nodeValue
  │                                     ┌─────────────┐
  │                                     │ «abstract»  │
  │                                     │    Value    │
  │metadata                             └─────────────┘
  │                                            △
  ○      ┌───────────────────┬─────────────────┼───────────────┬───────────────┐
  ┼      │                   │                 │               │               │
┌─────────────────┐ ┌─────────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│     Object      │ │      Array      │ │   Number    │ │   Boolean   │ │   String    │
├─────────────────┤ ├─────────────────┤ └─────────────┘ └─────────────┘ └─────────────┘
│members:         │ │members: [Value] │
│  [String, Value]│ └─────────────────┘
└─────────────────┘

The following example defines metadata using a node value:

metadata foo = "hello"

The following example defines a trait using a node value:

$version: "2"
namespace smithy.example

@length(min: 1, max: 10)
string MyString

1.4.1. Node value types#

Node values have the same data model as JSON; they consist of the following kinds of values:

Type Description
null The lack of a value
string A UTF-8 string
number A double precision floating point number
boolean A Boolean, true or false value
array A list of heterogeneous node values
object A map of string keys to heterogeneous node values

1.5. Merging model files#

Multiple model files can be used to create a semantic model. Implementations MUST take the following steps when merging two or more model files:

  1. Merge the metadata objects of all model files. If top-level metadata key-value pairs conflict, merge the metadata if possible or fail.
  2. Shapes defined in a single model file are added to the semantic model as-is.
  3. Shapes with the same shape ID defined in multiple model files are reconciled using the following rules:
    1. All conflicting shapes MUST have the same shape type.
    2. Conflicting aggregate shape types MUST contain the same members that target the same shapes.
    3. Conflicting service shape types MUST contain the same properties and target the same shapes.
    4. The traits from each shape are treated as if they are defined using an apply statement: non-conflicting traits are added to the merged shape, and conflicting traits are resolved through step (4).
  4. Conflicting traits defined in shape definitions or through apply statements are reconciled using trait conflict resolution.

1.6. Shapes#

Smithy models are made up of shapes. Shapes are named definitions of types.

Shapes are visualized using the following diagram:

Figure 1.4: Smithy shapes#
                                  ┌─────────────┐
                         members ╱│ «abstract»  │
                        ┌───────○─│    Shape    │
                        │        ╲│             │
                        │         └─────────────┘
                        │                △
              ┌─────────│────────────────┼────────────────────┐
              │         │                │                    │
      ┌───────────────┐ │         ┌─────────────┐      ┌─────────────┐
      │  «abstract»   │ │container│ «abstract»  │      │ «abstract»  │
      │    Simple     │ └────────┼│  Aggregate  │      │   Service   │
      └───────────────┘           └─────────────┘      └─────────────┘
              △                    △                    △
┌──────────┐  │  ┌──────────┐      │    ┌────────────┐  │    ┌─────────────────────────┐
│blob      │──┼──│boolean   │      ├────│    List    │  │    │         Service         │
└──────────┘  │  └──────────┘      │    ├────────────┤  │    ├─────────────────────────┤
┌──────────┐  │  ┌──────────┐      │    │member      │  │    │version                  │
│document  │──┼──│string    │      │    └────────────┘  ├────│operations: [Operation]? │
└──────────┘  │  └──────────┘      │                    │    │resources: [Resource]?   │
┌──────────┐  │       △            │    ┌────────────┐  │    └─────────────────────────┘
│timestamp │──┤       │            ├────│    Map     │  │    ┌─────────────────────────┐
└──────────┘  │  ┌──────────┐      │    ├────────────┤  │    │        Operation        │
              │  │enum      │      │    │key         │  │    ├─────────────────────────┤
              │  └──────────┘      │    │value       │  │    │input: Structure         │
      ┌───────────────┐            │    └────────────┘  ├────│output: Structure        │
      │  «abstract»   │            │                    |    │errors: [Structure]?     │
      │    Number     │            │    ┌────────────┐  │    └─────────────────────────┘
      └───────────────┘            ├────│ Structure  │  │    ┌─────────────────────────┐
              △                    │    └────────────┘  │    │        Resource         │
┌──────────┐  │  ┌──────────┐      │    ┌────────────┐  │    ├─────────────────────────┤
│float     │──┼──│double    │      └────│   Union    │  │    │identifiers?             │
└──────────┘  │  └──────────┘           └────────────┘  │    │create: Operation?       │
┌──────────┐  │  ┌──────────┐                           │    │put: Operation?          │
│bigInteger│──┼──│bigDecimal│                           │    │read: Operation?         │
└──────────┘  │  └──────────┘                           └────│update: Operation?       │
┌──────────┐  │  ┌──────────┐                                │delete: Operation?       │
│byte      │──┼──│short     │                                │list: : Operation?       │
└──────────┘  │  └──────────┘                                │operations: [Operation]? │
┌──────────┐  │  ┌──────────┐                                │collectionOperations:    │
│integer   │──┴──│long      │                                │    [Operation]?         │
└──────────┘     └──────────┘                                │resources: [Resource]?   │
     △                                                       └─────────────────────────┘
     │
┌──────────┐
│intEnum   │
└──────────┘

1.6.1. Shape types#

Shape types are grouped into three categories:

Simple types

Simple types are types that do not contain nested types or shape references.

Type Description
blob Uninterpreted binary data
boolean Boolean value type
string UTF-8 encoded string
enum A string with a fixed set of values.
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)
intEnum An integer with a fixed set of values.
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 Open content that functions as a kind of "any" type.
Aggregate types

Aggregate types contain configurable member references to others shapes.

Type Description
List Ordered collection of homogeneous 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 one of several different, but fixed, types
Service types

Types that define the organization and operations of a service.

Type Description
service Entry point of an API that aggregates resources and operations together
operation Represents the input, output, and errors of an API operation
resource Entity with an identity that has a set of operations

1.6.2. Member shapes#

Members are defined in shapes to reference other shapes using a shape ID. Members are found in enum, intEnum, list, map, structure, and union shapes. The shape referenced by a member is called its "target". A member MUST NOT target a trait, operation, resource, service, or member.

The following example defines a list that contains a member shape. Further examples can be found in the documentation for shape types.

$version: "2"
namespace smithy.example

list UserNameList {
    member: UserName
}

1.6.3. Shape ID#

A shape ID is used to refer to shapes in the model. All shapes have an assigned shape ID.

The following example defines a shape in the smithy.example namespace named MyString, giving the shape a shape ID of smithy.example#MyString:

$version: "2"
namespace smithy.example

string MyString

Shape IDs have the following syntax:

smithy.example.foo#ExampleShapeName$memberName
└─────────┬──────┘ └───────┬──────┘ └────┬───┘
     (Namespace)     (Shape name)  (Member name)
                   └──────────────┬────────────┘
                         (Relative shape ID)
└──────────────────────┬───────────────────────┘
              (Absolute shape ID)
Absolute shape ID
An absolute shape ID starts with a namespace, followed by "#", followed by a relative shape ID. For example, smithy.example#Foo and smithy.example#Foo$bar are absolute shape IDs.
Relative shape ID
A relative shape ID contains a shape name and an optional member name. The shape name and member name are separated by the "$" symbol if a member name is present. For example, Foo and Foo$bar are relative shape IDs.
Namespace

A namespace is a mechanism for logically grouping shapes in a way that makes them reusable alongside other models without naming conflicts. A semantic model MAY contain shapes defined across multiple namespaces. The IDL representation supports zero or one namespace per model file, while the JSON AST representation supports zero or more namespaces per model file.

Models SHOULD use a single namespace to model a single logical domain. Limiting the number of namespaces used to define a logical grouping of shapes limits the potential for ambiguity if the shapes are used by the same service or need to be referenced within the same model.

Shape name

The name of the shape within a namespace.

Consumers of a Smithy model MAY choose to inflect shape names, structure member names, and other facets of a Smithy model in order to expose a more idiomatic experience to particular programming languages. In order to make this easier for consumers of a model, model authors SHOULD utilize a strict form of PascalCase in which only the first letter of acronyms, abbreviations, and initialisms are capitalized. For example, prefer UserId over UserID, and Arn over ARN.

Root shape ID
A root shape ID is a shape ID that does not contain a member. For example, smithy.example#Foo and Foo are root shape IDs.

1.6.3.1. Shape ID ABNF#

Shape IDs are formally defined by the following ABNF:

ShapeId =
    RootShapeId [ShapeIdMember]

RootShapeId =
    AbsoluteRootShapeId / Identifier

AbsoluteRootShapeId =
    Namespace "#" Identifier

Namespace =
    Identifier *("." Identifier)

Identifier =
    IdentifierStart *IdentifierChars

IdentifierStart =
    (1*"_" (ALPHA / DIGIT)) / ALPHA

IdentifierChars =
    ALPHA / DIGIT / "_"

ShapeIdMember =
    "$" Identifier

1.6.3.2. Shape ID conflicts#

While shape ID references within the semantic model are case-sensitive, no two shapes in the semantic model can have the same case-insensitive shape ID. This restriction makes it easier to use Smithy models for code generation in programming languages that do not support case-sensitive identifiers or that perform some kind of normalization on generated identifiers (for example, a Python code generator might convert all member names to lower snake case). To illustrate, com.Foo#baz and com.foo#BAZ are not allowed in the same semantic model. This restriction also extends to member names: com.foo#Baz$bar and com.foo#Baz$BAR are in conflict.

See also

Merging model files for information on how conflicting shape definitions for the same shape ID are handled when assembling the semantic model from multiple model files.

1.7. Traits#

Traits are model components that can be attached to shapes to describe additional information about the shape; shapes provide the structure and layout of an API, while traits provide refinement and style.

1.7.1. Applying traits#

An instance of a trait applied to a shape is called an applied trait. Only a single instance of a trait can be applied to a shape. The way in which a trait is applied to a shape depends on the model file representation.

Traits are applied to shapes in the IDL using smithy:TraitStatements that immediately precede a shape. The following example applies the length trait and documentation trait to MyString:

$version: "2"
namespace smithy.example

@length(min: 1, max: 100)
@documentation("Contains a string")
string MyString

1.7.1.1. Scope of member traits#

Traits that target members apply only in the context of the member shape and do not affect the shape targeted by the member. Traits applied to a member supersede traits applied to the shape targeted by the member and do not inherently conflict.

In the following example, the range trait applied to numberOfItems takes precedence over the trait applied to PositiveInteger.

structure ShoppingCart {
    // This trait supersedes the PositiveInteger trait.
    @range(min: 7, max:12)
    numberOfItems: PositiveInteger
}

@range(min: 1)
integer PositiveInteger

1.7.1.2. Applying traits externally#

Both the IDL and JSON AST model representations allow traits to be applied to shapes outside of a shape's definition. This is done using an apply statement in the IDL, or the apply type in the JSON AST. For example, this can be useful to allow different teams within the same organization to independently own different facets of a model; a service team could own the model that defines the shapes and traits of the API, and a documentation team could own a model that applies documentation traits to the shapes.

The following example applies the documentation trait and length trait to the smithy.example#MyString shape:

$version: "2"
namespace smithy.example

apply MyString @documentation("This is my string!")
apply MyString @length(min: 1, max: 10)

Note

In the semantic model, applying traits outside of a shape definition is treated exactly the same as applying the trait inside of a shape definition.

1.7.1.3. Trait conflict resolution#

Trait conflict resolution is used when the same trait is applied multiple times to a shape. Duplicate traits applied to shapes are allowed in the following cases:

  1. If the trait is a list or set shape, then the conflicting trait values are concatenated into a single trait value.
  2. If both values are exactly equal, then the conflict is ignored.

All other instances of trait collisions are prohibited.

The following model definition is valid because the length trait is duplicated on the MyList shape with the same values:

$version: "2"
namespace smithy.example

@length(min: 0, max: 10)
list MyList {
    member: String
}

apply MyList @length(min: 0, max: 10)

The following model definition is valid because the tags trait is a list. The resulting value assigned to the tags trait on the Hello shape is a list that contains "a", "b", and "c".

$version: "2"
namespace smithy.example

@tags(["a", "b"])
string Hello

apply Hello @tags(["c"])

The following model definition is invalid because the length trait is duplicated on the MyList shape with different values:

$version: "2"
namespace smithy.example

@length(min: 0, max: 10)
list MyList {
    member: String
}

apply MyList @length(min: 10, max: 20)

1.7.1.4. Trait node values#

The value provided for a trait MUST be compatible with the shape of the trait. The following table defines each shape type that is available to target from traits and how their values are defined in node values.

Smithy type Node type Description
blob string A string value that is base64 encoded.
boolean boolean Can be set to true or false.
byte number The value MUST fall within the range of -128 to 127
short number The value MUST fall within the range of -32,768 to 32,767
integer number The value MUST fall within the range of -2^31 to (2^31)-1.
long number The value MUST fall within the range of -2^63 to (2^63)-1.
float string | number The value MUST be either a normal JSON number or one of the following string values: "NaN", "Infinity", "-Infinity".
double string | number The value MUST be either a normal JSON number or one of the following string values: "NaN", "Infinity", "-Infinity".
bigDecimal string | number bigDecimal values can be serialized as strings to avoid rounding issues when parsing a Smithy model in various languages.
bigInteger string | number bigInteger values can be serialized as strings to avoid truncation issues when parsing a Smithy model in various languages.
string string The provided value SHOULD be compatible with the mediaType of the string shape if present; however, this is not validated by Smithy.
timestamp number | string If a number is provided, it represents Unix epoch seconds with optional millisecond precision. If a string is provided, it MUST be a valid RFC 3339 string with optional fractional precision but no UTC offset (for example, 1985-04-12T23:20:50.52Z).
list array Each value in the array MUST be compatible with the targeted member.
map object Each key MUST be compatible with the key member of the map, and each value MUST be compatible with the value member of the map.
structure object All members marked as required MUST be provided in a corresponding key-value pair. Each key MUST correspond to a single member name of the structure. Each value MUST be compatible with the member that corresponds to the member name.
union object The object MUST contain a single key-value pair. The key MUST be one of the member names of the union shape, and the value MUST be compatible with the corresponding shape.

Important

Trait values MUST be compatible with the required trait and any associated constraint traits.

1.7.2. Defining traits#

Traits are defined by applying smithy.api#trait to a shape. This trait can only be applied to simple types and aggregate types. By convention, trait shape names SHOULD use a lowercase name so that they visually stand out from normal shapes.

The following example defines a trait with a shape ID of smithy.example#myTraitName and applies it to smithy.example#MyString:

$version: "2"
namespace smithy.example

@trait(selector: "*")
structure myTraitName {}

@myTraitName
string MyString

The following example defines two custom traits: beta and structuredTrait:

$version: "2"
namespace smithy.example

/// A trait that can be applied to a member.
@trait(selector: "structure > member")
structure beta {}

/// A trait that has members.
@trait(selector: "string", conflicts: [beta])
structure structuredTrait {
    @required
    lorem: StringShape

    @required
    ipsum: StringShape

    dolor: StringShape
}

// Apply the "beta" trait to the "foo" member.
structure MyShape {
    @required
    @beta
    foo: StringShape
}

// Apply the structuredTrait to the string.
@structuredTrait(
    lorem: "This is a custom trait!"
    ipsum: "lorem and ipsum are both required values.")
string StringShape

1.7.2.1. Prelude traits#

When using the IDL, built-in traits defined in the Smithy prelude namespace, smithy.api, are automatically available in every Smithy model and namespace through relative shape IDs.

1.7.2.2. References to traits#

The only valid reference to a trait is through applying a trait to a shape. Members and references within a model MUST NOT target shapes.

1.7.2.3. trait trait#

Summary
Marks a shape as a trait.
Trait selector

:is(simpleType, list, map, set, structure, union)

This trait can only be applied to simple types, list, map, set, structure, and union shapes.

Value type
structure
1.7.2.3.1. Trait properties#

smithy.api#trait is a structure that supports the following members:

Property Type Description
selector string A valid selector that defines where the trait can be applied. For example, a selector set to :test(list, map) means that the trait can be applied to a list or map shape. This value defaults to * if not set, meaning the trait can be applied to any shape.
conflicts [string] Defines the shape IDs of traits that MUST NOT be applied to the same shape as the trait being defined. This allows traits to be defined as mutually exclusive. Provided shape IDs MAY target unknown traits that are not defined in the model.
structurallyExclusive string One of "member" or "target". When set to "member", only a single member of a structure can be marked with the trait. When set to "target", only a single member of a structure can target a shape marked with this trait.
breakingChanges [BreakingChangeRule] Defines the backward compatibility rules of the trait.

1.7.2.4. Annotation traits#

A structure trait with no members is called an annotation trait. It's hard to predict what information a trait needs to capture when modeling a domain; a trait might start out as a simple annotation, but later might benefit from additional information. By defining an annotation trait rather than a boolean trait, the trait can safely add optional members over time as needed.

The following example defines an annotation trait named foo:

$version: "2"
namespace smithy.example

@trait
structure foo {}

A member can be safely added to an annotation trait if the member is not marked as required. The applications of the foo trait in the previous example and the following example are all valid even after adding a member to the foo trait:

$version: "2"
namespace smithy.example

@trait
structure foo {
    baz: String
}

@foo(baz: "bar")
string MyString4

1.7.2.5. Breaking change rules#

Backward compatibility rules of a trait can be defined in the breakingChanges member of a trait definition. This member is a list of diff rules. Smithy tooling that performs semantic diff analysis between two versions of the same model can use these rules to detect breaking or risky changes.

Note

Not every kind of breaking change can be described using the breakingChanges property. Such backward compatibility rules SHOULD instead be described through documentation and ideally enforced through custom diff tooling.

Property Type Description
change string

Required. The type of change. This value can be set to one of the following:

  • add: The trait or value at the given path was added.
  • remove: The trait or value at the given path was removed.
  • update: The trait or value at the given path was changed.
  • any: The trait or value at the given path was added, removed, or changed.
  • presence: The trait or value at the given path was either added or removed.
path string A JSON pointer as described in RFC 6901 that points to the values to compare from the original model to the updated model. If omitted or if an empty string is provided (""), the entire trait is used as the value for comparison. The provided pointer MUST correctly correspond to shapes in the model.
severity string

Defines the severity of the change. This value can be set to:

  • ERROR: The change is backward incompatible. This is the default assumed severity.
  • DANGER: The change is very likely backward incompatible.
  • WARNING: The change might be backward incompatible.
  • NOTE: The change is likely ok, but should be noted during things like code reviews.
message string Provides an optional plain text message that provides information about why the detected change could be problematic.

It is a backward incompatible change to add the following trait to an existing shape:

@trait(breakingChanges: [{change: "add"}])
structure cannotAdd {}

Note

The above trait definition is equivalent to the following:

@trait(
    breakingChanges: [
        {
            change: "add",
            path: "",
            severity: "ERROR"
        }
    ]
)
structure cannotAdd {}

It is a backward incompatible change to add or remove the following trait from an existing shape:

@trait(breakingChanges: [{change: "presence"}])
structure cannotToAddOrRemove {}

It is very likely backward incompatible to change the "foo" member of the following trait or to remove the "baz" member:

@trait(
    breakingChanges: [
        {
            change: "update",
            path: "/foo",
            severity: "DANGER"
        },
        {
            change: "remove",
            path: "/baz",
            severity: "DANGER"
        }
    ]
)
structure fooBaz {
    foo: String,
    baz: String
}

So for example, if the following shape:

@fooBaz(foo: "a", baz: "b")
string Example

Is changed to:

@fooBaz(foo: "b")
string Example

Then the change to the foo member from "a" to "b" is backward incompatible, as is the removal of the baz member.

1.7.2.5.1. Referring to list members#

The JSON pointer can path into the members of a list using a member segment.

In the following example, it is a breaking change to change values of lists or sets in instances of the names trait:

@trait(
    breakingChanges: [
        {
            change: "update",
            path: "/names/member"
        }
    ]
)
structure names {
    names: NameList
}

@private
list NameList {
    member: String
}

So for example, if the following shape:

@names(names: ["Han", "Luke"])
string Example

Is changed to:

@names(names: ["Han", "Chewy"])
string Example

Then the change to the second value of the names member is backward incompatible because it changed from Luke to Chewy.

1.7.2.5.2. Referring to map members#

Members of a map shape can be referenced in a JSON pointer using key and value.

The following example defines a trait where it is backward incompatible to remove a key value pair from a map:

@trait(
    breakingChanges: [
        {
            change: "remove",
            path: "/key"
        }
    ]
)
map jobs {
    key: String,
    value: String
}

So for example, if the following shape:

@jobs(Han: "Smuggler", Luke: "Jedi")
string Example

Is changed to:

@jobs(Luke: "Jedi")
string Example

Then the removal of the "Han" entry of the map is flagged as backward incompatible.

The following example detects when values of a map change.

@trait(
    breakingChanges: [
        {
            change: "update",
            path: "/value"
        }
    ]
)
map jobs {
    key: String,
    value: String
}

So for example, if the following shape:

@jobs(Han: "Smuggler", Luke: "Jedi")
string Example

Is changed to:

@jobs(Han: "Smuggler", Luke: "Ghost")
string Example

Then the change to Luke's mapping from "Jedi" to "Ghost" is backward incompatible.

Note

  • Using the "update" change type with a map key has no effect.
  • Using any change type other than "update" with map values has no effect.

1.8. Prelude#

All Smithy models automatically include a prelude. The prelude defines various simple shapes and every trait defined in the core specification. When using the IDL, shapes defined in the prelude that are not marked with the private trait can be referenced from within any namespace using a relative shape ID.

Smithy Prelude#
// This model defines the Smithy prelude model.
//
// Shapes in the prelude model that are not marked as `@private` can be
// referenced using a relative shape ID.
$version: "2.0"

namespace smithy.api

// --- The following shapes are publicly available prelude shapes.

string String

blob Blob

bigInteger BigInteger

bigDecimal BigDecimal

timestamp Timestamp

document Document

boolean Boolean

byte Byte

short Short

integer Integer

long Long

float Float

double Double

@default(false)
boolean PrimitiveBoolean

@default(0)
byte PrimitiveByte

@default(0)
short PrimitiveShort

@default(0)
integer PrimitiveInteger

@default(0)
long PrimitiveLong

@default(0)
float PrimitiveFloat

@default(0)
double PrimitiveDouble

// The `Unit` type is a publicly available "unit" value type. This shape
// is the only shape in Smithy allowed to be marked with the `@unitType` trait.
@unitType
structure Unit {}

// --- Shapes below are traits and the private shapes that define them.

/// Makes a shape a trait.
@trait(
    selector: ":is(simpleType, list, map, structure, union)"
    breakingChanges: [
        {change: "presence"}
        {path: "/structurallyExclusive", change: "any"}
        {
            path: "/conflicts"
            change: "update"
            severity: "NOTE"
            message: "Adding more conflicts to a trait could cause previously written models to fail validation."
        }
    ]
)
structure trait {
    /// The valid places in a model that the trait can be applied.
    selector: String

    /// Whether or not only a single member in a shape can have this trait.
    structurallyExclusive: StructurallyExclusive

    /// The traits that this trait conflicts with.
    conflicts: NonEmptyStringList

    /// Defines the backward compatibility rules of the trait.
    breakingChanges: TraitDiffRules
}

@private
@length(min: 1)
list TraitDiffRules {
    member: TraitDiffRule
}

@private
structure TraitDiffRule {
    /// Defines a JSON Pointer to the value to evaluate.
    path: String

    /// Defines the type of change that is not allowed.
    @required
    change: TraitChangeType

    /// Defines the severity of the change. Defaults to ERROR if not defined.
    severity: Severity = "ERROR"

    /// Provides a reason why the change is potentially backward incompatible.
    message: String
}

@private
enum TraitChangeType {
    /// Emit when a trait is modified.
    UPDATE = "update"

    /// Emit when a trait or value is added that previously did not exist.
    ADD = "add"

    /// Emit when a trait or value is removed.
    REMOVE = "remove"

    /// Emit when a trait is added or removed.
    PRESENCE = "presence"

    /// Emit when any change occurs.
    ANY = "any"
}

@private
enum Severity {
    /// A minor infraction occurred.
    NOTE

    /// An infraction occurred that needs attention.
    WARNING

    /// An infraction occurred that must be resolved.
    DANGER

    /// An unrecoverable infraction occurred.
    ERROR
}

@private
enum StructurallyExclusive {
    /// Only a single member of a shape can be marked with the trait.
    MEMBER = "member"

    /// Only a single member of a shape can target a shape marked with
    /// this trait.
    TARGET = "target"
}

/// Marks a shape or member as deprecated.
@trait
structure deprecated {
    /// The reason for deprecation.
    message: String

    /// A description of when the shape was deprecated (e.g., a date or
    /// version).
    since: String
}

/// Used only in Smithy 1.0 to indicate that a shape is boxed.
///
/// This trait cannot be used in Smithy 2.0 models. When a boxed shape is the
/// target of a member, the member may or may not contain a value, and the
/// member has no default value.
@trait(
    selector: """
        :test(boolean, byte, short, integer, long, float, double,
        member > :test(boolean, byte, short, integer, long, float, double))"""
)
structure box {}

/// Adds documentation to a shape or member using CommonMark syntax.
@trait
string documentation

/// Provides a link to additional documentation.
@trait
@length(min: 1)
map externalDocumentation {
    key: NonEmptyString
    value: NonEmptyString
}

/// Defines the ordered list of supported authentication schemes.
@trait(selector: ":is(service, operation)")
@uniqueItems
list auth {
    member: AuthTraitReference
}

/// A string that must target an auth trait.
@idRef(selector: "[trait|authDefinition]")
@private
string AuthTraitReference

/// Marks a trait as a protocol defining trait.
///
/// The targeted trait must only be applied to service shapes, must be a
/// structure, and must have the `trait` trait.
@trait(
    selector: "structure[trait|trait]"
    breakingChanges: [
        {change: "presence"}
    ]
)
structure protocolDefinition {
    /// The list of traits that protocol implementations must understand in
    /// order to successfully use the protocol.
    traits: TraitShapeIdList

    /// Set to true if inline documents are not supported by this protocol.
    @deprecated(message: "Use the `@constrainShapes` trait instead")
    noInlineDocumentSupport: Boolean
}

@private
list TraitShapeIdList {
    member: TraitShapeId
}

@private
@idRef(failWhenMissing: true, selector: "[trait|trait]")
string TraitShapeId

/// Marks a trait as an auth scheme defining trait.
///
/// The targeted trait must only be applied to service shapes or operation
/// shapes, must be a structure, and must have the `trait` trait.
@trait(
    selector: "structure[trait|trait]"
    breakingChanges: [
        {change: "presence"}
    ]
)
structure authDefinition {
    /// The list of traits that auth implementations must understand in order
    /// to successfully use the scheme.
    traits: TraitShapeIdList
}

/// HTTP Basic Authentication as defined in [RFC 2617](https://tools.ietf.org/html/rfc2617.html).
@trait(
    selector: "service"
    breakingChanges: [
        {change: "remove"}
    ]
)
@authDefinition
@externalDocumentation("RFC 2617": "https://tools.ietf.org/html/rfc2617.html")
structure httpBasicAuth {}

/// HTTP Digest Authentication as defined in [RFC 2617](https://tools.ietf.org/html/rfc2617.html).
@trait(
    selector: "service"
    breakingChanges: [
        {change: "remove"}
    ]
)
@authDefinition
@externalDocumentation("RFC 2617": "https://tools.ietf.org/html/rfc2617.html")
structure httpDigestAuth {}

/// HTTP Bearer Authentication as defined in [RFC 6750](https://tools.ietf.org/html/rfc6750.html).
@trait(
    selector: "service"
    breakingChanges: [
        {change: "remove"}
    ]
)
@authDefinition
@externalDocumentation("RFC 6750": "https://tools.ietf.org/html/rfc6750.html")
structure httpBearerAuth {}

/// An HTTP-specific authentication scheme that sends an arbitrary API key in
/// a header or query string parameter.
@trait(
    selector: "service"
    breakingChanges: [
        {change: "remove"}
    ]
)
@authDefinition
structure httpApiKeyAuth {
    /// Defines the name of the HTTP header or query string parameter that
    /// contains the API key.
    @required
    name: NonEmptyString

    /// Defines the location of where the key is serialized. This value can
    /// be set to `"header"` or `"query"`.
    @required
    in: HttpApiKeyLocations

    /// Defines the security scheme to use in the ``Authorization`` header.
    /// This can only be set if the "in" property is set to ``header``.
    scheme: NonEmptyString
}

/// A meta-trait used to apply validation to a specific shape in the model that a trait is applied to.
///
/// `traitValidators` is a map of validation event IDs to validators to apply to a shape.
/// Selectors are used to identify shapes that are incompatible with a constrained trait.
/// The shape with the targeted trait applied MUST be a valid entry point for the selector.
///
/// The following example defines a protocol that does not support document types. Each matching member found in the
/// closure of an attached shape emits a validation event:
///
/// ```
/// @trait(selector: "service")
/// @traitValidators(
///     "myCustomProtocol.NoDocuments": {
///         selector: "~> member :test(> document)"
///         message: "This protocol does not support document types"
///     }
/// )
/// @protocolDefinition
/// structure myCustomProtocol {}
/// ```
@trait(selector: "[trait|trait]")
map traitValidators {
    /// The validation event ID to emit when the constraint finds an incompatible shape.
    @length(min: 1)
    key: String

    /// The validator to apply.
    value: TraitValidator
}

@private
structure TraitValidator {
    /// A Smithy selector that receives only the shape to which the `traitValidators` trait is applied.
    /// Any shape yielded by the selector is considered incompatible with the trait.
    @required
    selector: String

    /// A message to use when a matching shape is found.
    message: String

    /// The severity to use when a matching shape is found.
    severity: Severity = "ERROR"
}

/// Provides a structure member with a default value. When added to root
/// level shapes, requires that every targeting structure member defines the
/// same default value on the member or sets a default of null.
///
/// This trait can currently only be used in Smithy 2.0 models.
@trait(selector: ":is(simpleType, list, map, structure > member :test(> :is(simpleType, list, map)))")
document default

/// Indicates that the default trait was added to a member.
@trait(
    selector: "structure > member [trait|default]"
    breakingChanges: [
        {change: "remove"}
    ]
)
structure addedDefault {}

/// Requires that non-authoritative generators like clients treat a structure
/// member as nullable regardless of if the member is also marked with the
/// required trait.
@trait(selector: "structure > member")
structure clientOptional {}

@private
enum HttpApiKeyLocations {
    HEADER = "header"
    QUERY = "query"
}

/// Indicates that an operation can be called without authentication.
@trait(
    selector: "operation"
    breakingChanges: [
        {change: "remove"}
    ]
)
structure optionalAuth {}

/// Provides example inputs and outputs for operations.
@trait(selector: "operation")
list examples {
    member: Example
}

@private
structure Example {
    @required
    title: String

    documentation: String

    input: Document

    output: Document

    error: ExampleError

    allowConstraintErrors: Boolean
}

@private
structure ExampleError {
    @idRef(selector: "structure[trait|error]")
    shapeId: String

    content: Document
}

/// Indicates that a structure shape represents an error.
///
/// All shapes referenced by the errors list of an operation MUST be targeted
/// with this trait.
@trait(
    selector: "structure"
    conflicts: [trait]
    breakingChanges: [
        {change: "any"}
    ]
)
enum error {
    CLIENT = "client"
    SERVER = "server"
}

/// Indicates that an error MAY be retried by the client.
@trait(
    selector: "structure[trait|error]"
    breakingChanges: [
        {change: "remove"}
    ]
)
structure retryable {
    /// Classifies the retry as throttling.
    throttling: Boolean
}

/// Indicates that an operation is effectively read-only.
@trait(
    selector: "operation"
    conflicts: [idempotent]
    breakingChanges: [
        {change: "remove"}
    ]
)
structure readonly {}

/// Indicates that the intended effect on the server of multiple identical
/// requests with an operation is the same as the effect for a single such
/// request.
@trait(
    selector: "operation"
    conflicts: [readonly]
    breakingChanges: [
        {change: "remove"}
    ]
)
structure idempotent {}

/// Defines the input member of an operation that is used by the server to
/// identify and discard replayed requests.
@trait(
    selector: "structure > :test(member > string)"
    structurallyExclusive: "member"
    breakingChanges: [
        {change: "remove"}
    ]
)
@notProperty
structure idempotencyToken {}

/// Shapes marked with the internal trait are meant only for internal use and
/// must not be exposed to customers.
@trait(
    breakingChanges: [
        {
            change: "remove"
            severity: "WARNING"
            message: "Removing the @internal trait makes a shape externally visible."
        }
    ]
)
structure internal {}

/// Allows a serialized object property name to differ from a structure member
/// name used in the model.
@trait(
    selector: ":is(structure, union) > member"
    breakingChanges: [
        {change: "any"}
    ]
)
string jsonName

/// Serializes an object property as an XML attribute rather than a nested XML
/// element.
@trait(
    selector: "structure > :test(member > :test(boolean, number, string, timestamp))"
    conflicts: [xmlNamespace]
    breakingChanges: [
        {change: "any"}
    ]
)
structure xmlAttribute {}

/// Unwraps the values of a list, set, or map into the containing
/// structure/union.
@trait(
    selector: ":is(structure, union) > :test(member > :test(list, map))"
    breakingChanges: [
        {change: "any"}
    ]
)
structure xmlFlattened {}

/// Changes the serialized element or attribute name of a structure, union,
/// or member.
@trait(
    selector: ":is(structure, union, member)"
    breakingChanges: [
        {change: "any"}
    ]
)
@pattern("^[a-zA-Z_][a-zA-Z_0-9-]*(:[a-zA-Z_][a-zA-Z_0-9-]*)?$")
string xmlName

/// Adds an xmlns namespace definition URI to an XML element.
@trait(
    selector: ":is(service, member, simpleType, list, map, structure, union)"
    conflicts: [xmlAttribute]
    breakingChanges: [
        {change: "any"}
    ]
)
structure xmlNamespace {
    /// The namespace URI for scoping this XML element.
    @required
    uri: NonEmptyString

    /// The prefix for the given namespace.
    @pattern("^[a-zA-Z_][a-zA-Z_0-9-]*$")
    prefix: NonEmptyString
}

@private
@length(min: 1)
string NonEmptyString

/// Indicates that the put lifecycle operation of a resource can only be used
/// to create a resource and cannot replace an existing resource.
@trait(selector: "resource:test(-[put]->)")
structure noReplace {}

/// Describes the contents of a blob shape using a media type as defined by
/// RFC 6838 (e.g., "video/quicktime").
@trait(
    selector: ":is(blob, string)"
    breakingChanges: [
        {change: "remove"}
    ]
)
string mediaType

/// Defines the resource shapes that are referenced by a string shape or a
/// structure shape and the members of the structure that provide values for
/// the identifiers of the resource.
@trait(selector: ":is(structure, string)")
list references {
    member: Reference
}

@private
structure Reference {
    /// The shape ID of the referenced resource.
    @required
    resource: NonEmptyString

    /// Defines a mapping of each resource identifier name to a structure member
    /// name that provides its value. Each key in the map MUST refer to one of the
    /// identifier names in the identifiers property of the resource, and each
    /// value in the map MUST refer to a valid structure member name that targets
    /// a string shape.
    ids: NonEmptyStringMap

    /// Providing a service makes the reference specific to a particular binding
    /// of the resource to a service. When omitted, the reference is late-bound to
    /// a service, meaning the reference is assumed to be a reference to the
    /// resource bound to the service currently in use by the client or server.
    service: NonEmptyString

    /// Defines the semantics of the relationship. The rel property SHOULD
    /// contain a link relation as defined in RFC 5988#section-4.
    rel: NonEmptyString
}

@private
map NonEmptyStringMap {
    key: NonEmptyString
    value: NonEmptyString
}

/// Indicates that the targeted structure member provides an identifier for
/// a resource.
@trait(
    selector: "structure > :test(member[trait|required] > string)"
    breakingChanges: [
        {change: "remove"}
    ]
)
@length(min: 1)
@notProperty
string resourceIdentifier

/// Prevents models defined in a different namespace from referencing the
/// targeted shape.
@trait
structure private {}

/// Indicates that the data stored in the shape is sensitive and MUST be
/// handled with care.
@trait(selector: ":not(:test(service, operation, resource, member))")
structure sensitive {}

/// Defines the version or date in which a shape or member was added to the
/// model.
@trait
string since

/// Indicates that the data stored in the shape is very large and should not
/// be stored in memory, or that the size of the data stored in the shape is
/// unknown at the start of a request. If the target is a union then the shape
/// represents a stream of events.
@trait(
    selector: ":is(blob, union)"
    structurallyExclusive: "target"
    breakingChanges: [
        {change: "any"}
    ]
)
structure streaming {}

/// Indicates that the streaming blob must be finite and has a known size.
@trait(
    selector: "blob[trait|streaming]"
    breakingChanges: [
        {change: "presence"}
    ]
)
structure requiresLength {}

/// Tags a shape with arbitrary tag names that can be used to filter and
/// group shapes in the model.
@trait
list tags {
    member: String
}

/// Defines a proper name for a service or resource shape.
///
/// This title can be used in automatically generated documentation
/// and other contexts to provide a user friendly name for services
/// and resources.
@trait(selector: ":is(service, resource)")
string title

/// Constrains the acceptable values of a string to a fixed set
/// of constant values.
@trait(
    selector: "string :not(enum)"
    // It's a breaking change to change values or enums or the ordering of
    // enums, but that validation happens in code to provide better error
    // messages.
    breakingChanges: [
        {change: "presence"}
    ]
)
@length(min: 1)
@deprecated(message: "The enum trait is replaced by the enum shape in Smithy 2.0", since: "2.0")
list enum {
    member: EnumDefinition
}

/// An enum definition for the enum trait.
@private
structure EnumDefinition {
    /// Defines the enum value that is sent over the wire.
    @required
    value: NonEmptyString

    /// Defines the name that is used in code to represent this variant.
    name: EnumConstantBodyName

    /// Provides optional documentation about the enum constant value.
    documentation: String

    /// Applies a list of tags to the enum constant.
    tags: NonEmptyStringList

    /// Whether the enum value should be considered deprecated.
    deprecated: Boolean
}

/// The optional name or label of the enum constant value.
///
/// This property is used in code generation to provide a label for
/// each enum value. No two enums can have the same 'name' value.
@private
@pattern("^[a-zA-Z_]+[a-zA-Z_0-9]*$")
string EnumConstantBodyName

/// Defines the value of an enum member.
@trait(selector: ":is(enum, intEnum) > member")
@tags(["diff.error.const"])
document enumValue

/// Constrains a shape to minimum and maximum number of elements or size.
@trait(selector: ":test(list, map, string, blob, member > :is(list, map, string, blob))")
structure length {
    /// Integer value that represents the minimum inclusive length of a shape.
    min: Long

    /// Integer value that represents the maximum inclusive length of a shape.
    max: Long
}

/// Restricts allowed values of byte, short, integer, long, float, double,
/// bigDecimal, and bigInteger shapes within an acceptable lower and upper
/// bound.
@trait(selector: ":test(number, member > number)")
structure range {
    /// Specifies the allowed inclusive minimum value.
    min: BigDecimal

    /// Specifies the allowed inclusive maximum value.
    max: BigDecimal
}

/// Restricts string shape values to a specified regular expression.
@trait(
    selector: ":test(string, member > string)"
    breakingChanges: [
        {
            change: "add"
            severity: "WARNING"
            message: "The @pattern trait should only be added if the string already had adhered to the pattern."
        }
        {
            change: "update"
            severity: "NOTE"
            message: "Changes to the @pattern trait should generally make the string more permissive, not less."
        }
    ]
)
string pattern

/// Marks a structure member as required, meaning a value for the member MUST
/// be present.
@trait(
    selector: "structure > member"
    breakingChanges: [
        {
            change: "add"
            severity: "WARNING"
            message: "If any consumers were previously omitting this member in operation inputs, making it required is backwards incompatible"
        }
    ]
)
structure required {}

/// Configures a structure member's resource property mapping behavior.
@trait(
    selector: "structure > member"
    conflicts: [resourceIdentifier]
    breakingChanges: [
        {change: "remove"}
        {change: "update"}
    ]
)
structure property {
    name: String
}

/// Explicitly excludes a member from resource property mapping or enables
/// another trait to carry the same implied meaning.
@trait(
    selector: ":is(operation -[input, output]-> structure > member, [trait|trait])"
    breakingChanges: [
        {change: "add"}
    ]
)
@notProperty
structure notProperty {}

/// Adjusts the resource property mapping of a lifecycle operation to the
/// targeted member.
@trait(
    selector: "operation -[input, output]-> structure > member :test(> structure)"
    structurallyExclusive: "member"
    breakingChanges: [
        {change: "any"}
    ]
)
@notProperty
structure nestedProperties {}

/// Indicates that a structure member SHOULD be set.
@trait(
    selector: "structure > member"
    conflicts: [required]
)
structure recommended {
    /// Provides a reason why the member is recommended.
    reason: String
}

/// Marks a list or map as sparse.
@trait(
    selector: ":is(list, map)"
    breakingChanges: [
        {change: "presence"}
    ]
)
structure sparse {}

/// Indicates that the items in a list MUST be unique.
@trait(
    selector: "list :not(> member ~> :is(float, double, document))"
    conflicts: [sparse]
    breakingChanges: [
        {change: "presence", severity: "WARNING"}
    ]
)
structure uniqueItems {}

/// Indicates that the shape is unstable and could change in the future.
@trait
structure unstable {}

/// The paginated trait indicates that an operation intentionally limits the
/// number of results returned in a single response and that multiple
/// invocations might be necessary to retrieve all results.
@trait(
    selector: ":is(service, operation)"
    breakingChanges: [
        {change: "remove"}
        {path: "/inputToken", change: "update"}
        {path: "/outputToken", change: "update"}
        {path: "/items", change: "remove"}
        {path: "/items", change: "add", severity: "NOTE"}
        {path: "/items", change: "update", severity: "NOTE"}
        {path: "/pageSize", change: "update"}
        {path: "/pageSize", change: "remove"}
    ]
)
structure paginated {
    /// The name of the operation input member that represents the continuation
    /// token.
    ///
    /// When this value is provided as operation input, the service returns
    /// results from where the previous response left off. This input member
    /// MUST NOT be required and MUST target a string shape.
    inputToken: NonEmptyString

    /// The name of the operation output member that represents the
    /// continuation token.
    ///
    /// When this value is present in operation output, it indicates that there
    /// are more results to retrieve. To get the next page of results, the
    /// client uses the output token as the input token of the next request.
    /// This output member MUST NOT be required and MUST target a string shape.
    outputToken: NonEmptyString

    /// The name of a top-level output member of the operation that is the data
    /// that is being paginated across many responses.
    ///
    /// The named output member, if specified, MUST target a list or map.
    items: NonEmptyString

    /// The name of an operation input member that limits the maximum number of
    /// results to include in the operation output. This input member MUST NOT
    /// be required and MUST target an integer shape.
    pageSize: NonEmptyString
}

/// Configures the HTTP bindings of an operation.
@trait(
    selector: "operation"
    breakingChanges: [
        {change: "remove"}
        {path: "/method", change: "update"}
        {path: "/uri", change: "update"}
        {path: "/code", change: "update"}
        {
            path: "/code"
            change: "presence"
            severity: "DANGER"
            message: "Adding or removing is backward compatible only if the value is the default value of 200"
        }
    ]
)
structure http {
    /// The HTTP method of the operation.
    @required
    method: NonEmptyString

    /// The URI pattern of the operation.
    ///
    /// Labels defined in the URI pattern are used to bind operation input
    /// members to the URI.
    @required
    uri: NonEmptyString

    // The HTTP status code of a successful response.
    @range(min: 100, max: 999)
    code: Integer = 200
}

/// Binds an operation input structure member to an HTTP label.
@trait(
    selector: "structure > member[trait|required] :test(> :test(string, number, boolean, timestamp))"
    conflicts: [httpHeader, httpQuery, httpPrefixHeaders, httpPayload, httpResponseCode, httpQueryParams]
    breakingChanges: [
        {change: "presence"}
    ]
)
structure httpLabel {}

/// Binds an operation input structure member to a query string parameter.
@trait(
    selector: """
        structure > member
        :test(> :test(string, number, boolean, timestamp),
        > list > member > :test(string, number, boolean, timestamp))"""
    conflicts: [httpLabel, httpHeader, httpPrefixHeaders, httpPayload, httpResponseCode, httpQueryParams]
    breakingChanges: [
        {change: "any"}
    ]
)
@length(min: 1)
string httpQuery

/// Binds an operation input structure member to the HTTP query string.
@trait(
    selector: """
        structure > member
        :test(> map > member[id|member=value] > :test(string, list > member > string))"""
    structurallyExclusive: "member"
    conflicts: [httpLabel, httpQuery, httpHeader, httpPayload, httpResponseCode, httpPrefixHeaders]
    breakingChanges: [
        {change: "any"}
    ]
)
structure httpQueryParams {}

/// Binds a structure member to an HTTP header.
@trait(
    selector: """
        structure > :test(member > :test(boolean, number, string, timestamp,
        list > member > :test(boolean, number, string, timestamp)))"""
    conflicts: [httpLabel, httpQuery, httpPrefixHeaders, httpPayload, httpResponseCode, httpQueryParams]
    breakingChanges: [
        {change: "any"}
    ]
)
@length(min: 1)
string httpHeader

/// Binds a map of key-value pairs to prefixed HTTP headers.
@trait(
    selector: """
        structure > member
        :test(> map :not([trait|sparse]) > member[id|member=value] > string)"""
    structurallyExclusive: "member"
    conflicts: [httpLabel, httpQuery, httpHeader, httpPayload, httpResponseCode, httpQueryParams]
    breakingChanges: [
        {change: "any"}
    ]
)
string httpPrefixHeaders

/// Binds a single structure member to the body of an HTTP request.
@trait(
    selector: "structure > member"
    conflicts: [httpLabel, httpQuery, httpHeader, httpPrefixHeaders, httpResponseCode, httpQueryParams]
    structurallyExclusive: "member"
    breakingChanges: [
        {change: "presence"}
    ]
)
structure httpPayload {}

/// Defines an HTTP response code for an operation error.
@trait(
    selector: "structure[trait|error]"
    breakingChanges: [
        {change: "any"}
    ]
)
integer httpError

/// Indicates that the structure member represents the HTTP response
/// status code. The value MAY differ from the HTTP status code provided
/// on the response.
@trait(
    selector: "structure :not([trait|input]) > member :test(> integer)"
    structurallyExclusive: "member"
    conflicts: [httpLabel, httpQuery, httpHeader, httpPrefixHeaders, httpPayload, httpQueryParams]
    breakingChanges: [
        {change: "any"}
    ]
)
structure httpResponseCode {}

/// Defines how a service supports cross-origin resource sharing.
@trait(
    selector: "service"
    breakingChanges: [
        {change: "remove"}
    ]
)
structure cors {
    /// The origin from which browser script-originating requests will be
    /// allowed.
    origin: NonEmptyString = "*"

    /// The maximum number of seconds for which browsers are allowed to cache
    /// the results of a preflight OPTIONS request.
    ///
    /// Defaults to 600, the maximum age permitted by several browsers.
    /// Set to -1 to disable caching entirely.
    maxAge: Integer = 600

    /// The names of headers that should be included in the
    /// Access-Control-Allow-Headers header in responses to preflight OPTIONS
    /// requests. This list will be used in addition to the names of all
    /// request headers bound to an input data member via the httpHeader, as
    /// well as any headers required by the protocol or authentication scheme.
    additionalAllowedHeaders: NonEmptyStringList

    /// The names of headers that should be included in the
    /// Access-Control-Expose-Headers header in all responses sent by the
    /// service. This list will be used in addition to the names of all
    /// request headers bound to an output data member via the httpHeader,
    /// as well as any headers required by the protocol or authentication
    /// scheme.
    additionalExposedHeaders: NonEmptyStringList
}

@private
list NonEmptyStringList {
    member: NonEmptyString
}

/// Marks a member as the payload of an event.
@trait(
    selector: "structure > :test(member > :test(blob, string, structure, union))"
    conflicts: [eventHeader]
    structurallyExclusive: "member"
    breakingChanges: [
        {change: "any"}
    ]
)
structure eventPayload {}

/// Marks a member as a header of an event.
@trait(
    selector: """
        structure >
        :test(member > :test(boolean, byte, short, integer, long, blob, string, timestamp))"""
    conflicts: [eventPayload]
    breakingChanges: [
        {change: "any"}
    ]
)
structure eventHeader {}

/// Indicates that a string value MUST contain a valid shape ID.
///
/// The provided shape ID MAY be absolute or relative to the shape to which
/// the trait is applied. A relative shape ID that does not resolve to a
/// shape defined in the same namespace resolves to a shape defined in the
/// prelude if the prelude shape is not marked with the private trait.
@trait(selector: ":test(string, member > string)")
structure idRef {
    /// Defines the selector that the resolved shape, if found, MUST match.
    selector: String = "*"

    /// When set to `true`, the shape ID MUST target a shape that can be
    /// found in the model.
    failWhenMissing: Boolean

    /// Defines a custom error message to use when the shape ID cannot be
    /// found or does not match the selector.
    ///
    /// A default message is generated when errorMessage is not defined.
    errorMessage: String
}

@trait(
    selector: ":test(timestamp, member > timestamp)"
    breakingChanges: [
        {change: "any"}
    ]
)
enum timestampFormat {
    /// Date time as defined by the date-time production in RFC3339 section 5.6
    /// with no UTC offset (for example, 1985-04-12T23:20:50.52Z).
    DATE_TIME = "date-time"

    /// Also known as Unix time, the number of seconds that have elapsed since
    /// 00:00:00 Coordinated Universal Time (UTC), Thursday, 1 January 1970,
    /// with decimal precision (for example, 1515531081.1234).
    EPOCH_SECONDS = "epoch-seconds"

    /// An HTTP date as defined by the IMF-fixdate production in
    /// RFC 7231#section-7.1.1.1 (for example, Tue, 29 Apr 2014 18:30:38 GMT).
    HTTP_DATE = "http-date"
}

/// Configures a custom operation endpoint.
@trait(
    selector: "operation"
    breakingChanges: [
        {change: "any"}
    ]
)
structure endpoint {
    /// A host prefix pattern for the operation.
    ///
    /// Labels defined in the host pattern are used to bind top-level
    /// operation input members to the host.
    @required
    hostPrefix: NonEmptyString
}

/// Binds a top-level operation input structure member to a label
/// in the hostPrefix of an endpoint trait.
@trait(
    selector: "structure > :test(member[trait|required] > string)"
    breakingChanges: [
        {change: "any"}
    ]
)
structure hostLabel {}

/// Suppresses validation events by ID for a given shape.
@trait
list suppress {
    @length(min: 1)
    member: String
}

/// Marks an operation as requiring checksum in its HTTP request.
/// By default, the checksum used for a service is a MD5 checksum
/// passed in the Content-MD5 header.
@unstable
@trait(selector: "operation")
structure httpChecksumRequired {}

/// Specializes a structure for use only as the input of a single operation.
@trait(
    selector: "structure"
    conflicts: [output, error]
    breakingChanges: [
        {change: "presence"}
    ]
)
structure input {}

/// Specializes a structure for use only as the output of a single operation.
@trait(
    selector: "structure"
    conflicts: [input, error]
    breakingChanges: [
        {change: "presence"}
    ]
)
structure output {}

/// Specializes a structure as a unit type that has no meaningful value.
/// This trait can only be applied to smithy.api#Unit, which ensures that
/// only a single Unit shape can be created.
@trait(selector: "[id=smithy.api#Unit]")
structure unitType {}

/// Makes a structure or union a mixin.
@trait(selector: ":not(member)")
structure mixin {
    localTraits: LocalMixinTraitList
}

@private
list LocalMixinTraitList {
    member: LocalMixinTrait
}

@idRef(
    selector: "[trait|trait]"
    failWhenMissing: true
    errorMessage: """
        Strings provided to the localTraits property of a mixin trait
        must target a valid trait."""
)
@private
string LocalMixinTrait

/// Defines the priority-ordered list of compression algorithms supported by
/// the service operation.
@private
list RequestCompressionEncodingsList {
    // Encodings are NOT constrained in a Smithy enum since supported encodings
    // are case-insensitive
    member: String
}

/// Indicates that an operation supports compressing requests from clients to
/// services.
@trait(
    selector: "operation"
    breakingChanges: [
        {
            change: "remove"
            severity: "DANGER"
            message: """
                Trait was removed so newly generated clients will no longer compress requests, \
                but the service MUST continue to support removed compression algorithms in `encodings`."""
        }
        {
            change: "remove"
            path: "/encodings"
            message: "`encodings` was removed, but is required for the requestCompression trait."
        }
        {
            change: "add"
            severity: "NOTE"
            path: "/encodings/member"
            message: """
                Members of `encodings` were added. Once a compression algorithm is added, \
                the service MUST support the compression algorithm."""
        }
        {
            change: "remove"
            severity: "DANGER"
            path: "/encodings/member"
            message: """
                Members of `encodings` were removed so newly generated clients will no longer compress requests \
                for removed compression algorithms. The service MUST continue to support old clients by supporting \
                removed compression algorithms."""
        }
        {
            change: "update"
            severity: "DANGER"
            path: "/encodings/member"
            message: """
                Members of `encodings` were updated so newly generated clients will no longer compress requests \
                for compression algorithms prior to the updates. The service MUST continue to support old clients by \
                supporting compression algorithms prior to the updates."""
        }
    ]
)
structure requestCompression {
    @required
    encodings: RequestCompressionEncodingsList
}

Note

Private shapes defined in the prelude are subject to change at any time.

1.8.1. Unit type#

Smithy provides a singular unit type named smithy.api#Unit. The unit type in Smithy is similar to Void and None in other languages. It is used when the input or output of an operation has no meaningful value or if a union member has no meaningful value. smithy.api#Unit MUST NOT be referenced in any other context.

The smithy.api#Unit shape is defined in Smithy's prelude as a structure shape marked with the smithy.api#unitType trait to differentiate it from other structures. It is the only such structure in the model that can be marked with the smithy.api#unitType trait.

See also

Union, Operation