# Custom Tapir Schema for self-recursive ADT

**URL:** <https://softwaremill.community/t/custom-tapir-schema-for-self-recursive-adt/517>\
**Category:** tapir\
**Created:** [August 8, 2025, 9:46am UTC](https://softwaremill.community/t/custom-tapir-schema-for-self-recursive-adt/517 "2025-08-08T09:46:46Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![mihaisoloi](https://dub1.discourse-cdn.com/flex005/user_avatar/softwaremill.community/mihaisoloi/32/333_2.png) [@mihaisoloi](https://softwaremill.community/u/mihaisoloi)\
**Post date:** [August 8, 2025, 9:46am UTC](https://softwaremill.community/t/custom-tapir-schema-for-self-recursive-adt/517/1 "2025-08-08T09:46:46Z")

</div>

I’ve got a rather simple structure that I have to expose as an API, initially I thought of defining these as just case classes but I ended up with over 60 different case classes and that is a pain to test and to generate data for.

I have created this scastie to see the code: [Scastie - An interactive playground for Scala.](https://scastie.scala-lang.org/2dzW5KmaQD2e8C2PLjQOZA) but here’s a little snippet:

```auto
final case class Sport(id: Int, name: Option[String]) derives Codec, Schema
final case class Competition(id: Int, name: Option[String]) derives Codec, Schema

type TradingRestriction = Restriction[SportRestriction]
type SportRestriction = Node[Sport, Competition]

enum Restriction[A]:
  case All()
  case Selections(values: NonEmptySet[A])

final case class Node[A, B](value: A, children: Restriction[B])

```

The problem I have is that in the NodeSchema I can’t flatten the `children` into separate fields in the Node. Do you have any suggestions?

---

<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:** [August 16, 2025, 2:57pm UTC](https://softwaremill.community/t/custom-tapir-schema-for-self-recursive-adt/517/2 "2025-08-16T14:57:22Z")

</div>

If I understand correctly, you want to “pull up” all the fields from `children` to the node’s schema, without the intermediate `children` wrapper?

If so, I think the way to do it is to manipulate the schemas themselves. So: you take the node schema:

```auto
Schema
    .derived[Node[A, B]]
    .name(Schema.SName(s"${key.capitalize}Node"))

```

Then you change it’s `SchemaType` field (it’s a case class), which will be a `SchemType.SProduct`. You need to append the fields from the `children` field to the top-level. Some not-so-pretty case class manipulation should do the trick 🙂
