OpenAPITools / openapi-generator

OpenAPI Generator allows generation of API client libraries (SDK generation), server stubs, documentation and configuration automatically given an OpenAPI Spec (v2, v3)
https://openapi-generator.tech
Apache License 2.0
21.31k stars 6.45k forks source link

[REQ] typescript-react-hooks generator #6022

Open abillingsley opened 4 years ago

abillingsley commented 4 years ago

Is your feature request related to a problem? Please describe.

I generally use openapi-generator with typescript-fetch in react applications. Which ends up looking something like or similar depending on the state management strategy being used.

const api = new PetApi();

useEffect(() => {
   const fetch = async () => {
       const pets = await api.findPetsByStatus({ status: [FindPetsByStatusStatusEnum.Available] });
       // do state things
   }
  fetch();
}, [api])

The popularity of the react hooks api has to lead to the creation of libraries like react-query, axios-hooks and others.

Describe the solution you'd like

Similarly to how openapi-generator provides the option to generate typescript-angular, typescript-rxjs, etc. it would be interesting to provide a typescript-react-hooks to enable a number of the same api conveniences for the react ecosystem.

I can imagine a similar api to one provided in libraries like axios-hooks or react-query

const [{ status, data, error }, findPetsByStatus] = usePetApiFindPetsByStatus();

useEffect(() => {
   findPetsByStatus({ status: [FindPetsByStatusStatusEnum.Available] })
}, [findPetsByStatus])

where the response is a tuple is defined something like

type TupleExample = [PromiseResult<Array<Pet>>, (requestParameters: FindPetsByStatusRequest) => Promise<Array<Pet>>];

type PromiseResult<T> = {
   status: 'loading' | 'error' | 'success';
   data: T;
   error: Error | null;
}

Some of the challenges I acknowledge given the example api above would be the configuration of the underlying api class (eg PetApi). The typescript-fetch generated code provides substantial ability to customize fetch through the use of middlewares etc. Perhaps a React Context could assist with the configuration of the specific api classes?

const config = new Configuration({ 
   fetchApi: customFetch,
   basePath: "someOtherBasePath",
   middleware: [...yourMiddlewaresHere]
})

<OpenAPIProvider configuration={config}>
   <YourComponentUsingGeneratedHook />
</OpenAPIProvider>

Describe alternatives you've considered

None, continue with the current strategy or write custom code to wrap open api generated code in hooks manually.

Additional context

amakhrov commented 4 years ago

axios, rxjs, angular are all different http client libraries

react, on the other hand, is view layer, agnostic of http. I don't think it's even feasible to have a generator for every combination of (view, http client). Do you want to use react with axios? or with fetch? or anything else out there?

abillingsley commented 4 years ago

I understand your perspective. Is the issue with the proposed name? Perhaps if instead of proposing the name is typescript-react-hooks we called it typescript-fetch-hooks would this be more agreeable?

I agree that axios and fetch are http clients. I have a hard time seeing rxjs and angular as http clients, rather I see these as libraries or frameworks that among many other things have provided an opinion on http.

I agree that React is a view library but React is an incredibly popular view library in the front end ecosystem. Within the React Ecosystem hooks are becoming the standard way to interact with state, http, etc. From my perspective, it seems reasonable to provide access to the open api client libraries using an api that makes sense to developers since this can only improve the adoption of open api.

I'd argue providing a hooks based api is similar to providing the necessary interfaces to enable Open API with Angular. An Angular Developer could just as easily use the typescript-fetch or typescript-axios generator then provide their own services ontop to enable these lower-level APIs to work within the angular dependency injection system. Currently within the React ecosystem developers who want to leverage hooks with open api need to wrap open api code generated using typescript-fetch or typescript-axios in custom code. Why not provide the same conveniences to React developers that have been provided to the Angular developers?

I 100% agree it would be hard if not impossible to provide generators for React + every http client available but I think starting with the common choices for react developers would be good enough.

ybelenko commented 4 years ago

@abillingsley Hmmm... Why new generator instead of -p useReactHooks=true option to existen TypeScript client?

abillingsley commented 4 years ago

@ybelenko I think it depends on the level of abstraction you want to create. For example, would you also want to support -p useVueHooks=true or similar within the typescript-fetch generator? perhaps. My suggestion in the issue description was based on the convention I thought I was seeing with typescript-[angular|angularjs|aurelia]. It seems the intent is to centralize around http client implementation (axios, fetch, angular's httpclient, etc) in this case I can definitely see an argument for making the hooks abstraction part of the existing generator(s) like typescript-fetch. I think I would need to defer to the openapi team on where to put the generated code for react hooks or similar convenience interfaces.

At the end of the day whether I run

openapi-generator generate -i openapi.json -o ./src --generator-name typescript-fetch-hooks

or

openapi-generator generate -i openapi.json -o ./src --generator-name typescript-fetch -p useReactHooks=true

is inconsequential to me. I think there is a larger architectural decision to be made to determine which approach is "right" for open api in the long term

ybelenko commented 4 years ago

@abillingsley If I remember correct, core team members wants to stick to MVP(minimal valuable product) philosophy in this project. Satisfy TypeScript users and not provide a feature for javascript-client is something that unlikely be done by core team. Of course you can create such generator for yourself and we'll probably merge your PR.

I work with frontend too(vanilla React). Current javascript-client already contains superagent package as http client. Maybe we should enhance javascript-client as a start? If I don't miss something you can use ES6 javascript in TypeScript, right?

abillingsley commented 4 years ago

The important thing for any typescript developer would be to make sure type definitions exist and are correct. If the javascript-client generator exports typescript type definitions then sure I would be willing to give it a try. If the choice were my own I'd prioritize typescript clients over javascript clients because you will satisfy typescript developers by providing accurate typescript types and javascript developers can benefit from the same code once it has been compiled with tsc. Effectively you could imagine a typescript-client which depends on your same superagent dependency. The javascript-client could simply be the typescript-client after it has been compiled

ybelenko commented 4 years ago

I agree with you @abillingsley.

~Btw is typescript-client packed with superagent too?~

Enhancement to javascript/typescript client is interesting while typescript-react-hooks independent generator without Models not so much. Or... if it contains Models then it's just an advanced typescript-client with new feature, not a standalone codegen.

UPD: My bad, just noticed that there are typescript-axios, typescript-fetch etc. So there is no typescript client with superagent http client.

abillingsley commented 4 years ago

I don't think there is a standalone typescript client. For typescript, I've only really seen typescript-fetch and typescript-axios I guess I am suggesting you could introduce a new generator for typescript which is dependant on superagent just like the existing javascript-client generator. Once that is done you could imagine a scenario where the implementation details of the javascript-client generator simply become

  1. generate typescript-client
  2. compile to javascript

doing so would allow open api to maintain a single generator that works for both typescript and javascript equally. The same strategy could be used for any of the javascript/typescript generators that may exist. I'd go further to suggest you may not even need to support javascript-*** generators at all since the typescript ones will just work for javascript developers too but for backward compatibility reasons, it probably makes sense to maintain the javascript generators you have.

I think we are starting to solve a different issue so to refocus on the original problem. If there is interest in perusing a reacts hooks api in either javascript or typescript I'd be willing to give it a try whether that comes as a standalone generator or a feature of an existing one. My personal bias would be towards providing Typescript types since I most often use Typescript

Thanks for the consideration

amakhrov commented 4 years ago

Also note that openapi-generator provides a mean for "light" customization via substituting built-in mustache templates with your own version (via --template-dir). Take, for instance, typescript-fetch. One could override apis.index.mustache to have smth like

import {makeReactHook} from "../your-custom-code/make-react-hook"

{{#operations}}
export * from './{{ classFilename }}';
export const {{classname}}Hook = makeReactHook({{classname}})
{{/operations}}

and implement this makeReactHook in a way that suits your project

abillingsley commented 4 years ago

@amakhrov I am intrigued by your suggestion of overriding certain mustache templates. For your suggestion to work would I need to essentially fork the typescript-fetch templates or is there support for using the default templates and include this extra behavior. While forking the generator is doable I'd like to avoid it if possible

abillingsley commented 4 years ago

I noticed the OpenAPI Roadmap includes "consolidate typescript generators". I also recently came across #802 which has some architectural discussion around the typescript generators. Perhaps including react hooks in that discussion is appropriate?

bodograumann commented 4 years ago

We are working on the generic typescript template. It is currently developed in its own branch. In it we have a framework option. Currently we allow isomorphic-fetch and jquery as values. So it would be possible to add an option react-hooks there. Adding a new separate typescript-react-hooks template is a bad idea, imho, as it leads us further down into the maintenance hell.

denyo commented 4 years ago

I don't know how other people use the output of this generator or any generator in general. However In the projects that me and my team are involved in we always put an abstraction layer in between and never call the generated code directly from a component/controller/thunk/epic/effect or whatever. This way the application stays clean, isn't populated by any generated models or functions and therefore won't need tons of adjustments when the property of a model changes. Also frontend development isn't dependent on an API that isn't ready, yet. Instead we can work with mocked service calls until the API is done and then connect them later.

abillingsley commented 4 years ago

@denyo our team has worked both ways. Rarely if ever do we actually call generated code directly a react component. I'd say we are less worried about exposing OpenAPI models throughout the codebase since we often control the API and tend to have APIs return domain models anyway but the fact HTTP is used is more or less abstracted away.

Providing a custom hook does not preclude the use of additional abstraction layer(s) if that is desired. It can, however, reduce some boilerplate code that you might be otherwise writing.

I think the framework option referenced above gives us a lot of options when it comes to the way applications will consume open api generated code.

If I am using thunks or sagas maybe I continue to generate code with isomorphic-fetch. But the application is lighter weight perhaps I go with the proposed react-hooks abstraction. In either case, I can still write additional code to abstract the open api models etc through the use of yet another custom hook or other strategies.

ybelenko commented 4 years ago

Adding a new separate typescript-react-hooks template is a bad idea, imho, as it leads us further down into the maintenance hell.

I realized recently that it was a main disadvantage of mysql schema generator, now I think database schema decoupled from a server language makes no sense. I would add --storage=mysql option to any server generator instead.

CodySchaaf commented 4 years ago

@amakhrov or @abillingsley Would using the --template-dir option to override the apis.index.mustache files be a good solution if we were hoping to use a 3rd party fetch hook like swr or react-query? Has anyone successfully done that and have an example project?

dantman commented 3 years ago

I'm in a similar scenario. I want to use TypeScript, Axios, and React Hooks together with an OpenAPI spec. I don't mind whether this is via a custom hook that uses the typescript-axios classes or via the useAxios hook (i.e. usePetApiFindPetsByStatus would pass path, etc to useAxios). But I do prefer to use axios and setup proper cancelation and error handling.

I tried following the documentation to override things and add an additional custom hooks.ts with custom hooks that would use the generated classes. But it didn't work.

---
templateDir: generator
files:
  hooks.mustache:
    templateType: SupportingFiles
    destinationFilename: hooks.ts
simPod commented 3 years ago

The API could look similar to what https://www.graphql-code-generator.com/ does

const { status, data, error } = usePetApiFindPetsByStatus();
const [{ status, data, error }, findPetsByStatus] = usePetApiFindPetsByStatusAsync();
melloware commented 2 years ago

I personally would like to see a React Query which is a hugely popular React library for querying and handling state. It is similar to what @simPod posted above syntax.

ybelenko commented 2 years ago

I personally would like to see a React Query which is a hugely popular React library for querying and handling state. It is similar to what @simPod posted above syntax.

There are dozens of tools. I used RTK Query shipped with @reduxjs/toolkit recently and really happy about it.

melloware commented 2 years ago

@ybelenko I understand that but what I am asking for is I have OpenAPI v3 JSON file I want to convert all of it to React Query and not native TypeScript Fetch and then have to wrap all my calls in React Query. I don't see how RTK Query is any different?

ybelenko commented 2 years ago

@ybelenko I understand that but what I am asking for is I have OpenAPI v3 JSON file I want to convert all of it to React Query and not native TypeScript Fetch and then have to wrap all my calls in React Query. I don't see how RTK Query is any different?

They got their own generator to convert openapi3 spec to RTK Query endpoints, 😆 . Another one. Didn't use it, since my spec was really small and I wrote all requests for an hour.

melloware commented 2 years ago

Ahh I have huge API's don't want my developers hand coding all these hooks. And I have been using this generator: https://orval.dev/ It does the job but its not as nice as openapi-generator or as active

mikemissionplus commented 2 years ago

any updates?