hey-api / openapi-ts

✨ Turn your OpenAPI specification into a beautiful TypeScript client
https://heyapi.vercel.app
MIT License
660 stars 48 forks source link

Query parameter serialization styles #393

Open swurzinger opened 2 months ago

swurzinger commented 2 months ago

Description

OpenAPI 3 supports various different options how query parameter objects should be serialized. See https://swagger.io/docs/specification/serialization/#query

Issue description

Unfortunately openapi-ts doesn't seem to support that yet. For query parameters it seems to default to style: deepObject The OpenAPI reference, in contrast, mentions style: form and explode: true as defaults for query parameters (see https://swagger.io/specification/#parameter-object). That causes incompatible clients for server APIs using that functionality.

Example

Here's an excerpt from a simple API endpoint returning a redirect URI and allowing to pass arbitrary query parameters. This is done by defining a query parameter params with a key-value map type and serialization options style: form and explode: true. The explicitly defined serialization options actually wouldn't be necessary since they just set the values that are defaults anyways, according to the spec.

  /application/{applicationGUID}/redirect:
    get:
      operationId: getApplicationRedirectUri
      parameters:
        - name: applicationGUID
          in: path
          description: applicationGUID
          required: true
          style: simple
          explode: false
          schema:
            type: string
        - in: query
          name: params
          schema:
            type: object
            additionalProperties:
              type: string
          style: form
          explode: true
      responses:
        '200':
          description: OK

A request with the following sample parameter values

this.applicationService.getApplicationRedirectUri({
    applicationGuid: 'a5578d89-7f07-4b9a-b9e6-6e1eb6ea2bf8',
    params: {
        source: 'previousActivity',
        target: 'nextActivity',
    },
}),

.. will result in the following (wrong) request URI: /application/a5578d89-7f07-4b9a-b9e6-6e1eb6ea2bf8/redirect?params[source]=previousActivity&params[target]=nextActivity

The correct request URI would be: /application/a5578d89-7f07-4b9a-b9e6-6e1eb6ea2bf8/redirect?source=previousActivity&target=nextActivity

How other generators deal with this

For an example how other generators solve this, see: https://openapi-ts.pages.dev/openapi-fetch/api#queryserializer In that project they just allow to configure this on a request / client level instead of per parameter. And they also use non-standard defaults.

jordanshatford commented 2 months ago

@swurzinger Thanks for creating this issue. Looks like something we should provide support for. Will need to dig deeper into it!

mrlubos commented 2 months ago

Hey @swurzinger, thank you for the issue. Out of curiosity, which codegen tool are you using? If you're not using the one you linked, why not?

swurzinger commented 2 months ago

For context: I'm trying to migrate from a manually written API client to a generated one. Therefore I'm evaluating multiple tools, not only this one. I chose this one for my prototype implementation, though, and encountered this issue during migration/testing. Afterwards I verified how other tools behave and unfortunately none seems to support it really properly, as described in the spec. openapi-typescript at least allows to configure it on a request or client level, but ignores the definition in the OpenAPI YAML and requires additional configuration of the client instead (which is bad). However, most others are worse and don't support it at all, e.g. openapi-generator. I'm always testing an angular generator, if available, otherwise a generic one using fetch. I prefer openapi-ts (this project) over openapi-typescript because they don't support angular.

mrlubos commented 2 months ago

@swurzinger will take this into consideration as part of the clients release

swurzinger commented 2 months ago

@swurzinger will take this into consideration as part of the clients release

Thank You! Is there some more information available on what will be different for the new clients vs. legacy clients? Or is it "just" new clients based on newer http client libraries/apis (fetch, axios, ..)? The documentation page doesn't really help me understanding that. I just see a potential problem for my use case: a client for Angular is not planned (yet?), which might make it unsuitable for me in the first version. Not sure how hard it will be to create a new client based on Angular's HttpClient or adapt the existing one. Although promise-based clients like fetch or axios could be used in Angular they don't integrate so well, cause Angular uses RxJS Observables instead of promises, so everything needs to be converted. Furthermore things like Angular's HttpInterceptors won't work.

Floppy012 commented 1 month ago

I've just been made aware of this tool and have seen that it's a fork of openapi-typescript-codegen. I have a PR open over there for more than a year now. It's basically ready to be merged but the repo owner is inactive. Maybe it helps with integrating this feature into this repo too.

https://github.com/ferdikoomen/openapi-typescript-codegen/pull/1426

mrlubos commented 1 month ago

Hey @Floppy012, welcome over on this side! 👋 Yes, this will be available in Hey API. It will be part of the new clients which is currently WIP.

elibolonur commented 1 month ago

@mrlubos thank you for the ping on the other side! Feels great to see that there is an alternative now with an active maintainer :) We will be moving to this tool soon, looking forward to have this feature!

mrlubos commented 1 month ago

Hey all, this is available in the new Fetch API client (demo). Angular/Axios/etc not available yet, if you're using Fetch API, I'd love to hear your thoughts!