Smithy Server Generator for TypeScript validation#
Smithy defines a set of constraint traits that can be used to restrict the set of allowed values for a shape. These traits do not change the type generated; the type will always correspond with the Smithy type. This allows Smithy models to evolve over time, so that model changes will not cause changes that break type checks.
Smithy server SDKs will only validate input values against constraint traits. While output structures can have constraint traits applied, they will never be evaluated by the server SDK directly. This is because the failure would be too late in the process. At the point of validation, the server SDKs could only return an InternalFailureException to the user, and changes made by the operation implementation would neither be returned to the user nor rolled back.
Default validation#
The server SDK validates inputs by default if every operation in the service has the
smithy.framework#ValidationException error associated. This can be accomplished by associating the error directly
with the service, as well. In this mode, the constraint traits built into Smithy are applied to each
input and an error is generated and automatically returned if any are violated. ValidationException
contains an
entry for each constraint violation encountered in the input, along with a pointer to the modeled member that failed
validation, so that user interfaces can associate these validation errors with input more easily.
When default validation is enabled and validation fails, the operation is not invoked, and there is no opportunity given to the developer to customize the format of the error, to suppress certain errors in certain conditions, or to apply constraints that are not supported directly by Smithy.
If any operation is not associated with smithy.framework#ValidationException
, the server SDK will refuse to generate
code unless default validation is disabled.
Custom validation#
For advanced use-cases, developers can choose to customize the validation behavior of the server SDK. This requires
setting disableDefaultValidation
to true
in smithy-build.json
for the typescript-ssdk-codegen
plugin:
"plugins": {
"typescript-ssdk-codegen": {
"package": "@aws-smithy/example",
"packageVersion": "1.0.0-alpha.1",
"disableDefaultValidation": true
}
}
When default validation is disabled, every input is still validated against the model's constraints. Instead of
short-circuiting and returning an error, however, the server SDK instead requires the developer to decide what to do
with the validation failures encountered. In order to support this, the server SDK adds a required function parameter
of type ValidationCustomizer
to every handler factory:
export const getExampleOperationHandler = <Context>(
operation: Operation<ExampleOperationServerInput, ExampleOperationServerOutput, Context>,
customizer: ValidationCustomizer<"ExampleOperation">
): ServiceHandler<Context, HttpRequest, HttpResponse> => {
Where ValidationCustomizer
is an interface built in to @aws-smithy/server-common
for the purpose of customizing
validation failures:
export type ValidationFailure =
| EnumValidationFailure
| LengthValidationFailure
| PatternValidationFailure
| RangeValidationFailure
| RequiredValidationFailure
| UniqueItemsValidationFailure;
export interface ValidationContext<O extends string> {
operation: O;
}
export type ValidationCustomizer<O extends string> = (
context: ValidationContext<O>,
failures: ValidationFailure[]
) => ServiceException | undefined;
If the developer-supplied ValidationCustomizer
returns undefined
, then the handler will continue executing the
operation, essentially suppressing validation failures. While this is generally not a good idea, it can be useful in
cases where new constraints are being evaluated for backwards compatibility, and the service wants to log certain
validation failures instead of returning errors.
The developer-supplied ValidationCustomizer
can also return a code-generated exception extending
ServiceException
. This will cause the operation to not be executed, and an error rendered instead.
Warning
Server SDKs will not return an error from an operation without the error being associated with the service or operation. If you return a code-generated error that is not associated with the operation in question, the handler will render an InternalFailureException instead.
For example, consider a service that, for backwards compatibility purposes, needs to return a BadInput
error:
@restJson1
service WeatherService {
version: "2018-05-10",
operations: [GetForecast],
errors: [BadInput]
}
@httpError(400)
@error("client")
structure BadInput {
message: String,
}
After disabling default validation in smithy-build.json
:
"plugins": {
"typescript-ssdk-codegen": {
"package": "@example/weather-service",
"packageVersion": "1.0.0-alpha.1",
"disableDefaultValidation": true
}
}
the developer can still use Smithy's built in validation, but return BadInput
instead of ValidationException
:
const handler = getGetForecastHandler(async (input) => getForecast(input),
(ctx, failures) => {
return new BadInputError(`${failures.length} bad inputs detected.`);
};
});