diff --git a/doc/how-tos/delimited-path-parameters.md b/doc/how-tos/delimited-path-parameters.md new file mode 100644 index 0000000000..9e975e2f8e --- /dev/null +++ b/doc/how-tos/delimited-path-parameters.md @@ -0,0 +1,103 @@ +# Handling Delimited Path Parameters + +Tapir allows you to handle complex path parameters, such as lists of custom types separated by delimiters (e.g., commas). +This can be achieved using `Codec.delimited`, which facilitates the serialization and deserialization of delimited lists +within path segments. + +## Use Case + +Suppose you want to define an endpoint that accepts a list of names as a comma-separated path parameter. Each name should +adhere to a specific pattern (e.g., only uppercase letters). + +## Implementation Steps: + +### 1. Define the Custom Type and Validator +Start by defining your custom type and the associated validator to enforce the desired pattern. + +```scala mdoc:compile-only +import sttp.tapir._ +import sttp.tapir.generic.auto._ +import sttp.tapir.Codec +import sttp.tapir.Validator +import sttp.tapir.CodecFormat.TextPlain +import sttp.tapir.model.Delimited + +case class Name(value: String) + +// Validator to ensure names consist of uppercase letters only +val nameValidator: Validator[String] = Validator.pattern("^[A-Z]+$") +``` + +### 2. Create Codecs for the Custom Type and Delimited List +Utilize `Codec.parsedString` for individual `Name` instances and `Codec.delimited` for handling the list. + +```scala +// Codec for single Name +given Codec[String, Name, TextPlain] = Codec.parsedString(Name.apply) + .validate(nameValidator.contramap(_.value)) + +// Codec for a list of Names, delimited by commas +given Codec[String, Delimited[",", Name], TextPlain] = Codec.delimited +``` + +### 3. Define the Endpoint with Delimited Path Parameter +Incorporate the delimited codec into your endpoint definition to handle the list of names in the path. + +```scala mdoc:compile-only +import sttp.tapir._ +import sttp.tapir.generic.auto._ +import sttp.tapir.Codec +import sttp.tapir.Validator +import sttp.tapir.CodecFormat.TextPlain +import sttp.tapir.model.Delimited + +case class Name(value: String) + +// Validator to ensure names consist of uppercase letters only +val nameValidator: Validator[String] = Validator.pattern("^[A-Z]+$") + +// Codec for single Name +given Codec[String, Name, TextPlain] = Codec.parsedString(Name.apply) + .validate(nameValidator.contramap(_.value)) + +// Codec for a list of Names, delimited by commas +given Codec[String, Delimited[",", Name], TextPlain] = Codec.delimited + +val getUserEndpoint = + endpoint.get + .in("user" / path[Delimited[",", Name]]("id")) + .out(stringBody) +``` + +### 4. Generated OpenAPI Schema +When you generate the OpenAPI documentation for this endpoint, the schema for the `id` path parameter will +correctly reflect it as an array with the specified pattern for each item. + +```yaml +paths: + /user/{id}: + get: + operationId: getUserId + parameters: + - name: id + in: path + required: true + schema: + type: array + items: + type: string + pattern: ^[A-Z]+$ +``` + +## Explanation +- `Codec.parsedString`: Transforms a `String` into a custom type (`Name`) and vice versa. It also applies validation to + ensure each `Name` adheres to the specified pattern. +- `Codec.delimited`: Handles the serialization and deserialization of a delimited list (e.g., comma-separated) of the + custom type. By specifying `Delimited[",", Name]`, Tapir knows how to split and join the list based on the delimiter. +- Endpoint Definition: The `path[List[Name]]("id")` indicates that the id path parameter should be treated as a list of + `Name` objects, utilizing the previously defined codecs. + +## Validation +Validators play a crucial role in ensuring that each element within the delimited list meets the required criteria. In +this example, `nameValidator` ensures that each `Name` consists solely of uppercase letters. Tapir applies this validation +to each element in the list, providing robust input validation. \ No newline at end of file diff --git a/doc/index.md b/doc/index.md index 9fe3f0bbe4..b57ef23384 100644 --- a/doc/index.md +++ b/doc/index.md @@ -156,6 +156,7 @@ sttp is a family of Scala HTTP-related projects, and currently includes: examples external + how-tos/delimited-path-parameters .. toctree:: :maxdepth: 2