# Why does \`query\` \`Codec\` uses \`List\[String\]\` as the low-level value?

**URL:** <https://softwaremill.community/t/why-does-query-codec-uses-list-string-as-the-low-level-value/413>\
**Category:** tapir\
**Created:** [July 1, 2024, 5:02pm UTC](https://softwaremill.community/t/why-does-query-codec-uses-list-string-as-the-low-level-value/413 "2024-07-01T17:02:47Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![arturaz](https://dub1.discourse-cdn.com/flex005/user_avatar/softwaremill.community/arturaz/32/275_2.png) [@arturaz](https://softwaremill.community/u/arturaz)\
**Post date:** [July 1, 2024, 5:02pm UTC](https://softwaremill.community/t/why-does-query-codec-uses-list-string-as-the-low-level-value/413/1 "2024-07-01T17:02:47Z")

</div>

Hey.

While implementing a `Codec` for my custom data type I was surprised that a `Codec[String, MyType, CodecFormat.TextPlain]` could not be used for `query` input.

Turns out `query` is defined like this:

```scala
def query[T: Codec[List[String], *, TextPlain]](name: String): EndpointInput.Query[T]

```

I understand there can be more than one query param in the URI, but why isn’t it this instead?

```scala
def query[T: Codec[String, *, TextPlain]](name: String): EndpointInput.Query[List[T]]

```

This way the same `Codec` that can be used for `path` segments would be useable for query parameters, which makes sense to me. I am not even sure how the `List[String]` codec should behave. Can it be passed `Nil` if query parameter wasn’t provided in the URI, or is it guaranteed to always have at least 1 item? (Something like cats `NonEmptyList` would be useful here then).

---

<div class="post-metadata">

**Author:** ![adamw](https://dub1.discourse-cdn.com/flex005/user_avatar/softwaremill.community/adamw/32/28_2.png) [@adamw](https://softwaremill.community/u/adamw)\
**Post date:** [July 1, 2024, 5:48pm UTC](https://softwaremill.community/t/why-does-query-codec-uses-list-string-as-the-low-level-value/413/2 "2024-07-01T17:48:05Z")

</div>

With:

```auto
def query[T: Codec[String, *, TextPlain]](name: String): EndpointInput.Query[List[T]]

```

the input would always yield a list of values, even if you always need one (which is a common case). In the tapir approach, when you have:

```auto
def query[T: Codec[List[String], *, TextPlain]](name: String): EndpointInput.Query[T]

```

it means that the codec needs to handle decoding of a list of values of the query parameter (as query parameters can be repeated). The codec might return errors if there are multiple, or not enough occurrences of the query parameter. That’s all handled via the `DecodeResult` value, which is used in `Codec.map`. More on codecs: [Codecs — tapir 1.x documentation](https://tapir.softwaremill.com/en/latest/endpoint/codecs.html)

In your case, if you have `val myCodec: Codec[String, MyType, CodecFormat.TextPlain]`, you can use `Codec.listHead(myCodec)`, which will give you a `Codec[List[String], MyType, CodecFormat.TextPlain]`. This will be applied automatically, if the `myCodec` value is available in the implicit scope, and you simply create the query parameter description as `query[MyType]`.

You can also use a `NonEmptyList`, if required, using the cats integration: [Datatypes integrations — tapir 1.x documentation](https://tapir.softwaremill.com/en/latest/endpoint/integrations.html#cats-datatypes-integration). If you provide a `Codec[String, MyType, TextPlain]` in the implicit scope, you’ll also be able to create a `query[NonEmptyList[MyType]]`, provided that the integration is in scope (imported).
