7. Type refinement traits#

Type refinement traits are traits that significantly refine, or change, the type of a shape.

7.1. default trait#

Summary
Provides a structure member with a default value.
Trait selector

:is(simpleType, list, map, structure > member :test(> :is(simpleType, list, map)))

A simple type, list, map, or a member of a structure that targets a simple type, list, or map.

Value type
Document type.
See also

The @default trait assigns a default value to a structure member. The following example defines a structure with a "language" member that has a default value:

structure Message {
    @required
    title: String

    language: Language = "en"
}

enum Language {
    EN = "en"
}

The above example uses syntactic sugar to apply the @default trait. The Message definition is exactly equivalent to:

structure Message {
    @required
    title: String

    @default("en")
    language: Language
}

The @default trait can be added to root-level simple types, lists, or maps. This can serve as a kind of template to enforce default values across structure members in a model. Any structure member that targets a shape marked with @default MUST also add a matching @default trait to the member.

@default(0)
integer ZeroValueInteger

structure Message {
    zeroValueInteger: ZeroValueInteger = 0 // must be repeated and match the target.
}

The @default trait on a structure member can be set to null to explicitly indicate that the member has no default value or to override the default value requirement of a targeted shape.

@default(0)
integer ZeroValueInteger

structure Message {
    zeroValueInteger: ZeroValueInteger = null // forces the member to be optional
}

Note

  • The @default trait on root-level shapes has no impact when targeted by any other shape than a structure member.
  • The @default trait on root-level shapes cannot be set to null.

7.1.1. Default value constraints#

The value of the @default trait MUST be compatible with the shape targeted by the member and any applied constraint traits (for example, values for numeric types MUST be numbers that fit within the targeted type and match any range constraints, string types match any length or pattern traits, etc).

The following shapes have restrictions on their default values:

  • enum: can be set to any valid string value of the enum.
  • intEnum: can be set to any valid integer value of the enum.
  • document: can be set to null, `true, false, string, numbers, an empty list, or an empty map.
  • list: can only be set to an empty list.
  • map: can only be set to an empty map.
  • structure: no default value.
  • union: no default value.

7.1.2. Impact on API design#

The @default trait SHOULD NOT be used for partial updates or patch style operations where it is necessary to differentiate between omitted values and explicitly set values. Assigning default values is typically something that occurs during deserialization, and as such, it is impossible for a server to differentiate between whether a property was set to its default value or if a property was omitted.

7.1.3. Updating default values#

The default value of a root-level shape MUST NOT be changed nor can the default trait be added or removed from an existing root-level shape. Changing the default value of a root-level shape would cause any member reference to the shape to break and could inadvertently impact code generated types for the shape.

The default value of a member SHOULD NOT be changed. However, it MAY be necessary in extreme cases to change a default value if changing the default value addresses a customer-impacting issue or availability issue for a service. Changing default values can result in parties disagreeing on the default value of a member because they are using different versions of the same model.

7.1.4. Default value serialization#

Authoritative model consumers like servers SHOULD always serialize default values to remove any ambiguity about the value of the most up to default value. However, to avoid information disclosure, servers SHOULD NOT serialize default values if the member is marked with the internal trait.

To allow servers to change default values if necessary, clients SHOULD NOT serialize default values unless the member is explicitly set to the default value or marked with the default trait. This implies that clients SHOULD implement a kind of "presence tracking" of defaulted members so that the member is only serialized if it is explicitly set to the default value.

7.1.5. Default and required#

A member that is both @default and @required SHOULD always be serialized, and implementations SHOULD NOT use any form of presence tracking to omit a member if the member is not explicitly set to the default value. It is a protocol-specific decision as to whether this is enforced in serialized messages; some protocols follow this strictly whereas others may not.

7.2. addedDefault trait#

Summary
Indicates that the default trait was added to a structure member after initially publishing the member. This allows tooling to decide whether to ignore the @default trait if it will break backward compatibility in the tool.
Trait selector

structure > member [trait|default]

Member of a structure marked with the default trait

Value type
Annotation trait.
See also

7.3. required trait#

Summary
Marks a structure member as required, meaning a value for the member MUST be present.
Trait selector

structure > member

Member of a structure

Value type
Annotation trait.
See also

The following example defines a structure with a required member.

structure MyStructure {
    @required
    foo: FooString
}

Important

The required trait isn't just for inputs

The required trait indicates that value MUST always be present for a member. It applies to all shapes, including inputs of operations, outputs of operations, and errors.

7.4. clientOptional trait#

Summary
Requires that non-authoritative generators like clients treat a structure member as optional regardless of if the member is also marked with the required trait or default trait.
Trait selector
structure > member
Value type
Annotation trait
See also

For cases when a service is unsure if a member will be required forever, the member can be marked with the @clientOptional trait to ensure that non-authoritative consumers of the model like clients treat the member as optional. The @required trait can be backward compatibly removed from a member marked as @clientOptional (and does not need to be replaced with the @default trait). This causes the @required and @default traits to function only as a server-side concern.

The @required trait on foo in the following structure is considered a validation constraint rather than a type refinement trait:

structure Foo {
    @required
    @clientOptional
    foo: String
}

Note

Structure members in Smithy are automatically considered optional. For example, the following structure:

structure Foo {
    baz: String
}

Is equivalent to the following structure:

structure Foo {
    @clientOptional
    baz: String
}

7.5. enumValue trait#

Summary
Defines the value of an enum or intEnum. For enum shapes, a non-empty string value must be used. For intEnum shapes, an integer value must be used.
Trait selector
:is(enum, intEnum) > member
Value type
string or integer
$version: "2"
namespace smithy.example

enum Enum {
    @enumValue("foo")
    FOO
}

intEnum IntEnum {
    @enumValue(1)
    FOO
}

The following enum definition uses syntactic sugar that is exactly equivalent:

$version: "2"
namespace smithy.example

enum Enum {
    FOO = "foo"
}

intEnum IntEnum {
    FOO = 1
}

7.6. error trait#

Summary
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
Value type
string that MUST be set to "client" or "server" to indicate if the client or server is at fault for the error.
Conflicts with
trait

The following structure defines a throttling error.

@error("client")
structure ThrottlingError {}

Note that this structure is lacking the retryable trait that generically lets clients know that the error is retryable.

@error("client")
@retryable
structure ThrottlingError {}

When using an HTTP-based protocol, it is recommended to add an httpError trait to use an appropriate HTTP status code with the error.

@error("client")
@retryable
@httpError(429)
structure ThrottlingError {}

The message member of an error structure is special-cased. It contains the human-readable message that describes the error. If the message member is not defined in the structure, code generated for the error may not provide an idiomatic way to access the error message (e.g., an exception message in Java).

@error("client")
@retryable
@httpError(429)
structure ThrottlingError {
    @required
    message: String
}

7.7. input trait#

Summary
Specializes a structure for use only as the input of a single operation, providing relaxed backward compatibility requirements for structure members.
Trait selector
structure
Value type
Annotation trait.
Conflicts with
See also

The following example defines an @input structure:

@input
structure SomeOperationInput {
    @required
    name: String
}

@input structure constraints

Structure shapes marked with the @input trait MUST adhere to the following constraints:

  1. They can only be referenced in the model as an operation's input.
  2. They cannot be used as the input of more than one operation.
  3. They SHOULD have a shape name that starts with the name of the operation that targets it (if any). For example, the input shape of the GetSprocket operation SHOULD be named GetSprocketInput, GetSprocketRequest, or something similar.

These constraints allow tooling to specialize operation input shapes in ways that would otherwise require complex model transformations.

Impact on backward compatibility

Required members of a structure marked with the @input trait are implicitly considered clientOptional. It is backward compatible to remove the @required trait from top-level members of structures marked with the @input trait, and the @required trait does not need to be replaced with the @default trait (though this is allowed as well). This gives service teams the ability to remove the @required trait from top-level input members and loosen requirements without risking breaking previously generated clients.

7.8. output trait#

Summary
Specializes a structure for use only as the output of a single operation.
Trait selector
structure
Value type
Annotation trait.
Conflicts with
input trait, error trait

Structure shapes marked with the @output trait MUST adhere to the following constraints:

  1. They can only be referenced in the model as an operation's output.
  2. They cannot be used as the output of more than one operation.
  3. They SHOULD have a shape name that starts with the name of the operation that targets it (if any). For example, the output shape of the GetSprocket operation SHOULD be named GetSprocketOutput.

These constraints allow tooling to specialize operation output shapes in ways that would otherwise require complex model transformations.

7.9. sparse trait#

Summary
Indicates that lists and maps MAY contain null values. The sparse trait has no effect on map keys; map keys are never allowed to be null.
Trait selector
:is(list, map)
Value type
Annotation trait.

The following example defines a list shape that MAY contain null values:

@sparse
list SparseList {
    member: String
}

The following example defines a map shape that MAY contain null values:

@sparse
map SparseMap {
    key: String
    value: String
}

7.10. mixin trait#

Summary
Indicates that the targeted shape is a mixin.
Trait selector
:not(member)
Value type
structure

The mixin trait is a structure that contains the following members:

Property Type Description
localTraits [Shape ID]

A list of shape IDs which MUST reference valid traits that are applied directly to the mixin. The traits in the list are not copied onto shapes that use the mixin. This only affects traits applied to the mixin container shape and has no impact on the members contained within a mixin.

Note

The mixin trait is considered implicitly present in this property and does not need to be explicitly added.

@mixin
structure BaseUser {
    id: String
}

structure UserDetails with [BaseUser] {
    alias: String
    email: String
}

See also

See Mixins for details on how mixins work.