{"id":15651598,"url":"https://github.com/ghostbuster91/sttp-openapi-generator","last_synced_at":"2025-10-14T03:31:26.130Z","repository":{"id":39749320,"uuid":"349704778","full_name":"ghostbuster91/sttp-openapi-generator","owner":"ghostbuster91","description":"Generate sttp client from openapi specification with ease!","archived":true,"fork":false,"pushed_at":"2023-10-20T17:46:14.000Z","size":917,"stargazers_count":32,"open_issues_count":17,"forks_count":11,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-10-01T11:46:06.459Z","etag":null,"topics":["http-client","openapi","scala","sttp","swagger"],"latest_commit_sha":null,"homepage":"","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ghostbuster91.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-03-20T11:23:11.000Z","updated_at":"2024-06-07T16:02:54.000Z","dependencies_parsed_at":"2023-02-11T13:15:27.744Z","dependency_job_id":"9eb4096c-e90d-4cde-a235-ae87b7c8ddda","html_url":"https://github.com/ghostbuster91/sttp-openapi-generator","commit_stats":null,"previous_names":[],"tags_count":15,"template":false,"template_full_name":null,"purl":"pkg:github/ghostbuster91/sttp-openapi-generator","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ghostbuster91%2Fsttp-openapi-generator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ghostbuster91%2Fsttp-openapi-generator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ghostbuster91%2Fsttp-openapi-generator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ghostbuster91%2Fsttp-openapi-generator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ghostbuster91","download_url":"https://codeload.github.com/ghostbuster91/sttp-openapi-generator/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ghostbuster91%2Fsttp-openapi-generator/sbom","scorecard":{"id":425851,"data":{"date":"2025-08-11","repo":{"name":"github.com/ghostbuster91/sttp-openapi-generator","commit":"5fded6f40b8077e19d73b632655362ab68fa4d95"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4.7,"checks":[{"name":"Maintained","score":0,"reason":"project is archived","details":["Warn: Repository is archived."],"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Code-Review","score":9,"reason":"Found 15/16 approved changesets -- score normalized to 9","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/dependency-graph.yml:1","Warn: no topLevel permission defined: .github/workflows/main.yml:1","Warn: no topLevel permission defined: .github/workflows/scala-steward.yml:1","Warn: no topLevel permission defined: .github/workflows/sourcegraph.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/dependency-graph.yml:15: update your workflow using https://app.stepsecurity.io/secureworkflow/ghostbuster91/sttp-openapi-generator/dependency-graph.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/dependency-graph.yml:16: update your workflow using https://app.stepsecurity.io/secureworkflow/ghostbuster91/sttp-openapi-generator/dependency-graph.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/dependency-graph.yml:17: update your workflow using https://app.stepsecurity.io/secureworkflow/ghostbuster91/sttp-openapi-generator/dependency-graph.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/main.yml:17: update your workflow using https://app.stepsecurity.io/secureworkflow/ghostbuster91/sttp-openapi-generator/main.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/main.yml:18: update your workflow using https://app.stepsecurity.io/secureworkflow/ghostbuster91/sttp-openapi-generator/main.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/main.yml:19: update your workflow using https://app.stepsecurity.io/secureworkflow/ghostbuster91/sttp-openapi-generator/main.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/main.yml:30: update your workflow using https://app.stepsecurity.io/secureworkflow/ghostbuster91/sttp-openapi-generator/main.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/main.yml:31: update your workflow using https://app.stepsecurity.io/secureworkflow/ghostbuster91/sttp-openapi-generator/main.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/main.yml:32: update your workflow using https://app.stepsecurity.io/secureworkflow/ghostbuster91/sttp-openapi-generator/main.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/main.yml:44: update your workflow using https://app.stepsecurity.io/secureworkflow/ghostbuster91/sttp-openapi-generator/main.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/main.yml:47: update your workflow using https://app.stepsecurity.io/secureworkflow/ghostbuster91/sttp-openapi-generator/main.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/main.yml:48: update your workflow using https://app.stepsecurity.io/secureworkflow/ghostbuster91/sttp-openapi-generator/main.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/main.yml:64: update your workflow using https://app.stepsecurity.io/secureworkflow/ghostbuster91/sttp-openapi-generator/main.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/main.yml:67: update your workflow using https://app.stepsecurity.io/secureworkflow/ghostbuster91/sttp-openapi-generator/main.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/main.yml:68: update your workflow using https://app.stepsecurity.io/secureworkflow/ghostbuster91/sttp-openapi-generator/main.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/main.yml:91: update your workflow using https://app.stepsecurity.io/secureworkflow/ghostbuster91/sttp-openapi-generator/main.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/main.yml:101: update your workflow using https://app.stepsecurity.io/secureworkflow/ghostbuster91/sttp-openapi-generator/main.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/scala-steward.yml:14: update your workflow using https://app.stepsecurity.io/secureworkflow/ghostbuster91/sttp-openapi-generator/scala-steward.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/scala-steward.yml:15: update your workflow using https://app.stepsecurity.io/secureworkflow/ghostbuster91/sttp-openapi-generator/scala-steward.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/scala-steward.yml:16: update your workflow using https://app.stepsecurity.io/secureworkflow/ghostbuster91/sttp-openapi-generator/scala-steward.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/scala-steward.yml:20: update your workflow using https://app.stepsecurity.io/secureworkflow/ghostbuster91/sttp-openapi-generator/scala-steward.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/sourcegraph.yml:12: update your workflow using https://app.stepsecurity.io/secureworkflow/ghostbuster91/sttp-openapi-generator/sourcegraph.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/sourcegraph.yml:13: update your workflow using https://app.stepsecurity.io/secureworkflow/ghostbuster91/sttp-openapi-generator/sourcegraph.yml/master?enable=pin","Info:   0 out of   8 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of  15 third-party GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 29 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-19T02:13:36.941Z","repository_id":39749320,"created_at":"2025-08-19T02:13:36.941Z","updated_at":"2025-08-19T02:13:36.941Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279017767,"owners_count":26086145,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-14T02:00:06.444Z","response_time":60,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["http-client","openapi","scala","sttp","swagger"],"created_at":"2024-10-03T12:39:15.327Z","updated_at":"2025-10-14T03:31:25.784Z","avatar_url":"https://github.com/ghostbuster91.png","language":"Scala","funding_links":[],"categories":[],"sub_categories":[],"readme":"# sttp-openapi-generator\n[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fghostbuster91%2Fsttp-openapi-generator.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fghostbuster91%2Fsttp-openapi-generator?ref=badge_shield)\n[\u003cimg alt=\"GitHub Workflow\" src=\"https://img.shields.io/github/actions/workflow/status/ghostbuster91/sttp-openapi-generator/.github/workflows/main.yml?style=for-the-badge\" height=\"24\"\u003e](https://github.com/ghostbuster91/sttp-openapi-generator/actions)\n[\u003cimg alt=\"Maven Central\" src=\"https://img.shields.io/maven-central/v/io.github.ghostbuster91.sttp-openapi/codegen-core_2.12?style=for-the-badge\" height=\"24\"\u003e](https://search.maven.org/artifact/io.github.ghostbuster91.sttp-openapi/codegen-core_2.12)\n[\u003cimg src=\"https://github.com/ghostbuster91/sttp-openapi-generator/raw/master/sourcegraph-mark.svg\" width=\"25\"\u003e](https://sourcegraph.com/github.com/ghostbuster91/sttp-openapi-generator)\n\nThe generator can be used in projects with following scala versions: 2.12, 2.13 and 3.x\n\n## Farewell\n\nI decided to archive this project. I kept putting off the rewrite of its internals to make the parser more robust but I think that it will never happen. \nI no longer have the energy nor the ambition to keep this project alive. Also, nowadays there are better alternatives to describe a service interface (e.g. [smithy](https://smithy.io/2.0/index.html) or [protobufs](https://protobuf.dev/)). \n\nIf you came here looking for a solution to generate scala code from some description of your api/interface I can recommend following options:\n- [Guardrail](https://github.com/guardrail-dev/guardrail) if you already have openapi spec\n- [smithy4s](https://github.com/disneystreaming/smithy4s) if you don't have yet openapi, and you are willing to use something better than yaml/json\n\n\n## Why?\n\nWhy creating another openapi-generator when there is an official one? While the mentioned generator is generally a great project and serves well for many people its scala part has a few flaws in my opinion. There is no proper encoding for discriminators, neither support for other json libraries. The genereted code doesn't feel like native. These, as well as the other things, could (and probably will at some point) be implemented, but the size of the project and underlying templating engine(mustache) don't make it easier. Last but not least it is currently impossible to generate openapi code into src-managed directory (https://github.com/OpenAPITools/openapi-generator/issues/6685). I think that, by extracting and focusing only on a scala related part, it can be done better.\n\n## Goals of the project\n\n- generate idiomatic scala code\n- support popular json libararies from scala ecosystem\n- support only sttp but do it well\n- [proper integration with sbt and other build tools](#sbt-plugin)\n- [support discriminators](#discriminators)\n- [support error encoding](#error-encoding)\n- [support open products](#open-product)\n- [comparison to similar projects](#comparison-to-similar-projects)\n\n## Teaser\n\nGiven following yaml:\n\n```yaml\nopenapi: 3.0.3\ninfo:\n  title: Entities\n  version: \"1.0\"\npaths:\n  /:\n    get:\n      operationId: getRoot\n      responses:\n        \"200\":\n          description: \"\"\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/Person\"\ncomponents:\n  schemas:\n    Person:\n      required:\n        - name\n        - age\n      type: object\n      properties:\n        name:\n          type: string\n        age:\n          type: integer\n          minimum: 11\n```\n\nit will be turned into:\n\n```scala\ntrait CirceCodecs extends SttpCirceApi {\n  implicit lazy val personDecoder: Decoder[Person] =\n    Decoder.forProduct2(\"name\", \"age\")(Person.apply)\n  implicit lazy val personEncoder: Encoder[Person] =\n    Encoder.forProduct2(\"name\", \"age\")(p =\u003e (p.name, p.age))\n}\nobject CirceCodecs extends CirceCodecs\n\ncase class Person(name: String, age: Int)\n\nclass DefaultApi(baseUrl: String, circeCodecs: CirceCodecs = CirceCodecs) {\n  import circeCodecs._\n\n  def getRoot(): Request[Person, Any] = basicRequest\n    .get(uri\"$baseUrl\")\n    .response(\n      fromMetadata(\n        asJson[Person].getRight,\n        ConditionalResponseAs(\n          _.code == StatusCode.unsafeApply(200),\n          asJson[Person].getRight\n        )\n      )\n    )\n}\n```\n## Getting started\n\n### sbt-plugin\n\nYou can use sttp-openapi-codegen seamlessly with sbt via a dedicated sbt plugin. \nIn order to use it, follow the usual convention, first add it to `project/plugins.sbt`:\n```scala\naddSbtPlugin(\"io.github.ghostbuster91.sttp-openapi\" % \"sbt-codegen-plugin\" % \u003cversion\u003e)\n```\n\nnext, enable it for the desired modules in `build.sbt`:\n```scala\nimport SttpOpenApiCodegenPlugin._\nenablePlugins(SttpOpenApiCodegenPlugin)\n```\n\nGenerator will walk through all files in input directory and generate for each one respective code into output directory.\nPackage name based on directory structure will be preserved.\n\nCode generation can be configured by one of the following options:\n\n- `sttpOpenApiOutputPath` - Directory for sources generated by sttp-openapi generator (default: `target/scala-2.12/src_managed/`)\n\n- `sttpOpenApiInputPath` - Input resources for sttp-openapi generator (default: `./resources`)\n\n- `sttpOpenApiJsonLibrary` - Json library for sttp-openapi generator to use (currently only `Circe`)\n\n- `sttpOpenApiHandleErrors` - If true the generator will include error information in types (default: `true`)\n\nFor always up-to-date examples of codegen plugin usage refer to sbt tests: https://github.com/ghostbuster91/sttp-openapi-generator/tree/master/sbt-codegen-plugin/src/sbt-test/codegen/simple\n\n### mill-plugin\n\nYou can use sttp-openapi-codegen seamlessly with mill via a dedicated mill plugin. \n```scala\nimport mill._, mill.scalalib._\n\nimport $ivy.`io.github.ghostbuster91.sttp-openapi::mill-codegen-plugin:\u003cversion\u003e`\nimport io.github.ghostbuster91.sttp.client3.OpenApiCodegenScalaModule\n\nobject app extends OpenApiCodegenScalaModule {\n  def scalaVersion = \"2.13.2\"\n  def mainClass = Some(\"com.example.Main\")\n}\n```\n\nAdditional configuration options:\n```scala\n/** Input resources for sttp-openapi generator\n  */\ndef sttpOpenApiInput: T[Seq[Input]] = T {\n  Seq(\n    Input.dir(millSourcePath / \"src\" / \"main\" / \"openapi\"),\n    Input.dir(millSourcePath / \"openapi\")\n  )\n}\n\n/** Json library for sttp-openapi generator to use\n  */\ndef sttpOpenApiJsonLibrary: T[JsonLibrary] = T(JsonLibrary.Circe: JsonLibrary)\n\n/** If true the generator will include error information in types\n  */\ndef sttpOpenApiHandleErrors: T[Boolean] = T(true)\n\n/** If true the generator will render model classes only if they are\n  * referenced by any of the exiting operations\n  */\ndef sttpOpenApiMinimizeOutput: T[Boolean] = T(true)\n\n/** Additional types mapping configuration\n  */\ndef sttpOpenApiTypesMapping: T[TypesMapping] = T(TypesMapping())\n```\n\nFor always up-to-date examples of codegen plugin usage refer to mill plugin integration tests: https://github.com/ghostbuster91/sttp-openapi-generator/tree/master/mill-codegen-plugin-itest\n\n## discriminators\n\nIn the openapi specification there is a notion of [discriminators](https://swagger.io/docs/specification/data-models/inheritance-and-polymorphism/). \nThese objects are used to distinguishing between polymorphic instances of some type based on a given value.\n\nThis project takes advantage of them and generates json configs accordingly.\n\n```yaml\ncomponents:\n  schemas:\n    Entity:\n      oneOf:\n        - $ref: \"#/components/schemas/Person\"\n        - $ref: \"#/components/schemas/Organization\"\n      discriminator:\n        propertyName: name\n        mapping:\n          john: \"#/components/schemas/Person\"\n          sml: \"#/components/schemas/Organization\"\n    Person:\n      required:\n        - name\n        - age\n      type: object\n      properties:\n        name:\n          type: string\n        age:\n          type: integer\n    Organization:\n      required:\n        - name\n        - size\n      type: object\n      properties:\n        name:\n          type: string\n        size:\n          type: integer\n```\n\n```scala\nsealed trait Entity\ncase class Organization(size: Int) extends Entity()\ncase class Person(name: String, age: Int) extends Entity()\n\ntrait CirceCodecs extends SttpCirceApi {\n  // codecs for Person and Organization omitted for readability\n\n  implicit lazy val entityDecoder: Decoder[Entity] = new Decoder[Entity]() {\n    override def apply(c: HCursor): Result[Entity] = c\n      .downField(\"name\")\n      .as[String]\n      .flatMap({\n        case \"john\" =\u003e Decoder[Person].apply(c)\n        case \"sml\"  =\u003e Decoder[Organization].apply(c)\n        case other =\u003e\n          Left(DecodingFailure(\"Unexpected value for coproduct:\" + other, Nil))\n      })\n  }\n  implicit lazy val entityEncoder: Encoder[Entity] = new Encoder[Entity]() {\n    override def apply(entity: Entity): Json = entity match {\n      case person: Person =\u003e Encoder[Person].apply(person)\n      case organization: Organization =\u003e\n        Encoder[Organization].apply(organization)\n    }\n  }\n}\n```\n\n## error encoding\n\nIn openapi error responses can be represented equally easily as success ones.\nThat is also the case for the sttp client. \nIf you are not a fan of error handling, you can disable that feature in generator settings.\n\n```yaml\nopenapi: 3.0.2\ninfo:\n  title: Entities\n  version: \"1.0\"\npaths:\n  /person:\n    put:\n      summary: Update an existing person\n      description: Update an existing person by Id\n      operationId: updatePerson\n      responses:\n        \"400\":\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/ErrorModel\"\n        \"401\":\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/ErrorModel2\"\ncomponents:\n  schemas:\n    ErrorModel:\n      required:\n        - msg\n      type: object\n      properties:\n        msg:\n          type: string\n    ErrorModel2:\n      required:\n        - msg\n      type: object\n      properties:\n        msg:\n          type: string\n```\n\n```scala\nsealed trait UpdatePersonGenericError\ncase class ErrorModel(msg: String) extends UpdatePersonGenericError()\ncase class ErrorModel2(msg: String) extends UpdatePersonGenericError()\n\nclass DefaultApi(baseUrl: String, circeCodecs: CirceCodecs = CirceCodecs) {\n  import circeCodecs._\n \n  def updatePerson(): Request[\n    Either[ResponseException[UpdatePersonGenericError, CirceError], Unit],\n    Any\n  ] = basicRequest\n    .put(uri\"$baseUrl/person\")\n    .response(\n      fromMetadata(\n        asJsonEither[UpdatePersonGenericError, Unit],\n        ConditionalResponseAs(\n          _.code == StatusCode.unsafeApply(400),\n          asJsonEither[ErrorModel, Unit]\n        ),\n        ConditionalResponseAs(\n          _.code == StatusCode.unsafeApply(401),\n          asJsonEither[ErrorModel2, Unit]\n        )\n      )\n    )\n}\n```\n\n## open-product\n\nIn openapi specifications data models can be extended by [arbitrary properties if needed](https://swagger.io/docs/specification/data-models/dictionaries/). \nTo do that one has to specify `additionalProperties` on particular model. At the same time on the call site special codecs need to be provided to support such types.\nLuckily, sttp-openapi-generator will handle that as well.\n\n```yaml\nopenapi: 3.0.3\ninfo:\n  title: Entities\n  version: \"1.0\"\npaths:\n  /:\n    get:\n      operationId: getRoot\n      responses:\n        \"200\":\n          description: \"\"\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/Person\"\ncomponents:\n  schemas:\n    Person:\n      required:\n        - name\n        - age\n      type: object\n      properties:\n        name:\n          type: string\n        age:\n          type: integer\n      additionalProperties: true\n```\n\n```scala\ntrait CirceCodecs extends SttpCirceApi {\n  implicit lazy val personDecoder: Decoder[Person] = new Decoder[Person]() {\n    override def apply(c: HCursor): Result[Person] =\n      for {\n        name \u003c- c.downField(\"name\").as[String]\n        age \u003c- c.downField(\"age\").as[Int]\n        additionalProperties \u003c- c.as[Map[String, Json]]\n      } yield Person(\n        name,\n        age,\n        additionalProperties.filterKeys(_ != \"name\").filterKeys(_ != \"age\")\n      )\n  }\n  implicit lazy val personEncoder: Encoder[Person] = new Encoder[Person]() {\n    override def apply(person: Person): Json = Encoder\n      .forProduct2[Person, String, Int](\"name\", \"age\")(p =\u003e (p.name, p.age))\n      .apply(person)\n      .deepMerge(\n        Encoder[Map[String, Json]].apply(person._additionalProperties)\n      )\n  }\n\n}\n\ncase class Person(\n    name: String,\n    age: Int,\n    _additionalProperties: Map[String, Json]\n)\n```\n\n## Comparison to similar projects\n\nApart from official openApi generator which was mentioned in the [Why?](#why?) section there are other similar projects.\n\n- [Guardrail](https://github.com/guardrail-dev/guardrail)\n\n    Guardrail can generate both client and server code. When it comes to client code generation it is similar to that project,\n    although it supports http4s and akka-http, while this project focuses solely on sttp.\n\n## Contributing\n\nContributions are more than welcome. This is an early stage project which means that everything is subject to change.\n\nSee the list of issues and pick one! Or report your own.\n\nIf you are having doubts on the why or how something works, don't hesitate to ask a question.\n\n\n## Releasing a new version\n\nPush a new tag to the master branch.\n\n## License\n[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fghostbuster91%2Fsttp-openapi-generator.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fghostbuster91%2Fsttp-openapi-generator?ref=badge_large)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fghostbuster91%2Fsttp-openapi-generator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fghostbuster91%2Fsttp-openapi-generator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fghostbuster91%2Fsttp-openapi-generator/lists"}