You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This is a Request for Comment, and feedback is welcome! Please read the proposal, and respond with ways in which this would help or hurt the project.
Summary
Blend the best of both worlds: zero-runtime cost performance with code generation, to enhance openapi-fetch to be “smarter” but without incurring the full weight and performance hits of a generated SDK.
Background
openapi-fetch was designed to be a thin wrapper around fetch() in runtime, with all of its type information supplied by types that get baked out of the build. In other words, a typesafe fetch() wrapper. But the problem comes in people expecting it to have information about the OpenAPI schema at runtime, which leads to confusion why it can’t simply “figure it out since that information is in my OpenAPI schema.”
Historically, this has been more of a “binary” decision tree: either build a heavyweight SDK that is often bloated, and slow, and provides little-to-no runtime benefits. Or build nothing, and let TS do the work.
But I think there’s a compromise between the two where people would be OK sacrificing maybe a couple KB of client weight, and the tiniest performance hit, to handle boilerplate necessary to tell every request exactly what to do. Even better if this is opt-in behavior that’s backwards-compatible.
Changes
This requires 2 parts: a new flag added to the openapi-typescript CLI, and a new option to openapi-fetch
CLI addition
openapi-typescript CLI already generates types for your OpenAPI schema. But adding a new flag would also generate a new, second file to be consumed with a --client-runtime flag, e.g.:
--client-runtime is just a placeholder flag name; I expect we’ll come up with a better name for it before shipping
This will then generate a file that has some information like the following (proof-of-concept):
exportdefault{// returns 204: no content"/no-content-path": {GET: {request: {/* request options, if any */},response: {parseAs: "stream"/* other response options */},},},// requires multipart/form"/multipart-form-upload-path": {POST: {request: {"content-type": "multipart/form-data",/* other options */},response: {/* response options, if any */},},},// (other, standard JSON paths are omitted)};
This can all be used to automatically set certain params in openapi-fetch the way generated SDKs do. But in a performant way. The key difference is:
This is the absolute-minimum information necessary. Things don’t go in this object unless they change default behavior
This only contains hints NOT available from the response. If the server should respond in a certain way (content-type header, status code, etc.), then that will NOT appear in this object. This only fills in ambiguity, not redundant response information.
This only contains data openapi-fetch cares about. Most of your schema is “gone,” and this generated object only contains information for openapi-fetch and nothing else
This is opt-in. (next part)
openapi-fetch addition
From the openapi-fetch side, this would be an opt-in API like so:
import { createClient } from 'openapi-fetch';
import type { paths } from './my-schema';
import runtime from './client-runtime';
const client = createClient<paths>({
baseUrl: 'https://my-api.com/v1/',
+ runtime,
});
Note
Here, too, runtime is just a placeholder name. The name can be decided on later.
It would then do things like handle 204: No Content better, automatically set headers and handle responses, etc. It would solve issues like but not limited to #1883, #2069, #2195, #2017, #1933, and more.
Unknowns / problems
It’s still unclear whether or not this requires the createClient<T> generic to change or not.
If yes, then it’d probably make sense for the --client-runtime output to also augment the new type to provide better data, e.g.:
If no, then this is a cleaner upgrade, but there might be more work involved to try and sniff out how runtime overrides the default <paths> generic.
Either way it seems some additional type inference work is needed, with the tradeoff being backwards compatibility vs library complexity/possibility of conflicting types
Alternatives
openapi-fetch stays zero codegen
In other words, rejecting this proposal. We continue to have issues about requiring boilerplate parseAs, extra headers, and extra handling around openapi-fetch. It results in a simpler library overall, but retains its papercuts (and we keep repeating them until they are well-known).
Full codegen
Requiring full codegen seems like unnecessary overkill, e.g.:
As of now, there are no known advantages to this approach, not when we can shift the API a little and have something opt-in and backwards-compatible. This also greatly increases the chance of bugs, since it’s a completely new, untested code path where an entire runtime is generated (as opposed to a simple object, which has almost no chance of breaking).
Feedback
We’re ideally looking for comments that address:
Problems this would not solve (ideally link to existing issues, or file new ones!)
Unforeseen difficulty this would impose (e.g. “in my setup, this would 10x the code size because X, Y, Z”)
Alternatives not discussed
Bikeshed-y name changes are welcome too 🙂
The text was updated successfully, but these errors were encountered:
Note
This is a Request for Comment, and feedback is welcome! Please read the proposal, and respond with ways in which this would help or hurt the project.
Summary
Blend the best of both worlds: zero-runtime cost performance with code generation, to enhance openapi-fetch to be “smarter” but without incurring the full weight and performance hits of a generated SDK.
Background
openapi-fetch was designed to be a thin wrapper around fetch() in runtime, with all of its type information supplied by types that get baked out of the build. In other words, a typesafe
fetch()
wrapper. But the problem comes in people expecting it to have information about the OpenAPI schema at runtime, which leads to confusion why it can’t simply “figure it out since that information is in my OpenAPI schema.”Historically, this has been more of a “binary” decision tree: either build a heavyweight SDK that is often bloated, and slow, and provides little-to-no runtime benefits. Or build nothing, and let TS do the work.
But I think there’s a compromise between the two where people would be OK sacrificing maybe a couple KB of client weight, and the tiniest performance hit, to handle boilerplate necessary to tell every request exactly what to do. Even better if this is opt-in behavior that’s backwards-compatible.
Changes
This requires 2 parts: a new flag added to the openapi-typescript CLI, and a new option to openapi-fetch
CLI addition
openapi-typescript CLI already generates types for your OpenAPI schema. But adding a new flag would also generate a new, second file to be consumed with a
--client-runtime
flag, e.g.:Note
--client-runtime
is just a placeholder flag name; I expect we’ll come up with a better name for it before shippingThis will then generate a file that has some information like the following (proof-of-concept):
This can all be used to automatically set certain params in openapi-fetch the way generated SDKs do. But in a performant way. The key difference is:
openapi-fetch addition
From the openapi-fetch side, this would be an opt-in API like so:
import { createClient } from 'openapi-fetch'; import type { paths } from './my-schema'; import runtime from './client-runtime'; const client = createClient<paths>({ baseUrl: 'https://my-api.com/v1/', + runtime, });
Note
Here, too,
runtime
is just a placeholder name. The name can be decided on later.It would then do things like handle
204: No Content
better, automatically set headers and handle responses, etc. It would solve issues like but not limited to #1883, #2069, #2195, #2017, #1933, and more.Unknowns / problems
It’s still unclear whether or not this requires the
createClient<T>
generic to change or not.If yes, then it’d probably make sense for the
--client-runtime
output to also augment the new type to provide better data, e.g.:However, this would be a breaking change
If no, then this is a cleaner upgrade, but there might be more work involved to try and sniff out how
runtime
overrides the default<paths>
generic.Either way it seems some additional type inference work is needed, with the tradeoff being backwards compatibility vs library complexity/possibility of conflicting types
Alternatives
openapi-fetch stays zero codegen
In other words, rejecting this proposal. We continue to have issues about requiring boilerplate
parseAs
, extra headers, and extra handling around openapi-fetch. It results in a simpler library overall, but retains its papercuts (and we keep repeating them until they are well-known).Full codegen
Requiring full codegen seems like unnecessary overkill, e.g.:
As of now, there are no known advantages to this approach, not when we can shift the API a little and have something opt-in and backwards-compatible. This also greatly increases the chance of bugs, since it’s a completely new, untested code path where an entire runtime is generated (as opposed to a simple object, which has almost no chance of breaking).
Feedback
We’re ideally looking for comments that address:
The text was updated successfully, but these errors were encountered: