Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add docs for delimited path parameters #4079

Merged
merged 5 commits into from
Oct 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 103 additions & 0 deletions doc/how-tos/delimited-path-parameters.md
Original file line number Diff line number Diff line change
@@ -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.
1 change: 1 addition & 0 deletions doc/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading