Code Monkey home page Code Monkey logo

openapi-react-query-codegen's Introduction

OpenAPI React Query Codegen

Node.js library that generates React Query (also called TanStack Query) hooks based on an OpenAPI specification file.

npm version

Features

  • Generates custom react hooks that use React Query's useQuery, useSuspenseQuery, useMutation and useInfiniteQuery hooks
  • Generates query keys and functions for query caching
  • Generates pure TypeScript clients generated by @hey-api/openapi-ts

Installation

$ npm install -D @7nohe/openapi-react-query-codegen

Register the command to the scripts property in your package.json file.

{
  "scripts": {
    "codegen": "openapi-rq -i ./petstore.yaml -c axios"
  }
}

You can also run the command without installing it in your project using the npx command.

$ npx --package @7nohe/openapi-react-query-codegen openapi-rq -i ./petstore.yaml -c axios

Usage

$ openapi-rq --help

Usage: openapi-rq [options]

Generate React Query code based on OpenAPI

Options:
  -V, --version              output the version number
  -i, --input <value>        OpenAPI specification, can be a path, url or string content (required)
  -o, --output <value>       Output directory (default: "openapi")
  -c, --client <value>       HTTP client to generate (choices: "angular", "axios", "fetch", "node", "xhr", default: "fetch")
  --request <value>          Path to custom request file
  --format <value>           Process output folder with formatter? (choices: "biome", "prettier")
  --lint <value>             Process output folder with linter? (choices: "biome", "eslint")
  --operationId              Use operation ID to generate operation names?
  --serviceResponse <value>  Define shape of returned value from service calls (choices: "body", "response", default: "body")
  --base <value>             Manually set base in OpenAPI config instead of inferring from server value
  --enums <value>            Generate JavaScript objects from enum definitions? ['javascript', 'typescript', 'typescript+namespace']
  --enums <value>            Generate JavaScript objects from enum definitions? (choices: "javascript", "typescript")
  --useDateType              Use Date type instead of string for date types for models, this will not convert the data to a Date object
  --debug                    Run in debug mode?
  --noSchemas                Disable generating JSON schemas
  --schemaType <value>       Type of JSON schema [Default: 'json'] (choices: "form", "json")
  --pageParam <value>        Name of the query parameter used for pagination (default: "page")
  --nextPageParam <value>    Name of the response parameter used for next page (default: "nextPage")
  -h, --help                 display help for command

Example Usage

Command

$ openapi-rq -i ./petstore.yaml

Output directory structure

- openapi
  - queries
    - index.ts <- main file that exports common types, variables, and queries. Does not export suspense or prefetch hooks
    - common.ts <- common types
    - queries.ts <- generated query hooks
    - suspenses.ts <- generated suspense hooks
    - prefetch.ts <- generated prefetch hooks learn more about prefetching in in link below
  - requests <- output code generated by @hey-api/openapi-ts

In your app

Using the generated hooks
// App.tsx
import { usePetServiceFindPetsByStatus } from "../openapi/queries";
function App() {
  const { data } = usePetServiceFindPetsByStatus({ status: ["available"] });

  return (
    <div className="App">
      <h1>Pet List</h1>
      <ul>{data?.map((pet) => <li key={pet.id}>{pet.name}</li>)}</ul>
    </div>
  );
}

export default App;
Using the generated typescript client
import { useQuery } from "@tanstack/react-query";
import { PetService } from "../openapi/requests/services";
import { usePetServiceFindPetsByStatusKey } from "../openapi/queries";

function App() {
  // You can still use the auto-generated query key
  const { data } = useQuery({
    queryKey: [usePetServiceFindPetsByStatusKey],
    queryFn: () => {
      // Do something here
      return PetService.findPetsByStatus(["available"]);
    },
  });

  return <div className="App">{/* .... */}</div>;
}

export default App;
Using Suspense Hooks
// App.tsx
import { useDefaultClientFindPetsSuspense } from "../openapi/queries/suspense";
function ChildComponent() {
  const { data } = useDefaultClientFindPetsSuspense({ tags: [], limit: 10 });

  return <ul>{data?.map((pet, index) => <li key={pet.id}>{pet.name}</li>)}</ul>;
}

function ParentComponent() {
  return (
    <>
      <Suspense fallback={<>loading...</>}>
        <ChildComponent />
      </Suspense>
    </>
  );
}

function App() {
  return (
    <div className="App">
      <h1>Pet List</h1>
      <ParentComponent />
    </div>
  );
}

export default App;
Using Mutation hooks
// App.tsx
import { usePetServiceAddPet } from "../openapi/queries";

function App() {
  const { mutate } = usePetServiceAddPet();

  const handleAddPet = () => {
    mutate({ name: "Fluffy", status: "available" });
  };

  return (
    <div className="App">
      <h1>Add Pet</h1>
      <button onClick={handleAddPet}>Add Pet</button>
    </div>
  );
}

export default App;
Invalidating queries after mutation

Invalidating queries after a mutation is important to ensure the cache is updated with the new data. This is done by calling the queryClient.invalidateQueries function with the query key used by the query hook.

Learn more about invalidating queries here.

To ensure the query key is created the same way as the query hook, you can use the query key function exported by the generated query hooks.

import {
  usePetServiceFindPetsByStatus,
  usePetServiceAddPet,
  UsePetServiceFindPetsByStatusKeyFn,
} from "../openapi/queries";

// App.tsx
function App() {
  const [status, setStatus] = React.useState(["available"]);
  const { data } = usePetServiceFindPetsByStatus({ status });
  const { mutate } = usePetServiceAddPet({
    onSuccess: () => {
      queryClient.invalidateQueries({
        // Call the query key function to get the query key
        // This is important to ensure the query key is created the same way as the query hook
        // This insures the cache is invalidated correctly and is typed correctly
        queryKey: [UsePetServiceFindPetsByStatusKeyFn({
          status
        })],
      });
    },
  });

  return (
    <div className="App">
      <h1>Pet List</h1>
      <ul>{data?.map((pet) => <li key={pet.id}>{pet.name}</li>)}</ul>
      <button
        onClick={() => {
          mutate({ name: "Fluffy", status: "available" });
        }}
      >
        Add Pet
      </button>
    </div>
  );
}

export default App;
Using Infinite Query hooks

This feature will generate a function in infiniteQueries.ts when the name specified by the pageParam option exists in the query parameters and the name specified by the nextPageParam option exists in the response.

Example Schema:

paths:
  /paginated-pets:
    get:
      description: |
        Returns paginated pets from the system that the user has access to
      operationId: findPaginatedPets
      parameters:
        - name: page
          in: query
          description: page number
          required: false
          schema:
            type: integer
            format: int32
        - name: tags
          in: query
          description: tags to filter by
          required: false
          style: form
          schema:
            type: array
            items:
              type: string
        - name: limit
          in: query
          description: maximum number of results to return
          required: false
          schema:
            type: integer
            format: int32
      responses:
        '200':
          description: pet response
          content:
            application/json:
              schema:
                type: object
                properties:
                  pets:
                    type: array
                    items:
                      $ref: '#/components/schemas/Pet'
                  nextPage: 
                    type: integer
                    format: int32
                    minimum: 1

Usage of Generated Hooks:

import { useDefaultServiceFindPaginatedPetsInfinite } from "@/openapi/queries/infiniteQueries";

const { data, fetchNextPage } = useDefaultServiceFindPaginatedPetsInfinite({
  limit: 10,
  tags: [],
});
Runtime Configuration

You can modify the default values used by the generated service calls by modifying the OpenAPI configuration singleton object.

It's default location is openapi/requests/core/OpenAPI.ts and it is also exported from openapi/index.ts

Import the constant into your runtime and modify it before setting up the react app.

/** main.tsx */
import { OpenAPI as OpenAPIConfig } from './openapi/requests/core/OpenAPI';
...
OpenAPIConfig.BASE = 'www.domain.com/api';
OpenAPIConfig.HEADERS = {
  'x-header-1': 'value-1',
  'x-header-2': 'value-2',
};
...
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
  <React.StrictMode>
    <QueryClientProvider client={queryClient}>
      <App />
    </QueryClientProvider>
  </React.StrictMode>
);

Development

Install dependencies

pnpm install

Run tests

pnpm test

Run linter

pnpm lint

Run linter and fix

pnpm lint:fix

Update snapshots

pnpm snapshot

Build example and validate generated code

npm run build && pnpm --filter @7nohe/react-app generate:api && pnpm --filter @7nohe/react-app test:generated 

License

MIT

openapi-react-query-codegen's People

Contributors

7nohe avatar ali4heydari avatar azproduction avatar collink avatar corepex avatar jamesnw avatar jtree03 avatar luskin avatar mmurto avatar nopitown avatar seike460 avatar seriouslag avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

openapi-react-query-codegen's Issues

Dynamic base url using env vars

Is your feature request related to a problem? Please describe.
Currently, I'm unable to use a dynamic base for the URLs. In production webs, urls might differ from those in dev environments. I saw that we have a --base option, but it seems that only a string can be sent.

Describe the solution you'd like
Make --base more flexible so it can also accept env vars. This is currently possible if we modify the OpenAPI config

Describe alternatives you've considered
I also considered using the new interceptor feature, but it isn't possible to modify the URL.

Additional context
This feature would be really good in order to be able to prefetch queries in server components as fetch calls doesn't accept relative paths.

Add support for older versions of react query

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

The product I'm working on is integrating in a federation of React components. See https://github.com/openshift/dynamic-plugin-sdk Which means that I can't chose the version of React I'm working with. Right now OpenShift console runs React 17. In turn this means that we have to use react-query at version 4.

Describe the solution you'd like

I'd love the codegen executable to accept a parameter to specify which version of react-query to use for the generation.

Describe alternatives you've considered

ATM, to overcome this issue I'm running your codegen solution in it's version 0.4.1. While it's been working great, it also means that I would miss on any new cool features you are implementing.

V1 ommits put and delete requests

Here are some examples. The last one is not being generated.

"/api/v1/Catalogs/GetByStatus/{status}": {
      "get": {
        "tags": [
          "Catalogs"
        ],
        "summary": "Returns catalogs by status. (Auth)",
        "operationId": "GetCatalogsByStatus",
        "parameters": [
          {
            "name": "status",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-DB-Catalog",
            "in": "header",
            "description": "Database catalog",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Catalogs"
                  }
                }
              }
            }
          },
          "400": {
            "description": "No results found."
          },
          "404": {
            "description": "An error occurred while executing GetByStatus"
          }
        }
      }
    },
    "/api/v1/Catalogs/GetByDescription/{description}": {
      "get": {
        "tags": [
          "Catalogs"
        ],
        "summary": "Returns catalogs by description. (Auth)",
        "operationId": "GetCatalogsByDescription",
        "parameters": [
          {
            "name": "description",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-DB-Catalog",
            "in": "header",
            "description": "Database catalog",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Catalogs"
                  }
                }
              }
            }
          },
          "400": {
            "description": "No results found."
          },
          "404": {
            "description": "An error occurred while executing GetByStatus"
          }
        }
      }
    },
    "/api/v1/Catalogs/GroupByColumn/{column}": {
      "get": {
        "tags": [
          "Catalogs"
        ],
        "summary": "Returns catalogs by column. (Auth)",
        "operationId": "GetCatalogsGroupByColumn",
        "parameters": [
          {
            "name": "column",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-DB-Catalog",
            "in": "header",
            "description": "Database catalog",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Catalogs"
                  }
                }
              }
            }
          },
          "400": {
            "description": "No results found."
          },
          "404": {
            "description": "An error occurred while executing GroupByColumn"
          }
        }
      }
    },
    "/api/v1/Catalogs/{type}": {
      "get": {
        "tags": [
          "Catalogs"
        ],
        "summary": "Returns catalogs by type. (Auth)",
        "operationId": "GetCatalogs",
        "parameters": [
          {
            "name": "type",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-DB-Catalog",
            "in": "header",
            "description": "Database catalog",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Catalogs"
                  }
                }
              }
            }
          },
          "400": {
            "description": "No results found."
          },
          "404": {
            "description": "An error occurred while executing GetCatalogs"
          }
        }
      }
    },
    "/api/v1/Catalogs": {
      "post": {
        "tags": [
          "Catalogs"
        ],
        "summary": "Create a new catalog. (Auth)",
        "operationId": "PostCatalogs",
        "parameters": [
          {
            "name": "X-DB-Catalog",
            "in": "header",
            "description": "Database catalog",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Catalogs"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Catalog created succesfully."
          },
          "400": {
            "description": "No results found."
          },
          "404": {
            "description": "An error occurred while executing PostCatalogs"
          }
        }
      }
    },
    "/api/v1/Catalogs/{sequential}": {
      "put": {
        "tags": [
          "Catalogs"
        ],
        "summary": "Update an existing catalog. (Auth)",
        "operationId": "PutCatalogs",
        "parameters": [
          {
            "name": "sequential",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "X-DB-Catalog",
            "in": "header",
            "description": "Database catalog",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Catalogs"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Catalog updated succesfully."
          },
          "400": {
            "description": "No results found."
          },
          "404": {
            "description": "An error occurred while executing PutCatalogs"
          }
        }
      },
      "delete": {
        "tags": [
          "Catalogs"
        ],
        "summary": "Delete an existing catalog. (Auth)",
        "operationId": "DeleteCatalogs",
        "parameters": [
          {
            "name": "sequential",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "X-DB-Catalog",
            "in": "header",
            "description": "Database catalog",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Catalog deleted succesfully."
          },
          "400": {
            "description": "No results found."
          },
          "404": {
            "description": "An error occurred while executing DeleteCatalogs"
          }
        }
      }
    },

queries.ts output

export const useCatalogsServiceGetCatalogsByStatus = <TData = Common.CatalogsServiceGetCatalogsByStatusDefaultResponse, TError = unknown, TQueryKey extends Array<unknown> = unknown[]>({ status, xDbCatalog }: {
    status: string;
    xDbCatalog: string;
}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>, "queryKey" | "queryFn" | "initialData">) => useQuery<TData, TError>({ queryKey: [Common.useCatalogsServiceGetCatalogsByStatusKey, ...(queryKey ?? [{ status, xDbCatalog }])], queryFn: () => CatalogsService.getCatalogsByStatus({ status, xDbCatalog }) as TData, ...options });
export const useCatalogsServiceGetCatalogsByDescription = <TData = Common.CatalogsServiceGetCatalogsByDescriptionDefaultResponse, TError = unknown, TQueryKey extends Array<unknown> = unknown[]>({ description, xDbCatalog }: {
    description: string;
    xDbCatalog: string;
}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>, "queryKey" | "queryFn" | "initialData">) => useQuery<TData, TError>({ queryKey: [Common.useCatalogsServiceGetCatalogsByDescriptionKey, ...(queryKey ?? [{ description, xDbCatalog }])], queryFn: () => CatalogsService.getCatalogsByDescription({ description, xDbCatalog }) as TData, ...options });
export const useCatalogsServiceGetCatalogsGroupByColumn = <TData = Common.CatalogsServiceGetCatalogsGroupByColumnDefaultResponse, TError = unknown, TQueryKey extends Array<unknown> = unknown[]>({ column, xDbCatalog }: {
    column: string;
    xDbCatalog: string;
}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>, "queryKey" | "queryFn" | "initialData">) => useQuery<TData, TError>({ queryKey: [Common.useCatalogsServiceGetCatalogsGroupByColumnKey, ...(queryKey ?? [{ column, xDbCatalog }])], queryFn: () => CatalogsService.getCatalogsGroupByColumn({ column, xDbCatalog }) as TData, ...options });
export const useCatalogsServiceGetCatalogs = <TData = Common.CatalogsServiceGetCatalogsDefaultResponse, TError = unknown, TQueryKey extends Array<unknown> = unknown[]>({ type, xDbCatalog }: {
    type: string;
    xDbCatalog: string;
}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>, "queryKey" | "queryFn" | "initialData">) => useQuery<TData, TError>({ queryKey: [Common.useCatalogsServiceGetCatalogsKey, ...(queryKey ?? [{ type, xDbCatalog }])], queryFn: () => CatalogsService.getCatalogs({ type, xDbCatalog }) as TData, ...options });
export const useCatalogsServicePostCatalogs = <TData = Common.CatalogsServicePostCatalogsMutationResult, TError = unknown, TContext = unknown>(options?: Omit<UseMutationOptions<TData, TError, {
    requestBody?: Catalogs;
    xDbCatalog: string;
}, TContext>, "mutationFn">) => useMutation<TData, TError, {
    requestBody?: Catalogs;
    xDbCatalog: string;
}, TContext>({ mutationFn: ({ requestBody, xDbCatalog }) => CatalogsService.postCatalogs({ requestBody, xDbCatalog }) as unknown as Promise<TData>, ...options });

Issues with version 1.0.1 release

There is a broken path to the bin executable in the version 1.0.1 release.

Here openapi-react-query-codegen

The package.json bin should point to dist/cli.mjs instead of dist/cli.js

Default empty arguments

It would be nice if the arguments for the generated hooks had a default value if all the properties of the argument can be undefined.

Example of generated hook as is:

export const useCatsServiceGetCats = <
  TData = CatServiceGetCatsDefaultResponse,
  TError = unknown,
  TQueryKey extends Array<unknown> = unknown[]
>(
  {
    color,
    age,
  }: {
    color?: string;
    age?: number;
  },
  queryKey?: TQueryKey,
  options?: Omit<
    UseQueryOptions<TData, TError>,
    "queryKey" | "queryFn" | "initialData"
  >
) =>
  useQuery<TData, TError>({
    queryKey: [
      useCatsServiceGetCatssKey,
      ...(queryKey ?? [{ color, age }]),
    ],
    queryFn: () =>
      CatsService.getCats(color, age) as TData,
    ...options,
  });
  
 // usage - need to pass empty object at least
 const {data} = useCatsServiceGetCats({});

Example of proposed generated code:

export const useCatsServiceGetCats = <
  TData = CatServiceGetCatsDefaultResponse,
  TError = unknown,
  TQueryKey extends Array<unknown> = unknown[]
>(
  {
    color,
    age,
  }: {
    color?: string;
    age?: number;
  } = {}, // this is defaulted since all properties can be undefined
  queryKey?: TQueryKey,
  options?: Omit<
    UseQueryOptions<TData, TError>,
    "queryKey" | "queryFn" | "initialData"
  >
) =>
  useQuery<TData, TError>({
    queryKey: [
      useCatsServiceGetCatssKey,
      ...(queryKey ?? [{ color, age }]),
    ],
    queryFn: () =>
      CatsService.getCats(color, age) as TData,
    ...options,
  });
 
 // usage - no need to pass empty object
const {data} = useCatsServiceGetCats();

Schemas that have a name with a '-' character in them cause invalid code to be generated

eg:

"Schema-Name": {
  "id": {
         "type": "integer",
          "title": "Id"
    }
   ...
}

Will generate code that looks like this:

export const $Schema-Name= {
    properties: {
        id: {
            type: 'integer',
            title: 'Id'
        }
        ...
     }

Dashes are not valid characters for javascript variable names, but are valid strings for json keys.

Not sure the best way to deal with this, maybe export an object instead, mangle to a _ or just explicitly not support having - in schema names and add a test for that.

Change OpenAPI generator under the hood

Hi,

First of all I want to thank you for creating this great lib, it saved me a lot of hours of development.
I just wanted to ask if you considered swapping the OpenAPI generator under the hood, or if there is any way it can be done in the client implementation. Reason is, my API is using the discriminator mapping feature and currently the typescript generator does not support it (ferdikoomen/openapi-typescript-codegen#998 (comment))

I was looking at https://github.com/OpenAPITools/openapi-generator-cli and they seem to have it.

Thanks for your help

Question: How can I send parameters to the mutations ?

The generated code creates a mutation for me:

export const useSecurityServiceLogin = (
  options?: Omit<
    UseMutationOptions<
      Awaited<ReturnType<typeof SecurityService.login>>,
      unknown,
      {
        requestBody: {
          brokerName: string;
          userName: string;
          password: string;
          jolokiaHost: string;
          scheme: string;
          port: string;
        };
      },
      unknown
    >,
    'mutationFn'
  >,
) =>
  useMutation(({ requestBody }) => SecurityService.login(requestBody), options);

However, I don't know how to use it, because if in the code I'm doing something like:

  const { data: dataToken, isSucces: tokenSuccess } = useSecurityServiceLogin({
    options: {
      brokerName: getBrokerKey(broker, ordinal),
      userName: userName,
      password: password,
      jolokiaHost: jolokiaHost,
      port: jolokiaPort,
      scheme: protocol,
    },
  });

I'm getting errors from the linter

image

I can make it work like this:

  const { data, isSuccess, isLoading } = useQuery({
    queryKey: ['useSecurityServiceLogin'],
    queryFn: () => {
      return SecurityService.login({
        brokerName: getBrokerKey(broker, ordinal),
        userName: userName,
        password: password,
        jolokiaHost: jolokiaHost,
        port: jolokiaPort,
        scheme: protocol,
      });
    },
    enabled: fireRequest,
    onSuccess: () => {
      const receivedToken = isSuccess ? data['jolokia-session-id'] : '';
      sessionStorage.setItem(getBrokerKey(broker, ordinal), receivedToken);
    },
  });

But I would prefer to use the mutation hook. Any idea on what I'm doing wrong?

Thanks

Not working with NextJS

Hi,

Any idea why the example is not working with NextJSs pages router?

I see the network call and the browser is receiving the data but nothing renders.

Thanks

Prefetch hook keys aren't properly set

Describe the bug
I started using the prefetch hooks introduced a few days ago, but the dynamic key is hardcoded, making it challenging to use the prefetched data because the keys do not match.

To Reproduce
Just generate the hooks and check the prefetch code.

Here is an example of the code generated:

export const prefetchUseFooServiceGetBar = (queryClient: QueryClient) =>
  queryClient.prefetchQuery({
    queryKey: [Common.useUseFooServiceGetBarKey, []],
    queryFn: () => FooService.getBar(),
  });

OpenAPI spec file
N/A

Expected behavior
Similar to a query hook, the keys must be dynamic so we can tell React Query what key should be used for caching:

export const prefetchUseFooServiceGetBar = (queryClient: QueryClient, queryKey?: TQueryKey) =>
  queryClient.prefetchQuery({
    queryKey: Common.useUseFooServiceGetBarKeyFn(queryKey),
    queryFn: () => FooService.getBar(),
  });

Screenshots

  • OS: MacOS
  • Version: 14.4.1

Additional context
N/A

Sample Request Generation

Hi, I really like this project, but I have an issue with the sample request on the example project.

It gets a bit confusing on how it works:

  1.  So based on the example project, you would need types like onCancel, OpenAPIConfig meaning you have to run the codegen first before you can even access the types
  2. The Sample request.ts file differs from the  request file generated from the latest version of the app
  3. if I were to use my own request but just want to change the baseUrl and Header, I would need to either update the path of the types, which would cause the generated request.ts file inside the core folder to call a path that doesn't exist, if I were to put my custom request inside the code folder it will be deleted upon the code gen.

I want to ask how the request generation should work or am I using it incorrectly?

InitialData Omitted from queries.

options?: Omit<UseQueryOptions<TData, TError>, "queryKey" | "queryFn" | "initialData">

I have a usecase where initialData from tanStack would be nice to have.
But the generated queries handles queryKey & queryFn, but initialData is not usable?
I have a workaround, but i was wondering why it is. And if there is a recommended way of handling "initialData"

"error TS6133: 'queryKey' is declared but its value is never read" from queries.ts and suspense.ts

Describe the bug
Files queries/queries.ts and queries/suspense.ts generate TS errors from queryKey being declared but its value never read, resulting in error TS6133 when building a project with the API.

OpenAPI JSON causing the bug
api.json

Expected behavior
Either don't generate unused parameters, or auto-insert

// @ts-nocheck

to generated files which may declare unused variables.

  • OS: Ubuntu 22.04
  • Version 1.3.0

Add support for generating `useInfiniteQuery` custom hooks

Is your feature request related to a problem? Please describe.
Currently, this library does not support generating custom hooks for useInfiniteQuery. This limitation makes it difficult to handle APIs that require pagination or infinite scrolling, leading to additional manual coding and reduced efficiency.

Describe the solution you'd like
I would like the library to support the generation of useInfiniteQuery custom hooks directly from OpenAPI schemas. This feature should automatically create hooks that handle pagination parameters and fetch data incrementally as needed, making it easier for developers to integrate infinite scrolling functionality into their applications. Specifically, the solution would involve adding CLI options --next-page-param and --page-param to specify the relevant parameters. If the API has query parameters and responses that match these options, the library should automatically generate useInfiniteQuery custom hooks.

Here is an example of a generated custom hook:

export const useInfiniteDefaultServiceFindPets = <
  TData = Common.DefaultServiceFindPetsDefaultResponse,
  TError = unknown,
  TQueryKey extends Array<unknown> = unknown[]
>(
  {
    limit,
    tags,
  }: {
    limit?: number;
    tags?: string[];
  } = {},
  queryKey?: TQueryKey,
  options?: Omit<UseInfiniteQueryOptions<TData, TError>, "queryKey" | "queryFn">
) =>
  useInfiniteQuery({
    queryKey: Common.UseDefaultServiceFindPetsKeyFn({ limit, tags }, queryKey),
    queryFn: (({ pageParam }) => DefaultService.findPets({ limit, tags, page: pageParam as number })) as QueryFunction<TData, QueryKey, unknown>,
    initialPageParam: 1,
    getNextPageParam: (response) => (response as { nextPage: number }).nextPage,
    ...options,
  });

Additional context
Here is the documentation for useInfiniteQuery for reference.

How can I specify the server hostname on the fly for the generated requests?

Hello,

I'm looking to use react-query and the code gen for a project. I'd like to be able to specify on the fly in my React component the host that would receive the query. In my use case I have several machines that could potentially receive the queries I want to send.

This is my request for now:

  const { data, isError } = useQuery({
    queryKey: [useSecurityServiceLogin],
    queryFn: () => {
      return SecurityService.login({
        formData: {
          brokerName: getBrokerKey(broker, ordinal),
          userName: userName,
          password: password,
          jolokiaHost: jolokiaHost,
          port: jolokiaPort,
          scheme: protocol,
        },
      });
    },
  });

  return [!isError, data['jolokia-session-id']];

And the generated SecurityService class:

export class SecurityService {
    /**
     * The login api
     * This api is used to login to a jolokia endpoint. It tries to get the broker mbean via the joloia url using the parameters passed in.
     *
     * If it succeeds, it generates a [jwt token](https://jwt.io/introduction) and returns
     * it back to the client. If it fails it returns a error.
     *
     * Once authenticated, the client can access the
     * apis defined in this file. With each request the client must include a valid jwt token in a http header named `jolokia-session-id`. The api-server will validate the token before processing a request is and rejects the request if the token is not valid.
     *
     * @param data The data for the request.
     * @param data.formData
     * @returns SimpleResponse Success
     * @throws ApiError
     */
    public static login(data: $OpenApiTs['/jolokia/login']['post']['req']): CancelablePromise<$OpenApiTs['/jolokia/login']['post']['res'][200]> {
        return __request(OpenAPI, {
            method: 'POST',
            url: '/jolokia/login',
            formData: data.formData,
            mediaType: 'application/x-www-form-urlencoded'
        });
    }

}

How can I put in ther the hostname and the port ?

Thanks.

Not able to run the codegen command

Using the command provided in the readme gives me the following error:

$ npx --package @7nohe/openapi-react-query-codegen openapi-rq -i ./spec.yaml -c fetch

test/frontend/node_modules/commander/index.js:638
  var normalized = this.normalize(argv.slice(2));
                                       ^

TypeError: Cannot read properties of undefined (reading 'slice')
    at Command.parse (test/frontend/node_modules/commander/index.js:638:40)
    at Object.<anonymous> (test/frontend/node_modules/@7nohe/openapi-react-query-codegen/dist/src/cli.js:24:6)
    at Module._compile (node:internal/modules/cjs/loader:1368:14)
    at Module._extensions..js (node:internal/modules/cjs/loader:1426:10)
    at Module.load (node:internal/modules/cjs/loader:1205:32)
    at Module._load (node:internal/modules/cjs/loader:1021:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:142:12)
    at node:internal/main/run_main_module:28:49

Node.js v21.7.1

Enums flag does not work?

Hi I'm trying to generate the a enum from the following swagger/openapi definition

"Test": {
  "enum": [
    "Start",
    "Step1",
    "Step2",
    "Step3",
    "Step4"
  ],
  "type": "string"
},

But even when i use the --enums flag it generates a type instead?
export type Test = 'Start' | 'Step1' | 'Step2' | 'Step3' | 'Step4';
I was expecting something like

export enum Test {
  START = 'Start',
  STEP1 = 'Step1',
  ....
}

I'm running the following command: cross-env NODE_TLS_REJECT_UNAUTHORIZED=0 openapi-rq --enums -i https://localhost:44353/swagger/v1/swagger.json -o src/openapi/

Create a test and example using enums.

Is your feature request related to a problem? Please describe.
There are consumers of this library that use enums, we have no tests or examples showing their generation/usage.

Describe the solution you'd like
A unit test or snapshot showing their creation.

Describe alternatives you've considered
None,

Do not override requests/core/OpenApi.ts if it already exists

Do not override requests/core/OpenApi.ts if it already exists

Since we set the OpenAPIConfig there, each time you re-generate from the swagger docs, thise file is overriden.

Wouldn't it make more sense to not override this file if it already exists?

That way the user can decide by themselves to delete the file before regenerating if they want that file to be regenerated. eg:

rm -rf src/generated && openapi-rq -i http://localhost:55577/docs-yaml -o ./src/generated/api

also, wouldn't it make sense to set the BASE config option to be the same as the -i option from the generate command by default? eg:

export const OpenAPI: OpenAPIConfig = {
  BASE: 'http://localhost:55577',
  VERSION: '0.0.1',
  WITH_CREDENTIALS: false,
  CREDENTIALS: 'include',
  TOKEN: undefined,
  USERNAME: undefined,
  PASSWORD: undefined,
  HEADERS: undefined,
  ENCODE_PATH: undefined,
};

How to set BasePath, Headers

How to Set BaseUrl, Headers,

At the moment when i generate the files again, it overrides the old one

Which docs should i follow ?

Edit : its working , there was a request file option in cli, i can create my seperate file with all the headers , and config and generate via cli

Enums are suffixed with Enum?

Describe the bug
All enums that are generated now end with Enum this was not the case before.
Are there any flags to disable this?

To Reproduce
Steps to reproduce the behavior:

  • Expose a enum in the OpenAPI specification

Expected behavior
To not have Enum as an suffix.

  • OS: Windows 11
  • Version 1.2.0

queries not working after upgrading from 0.5.3 to 1.0.6

Very cool repo! Love the work that's being done.

I have an issue with version 1.0 when trying to codegen for a very simple OpenAPI Spec.

When doing codegen with v0.5.3, I get content in my generated index.ts (eg: useDefaultServiceGetProjects)
However, when doing codegen with v1.0.6, none of the files contain the above hook.

My spec is generated with tsoa and valid according to https://editor.swagger.io/

Is something missing from my spec, or is there a bug in this repo?

Here's the spec

openapi: 3.0.0
components:
  examples: {}
  headers: {}
  parameters: {}
  requestBodies: {}
  responses: {}
  schemas:
    Project:
      properties:
        id:
          type: string
        name:
          type: string
        description:
          type: string
      required:
        - id
        - name
        - description
      type: object
      additionalProperties: false
    ProjectCreateParams:
      properties:
        name:
          type: string
      required:
        - name
      type: object
      additionalProperties: false
  securitySchemes: {}
info:
  title: projects-api
  version: 1.0.0
  description: Projects backend for frontend
  license:
    name: ISC
  contact: {}
paths:
  /v1/projects:
    get:
      operationId: GetProjects
      responses:
        "200":
          description: Ok
          content:
            application/json:
              schema:
                items:
                  $ref: "#/components/schemas/Project"
                type: array
      security: []
      parameters: []
    post:
      operationId: CreateProject
      responses:
        "201":
          description: Created
      security: []
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProjectCreateParams"
  /v1/projects/{id}:
    get:
      operationId: GetProject
      responses:
        "200":
          description: Ok
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Project"
      security: []
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
servers:
  - url: /

Generating code and running TSC errors with TS2742

Describe the bug
When I run TSC after running codegen on a simple open api spec, I get error TS2742. It seems that this lib is missing some typings in the generated code.

To Reproduce
Steps to reproduce the behavior:

Generated code with the attached swagger file, and run TSC on it.

OpenAPI spec file
If possible, please upload the OpenAPI spec file.

{
	"openapi": "3.0.0",
	"components": {
		"examples": {},
		"headers": {},
		"parameters": {},
		"requestBodies": {},
		"responses": {},
		"schemas": {
			"UUID": {
				"type": "string",
				"description": "Stringified UUIDv4.\nSee [RFC 4112](https://tools.ietf.org/html/rfc4122)"
			},
			"Project": {
				"properties": {
					"shared_with_client": {
						"type": "boolean"
					},
					"updated_by": {
						"$ref": "#/components/schemas/UUID"
					},
					"last_updated": {
						"type": "string"
					},
					"date_created": {
						"type": "string"
					},
					"name": {
						"type": "string"
					},
					"id": {
						"$ref": "#/components/schemas/UUID"
					}
				},
				"required": [
					"shared_with_client",
					"updated_by",
					"last_updated",
					"date_created",
					"name",
					"id"
				],
				"type": "object"
			},
			"ApiInternalError": {
				"properties": {
					"message": {
						"type": "string"
					}
				},
				"required": [
					"message"
				],
				"type": "object"
			},
			"ApiResponseListMeta": {
				"properties": {
					"total": {
						"type": "number",
						"format": "double"
					}
				},
				"required": [
					"total"
				],
				"type": "object",
				"additionalProperties": false
			},
			"ApiResponseList_Project_": {
				"properties": {
					"data": {
						"items": {
							"$ref": "#/components/schemas/Project"
						},
						"type": "array"
					},
					"errors": {
						"items": {
							"$ref": "#/components/schemas/ApiInternalError"
						},
						"type": "array"
					},
					"meta": {
						"$ref": "#/components/schemas/ApiResponseListMeta"
					}
				},
				"required": [
					"data",
					"errors"
				],
				"type": "object",
				"additionalProperties": false
			},
			"ApiResponse_Project_": {
				"properties": {
					"data": {
						"$ref": "#/components/schemas/Project"
					},
					"errors": {
						"items": {
							"$ref": "#/components/schemas/ApiInternalError"
						},
						"type": "array"
					},
					"meta": {
						"type": "number",
						"enum": [
							null
						],
						"nullable": true
					}
				},
				"required": [
					"data",
					"errors"
				],
				"type": "object",
				"additionalProperties": false
			},
			"ProjectCreateParams": {
				"properties": {
					"name": {
						"type": "string"
					}
				},
				"required": [
					"name"
				],
				"type": "object",
				"additionalProperties": false
			}
		},
		"securitySchemes": {
			"bearerAuth": {
				"type": "http",
				"scheme": "bearer",
				"bearerFormat": "JWT"
			}
		}
	},
	"info": {
		"title": "projects-api",
		"version": "1.0.0",
		"description": "Projects backend for frontend",
		"license": {
			"name": "ISC"
		},
		"contact": {}
	},
	"paths": {
		"/v1/projects": {
			"get": {
				"operationId": "GetProjects",
				"responses": {
					"200": {
						"description": "Ok",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiResponseList_Project_"
								}
							}
						}
					}
				},
				"security": [
					{
						"bearerAuth": []
					}
				],
				"parameters": []
			},
			"post": {
				"operationId": "CreateProject",
				"responses": {
					"201": {
						"description": "Created",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiResponse_Project_"
								}
							}
						}
					}
				},
				"security": [
					{
						"bearerAuth": []
					}
				],
				"parameters": [],
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/ProjectCreateParams"
							}
						}
					}
				}
			}
		}
	},
	"servers": [
		{
			"url": "/projects-api"
		}
	]
}

Expected behavior
TSC should not error on generated files

Screenshots
image

  • OS: Mac OS Ventura 13.2
  • Version: v1.4.0
  • Typescript Version: 5.2.2

Additional context
Add any other context about the problem here.

Return types of query results

It would be great if the return type of a generated useQuery were automatically exported, making it easier for me to use them in my own interfaces. Since the recent update you released, the return type of a query is, for example, Omit<useQueryResult<type, unknown>, "data"> & { data: TData }, which is fantastic because it allows us to override the return type. However, it now requires me to adjust all my code to accommodate the 'omit' variant, and 'UseQueryResult<type, unknown>' is no longer valid.

Fail to generate the queries of Camunda 7 openapi.yaml

Describe the bug
I tried to generate the client with Camunda 7 openapi.yaml but it's failed via creating the queries.

To Reproduce
Run the following command with the provided openapi.yaml
openapi-rq -i ./openapi.yaml -o ./client -c axios --format prettier --debug

OpenAPI spec file
openapi.yaml.zip

Expected behavior
requests and queries folder code generate successfully.

  • OS: macOS
  • Version 14.4.1 (23E224)

Prettier not run on `api/queries`

Describe the bug

With --format prettier, no formatting seems to happen for files generated in api/queries.

To Reproduce

Create a .prettierrc with some non-default formatting setting and run with --format prettier.

OpenAPI spec file

n/a

Expected behavior

File in api/queries should get formatted just like the ones in api/requests do.

Screenshots

n/a

  • OS: Linux and Windows
  • Version: v1.3.0

Additional context

The effect can be seen in https://github.com/eclipse-apoapsis/ort-server/pull/224/files.

Request cannot be constructed from a URL that includes credentials

$ pnpm run codegen

[email protected] codegen C:\Users\zahid\Workspace\invictaworld-workspace\HafniaPoolWeb
dotenv -e .env.local -- cross-var openapi-rq -i %VITE_SWAGGER_URL% -c axios -o ./src/services/generated

node:internal/process/promises:288
triggerUncaughtException(err, true /* fromPromise */);
^

JSONParserError: Error downloading https://username:[email protected]/swagger/v1/swagger.yaml
Request cannot be constructed from a URL that includes credentials: https://username:[email protected]/swagger/v1/swagger.yaml
at download (C:\Users\zahid\Workspace\invictaworld-workspace\HafniaPoolWeb\node_modules.pnpm@[email protected]\node_modules@apidevtools\json-schema-ref-parser\dist\lib\resolvers\http.js:113:15) {
code: 'ERESOLVER',
name: 'ResolverError',
source: 'https://username:[email protected]/swagger/v1/swagger.yaml',
path: null,
toJSON: [Function: toJSON],
[Symbol(nodejs.util.inspect.custom)]: [Function: inspect]
}

Node.js v18.17.1
 ELIFECYCLE  Command failed with exit code 1.

 For security purposes, I've replaced the username and password with placeholder values, please look into this issue ASAP.

Error `No models file found` after upgrading to v1

I upgraded from 0.5.3 to 1.0.6, and ran-

openapi-rq -i src/client-gen/openapi.yml -c axios -o src/client

It starts generating, and outputs-

Then, it throws an Error at

throw new Error("No models file found");

✨ Creating Axios client
✨ Running Prettier
✨ Done! Your client is located in: /Users/devpath/src/client/requests
file:///Users/devpath/node_modules/@7nohe/openapi-react-query-codegen/dist/createImports.mjs:17
        throw new Error("No models file found");
              ^

Error: No models file found
    at createImports (file:///Users/devpath/node_modules/@7nohe/openapi-react-query-codegen/dist/createImports.mjs:17:15)
    at createSourceFile (file:///Users/devpath/node_modules/@7nohe/openapi-react-query-codegen/dist/createSource.mjs:18:21)
    at async createSource (file:///Users/devpath/node_modules/@7nohe/openapi-react-query-codegen/dist/createSource.mjs:47:71)
    at async generate (file:///Users/devpath/node_modules/@7nohe/openapi-react-query-codegen/dist/generate.mjs:37:20)

Node.js v18.16.0

I confirmed there is not a models.ts on disk, or in the response of project.getSourceFiles().

Did I miss a migration step somewhere? Thanks!

Incorrect implementation of `readOnly` and `writeOnly`

Thanks for the great lib, I've been looking for something to replace https://github.com/ferdikoomen/openapi-typescript-codegen as its no longer mantained.

There is quite a big issue on that old project too which was never fixed ferdikoomen/openapi-typescript-codegen#432

There have been considerations to fix it in a fork => hey-api/openapi-ts#28

But as of right now, they don't support react query codegen, so i'm out of options.

This library seems well mantained, and I like the API. So I would like to contribute to fixing this issue.

Describe the bug
As per the OpenAPI spec for readOnly and writeOnly

You can use the readOnly and writeOnly keywords to mark specific properties as read-only or write-only. This is useful, for example, when GET returns more properties than used in POST – you can use the same schema in both GET and POST and mark the extra properties as readOnly. readOnly properties are included in responses but not in requests, and writeOnly properties may be sent in requests but not in responses.

This implies that when a property is marked readOnly it should not be documented as part of the request object

To Reproduce

  1. Generate the following spec

OpenAPI spec file

openapi: 3.0.3
info:
  title: Templi API
  version: 1.0.0
  description: The REST API for the Templi application.
paths:
  /documents/:
    get:
      operationId: documents_list
      description: API for document CRUD actions
      parameters:
      - name: page
        required: false
        in: query
        description: A page number within the paginated result set.
        schema:
          type: integer
      tags:
      - documents
      security:
      - auth0: []
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Document'
openapi: 3.0.3
info:
  title: Templi API
  version: 1.0.0
  description: The REST API for the Templi application.
paths:
  /documents/:
    get:
      operationId: documents_list
      description: API for document CRUD actions
      parameters:
      - name: page
        required: false
        in: query
        description: A page number within the paginated result set.
        schema:
          type: integer
      tags:
      - documents
      security:
      - auth0: []
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PaginatedDocumentList'
          description: ''
components:
  schemas:
    Document:
      type: object
      properties:
        creator:
          allOf:
          - $ref: '#/components/schemas/Auth0User'
          readOnly: true
        id:
          type: integer
          readOnly: true
        orgId:
          type: integer
          writeOnly: true
      required:
      - name

Expected behavior
openapi/requests/types.gen.ts should make a differentation between a requset and response schema

ie.

// one type for requesting with readOnly props removed
export type CreateDocumentRequest = {
    name: string;
    orgId: number
};

// one type for response with writeOnly props removed
export type Document = {
    id: number
    name: string;
}

** Actual behaviour **
The ts readonly keyword is just prepended to fields with readonly, which is not semantically correct by OpenAPI spec

export type Document = {
    readonly id: number;
    name: string;
    orgId: number
};
  • OS: Arch Linux

[version: 0.4.0] Problems with the path to the files

After generate client with openapi-react-query-codegen i get not quite the expected result.

The windows platform uses \\ in paths, for example:

import { useQuery, useMutation, UseQueryOptions, UseMutationOptions } from "@tanstack\\react-query";
import { ValidationError } from "..\\requests\\models\\ValidationError";
import { ResponseData } from "..\\requests\\models\\ResponseData";  

But I expect this path /, it works correctly if I generate it in Linux:

import { useQuery, useMutation, UseQueryOptions, UseMutationOptions } from "@tanstack/react-query";
import { ValidationError } from "../requests/models/ValidationError";
import { ResponseData } from "../requests/models/ResponseData";  

here is my syntax in package.json:

"generate-client": "openapi-rq -i http://127.0.0.1:8082/openapi.json -o ./app/api --indent 2 --postfixServices \"\" ",

how do i fix it?

How to specify basepath url?

Hey, thanks for the work on the package, pretty useful!

Had a question regarding generating a different BASE url property based on the running env.

export const OpenAPI: OpenAPIConfig = {
BASE: 'https://my.staging.api.com,
}

On dev we are accessing the generated API staging env
On prod we are accessing the generated API prod env

For prod we'd like to generate

export const OpenAPI: OpenAPIConfig = {
BASE: 'https://my.api.com,
}

It's the same API, just a different BASE url. 👍

Thanks!

Override return types of hooks

Currently, when the response type schema is not present for endpoints(Open API), the return type of the hooks is "any". It would be nice if we were able to override them.

Example:

const { data } = useFoo<MyType>();

PS: Thank you for maintaining this library! I use it in our day-to-day, and it has saved me a ton of hours.

Error '.requiredOption is not a function'

I just installed and tested but get the following error:

 openapi-rq --input http://localhost:8082/docs/openapi.yaml -o src/services/openapi
C:\Users\...\node_modules\@7nohe\openapi-react-query-codegen\dist\src\cli.js:15
    .requiredOption("-i, --input <value>", "OpenAPI specification, can be a path, url or string content (required)")
     ^

TypeError: program.name(...).version(...).description(...).requiredOption is not a function
    at Object.<anonymous> (C:\Users\...\node_modules\@7nohe\openapi-react-query-codegen\dist\src\cli.js:15:6)
    at Module._compile (node:internal/modules/cjs/loader:1254:14)
    at Module._extensions..js (node:internal/modules/cjs/loader:1308:10)
    at Module.load (node:internal/modules/cjs/loader:1117:32)
    at Module._load (node:internal/modules/cjs/loader:958:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
    at node:internal/main/run_main_module:23:47

Node.js v18.15.0
error Command failed with exit code 1.

I tried with Node version 16 before, same issue.
I am on windows 11.
JFYI: generating code with openapi-typescript-codegen works in this project.

Thanks for any advice.

Respect deprecated properties

It would be nice if the generated hooks that were listed as depreciated in the OpenAPI spec had a JSDoc @deprecated comment.

Generated code is vulnerable for Denial of Service (DoS)

The generated code contains a possible DoS vulnerability in request.ts:

const getUrl = (config: OpenAPIConfig, options: ApiRequestOptions): string => {
 const encoder = config.ENCODE_PATH \|\| encodeURI;

const path = options.url.replace('{api-version}', config.VERSION).replace(/{(.*?)}/g, (substring: string, group: string) => {
 if (options.path?.hasOwnProperty(group)) {
return encoder(String(options.path[group]));
}
 return substring;
 });

The risk is not great, since we can assume the URL is not user input.
See: https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS

Build error: TS2345: Argument of type 'ApiResult<any>' is not assignable to parameter of type 'T | PromiseLike<T>'.

Describe the bug
There's an issue building the generated code.

To Reproduce
npx --package @7nohe/openapi-react-query-codegen openapi-rq -i ./api-server/config/openapi.yml -c axios -o src/openapi/jolokia

Upon building:

ERROR in /home/tlavocat/dev/activemq-artemis-self-provisioning-plugin/src/openapi/jolokia/requests/core/request.ts
./openapi/jolokia/requests/core/request.ts 332:12-18
[tsl] ERROR in /home/tlavocat/dev/activemq-artemis-self-provisioning-plugin/src/openapi/jolokia/requests/core/request.ts(332,13)
      TS2345: Argument of type 'ApiResult<any>' is not assignable to parameter of type 'T | PromiseLike<T>'.
  Type 'ApiResult<any>' is not assignable to type 'T'.
    'T' could be instantiated with an arbitrary type which could be unrelated to 'ApiResult<any>'.
ts-loader-default_ca3771bf23e1d373
 @ ./openapi/jolokia/requests/services.gen.ts 3:0-54 21:15-24 49:15-24 97:15-24 137:15-24 191:15-24 227:15-24 254:15-24 282:15-24 339:15-24 389:15-24 417:15-24 463:15-24 520:15-24
 @ ./openapi/jolokia/requests/index.ts 6:0-31 6:0-31
 @ ./utils/api-server.tsx 7:0-62 46:19-40
 @ ./utils/index.ts 7:0-29 7:0-29
 @ ./brokers/broker-details/BrokerDetails.container.tsx 8:0-94 27:24-38 41:32-47 56:17-37 56:1559-1575
 @ ./brokers/broker-details/index.ts 1:0-52 1:0-52
 @ container entry BrokerDetailsPage[0]

OpenAPI spec file

https://github.com/artemiscloud/activemq-artemis-self-provisioning-plugin/blob/main/api-server/config/openapi.yml

tsconfig

{
  "compilerOptions": {
    "allowJs": true,
    "baseUrl": ".",
    "outDir": "./dist",
    "module": "esnext",
    "moduleResolution": "node",
    "target": "es2020",
    "jsx": "react-jsx",
    "strict": false,
    "esModuleInterop": true,
    "noUnusedLocals": true,
    "resolveJsonModule": true,
    "forceConsistentCasingInFileNames": true,
    "importHelpers": true,
    "declaration": true,
    "noUnusedParameters": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "noImplicitAny": true,
    "skipLibCheck": true,
    "paths": {
      "@app/*": ["src/*"],
      "react": ["./node_modules/@types/react"]
    }
  },
  "include": ["./src/*", "./src/**/*", "setupTests.ts", "__mocks__"],
  "exclude": ["node_modules", "dist"]
}

eslint config

env:
  browser: true
  es2021: true
  node: true
  es6: true
extends:
  - eslint:recommended
  - plugin:react/recommended
  - plugin:@typescript-eslint/recommended
  - prettier
parser: '@typescript-eslint/parser'
parserOptions:
  ecmaFeatures:
    jsx: true
  ecmaVersion: 2016
  sourceType: module
plugins:
  - prettier
  - react
  - '@typescript-eslint'
rules:
  prettier/prettier:
    - error
  react/react-in-jsx-scope: 'off'
  react/no-unescaped-entities: 'off'
  react/prop-types: 'off'
settings:
  react:
    version: detect

Expected behavior
no compilation error

  • OS: fedora
  • Version: @tanstack/react-query": "^5.32.0", "axios": "^1.6.8",

A missing `prettier` formatter is silently ignored

Describe the bug

Generated files under "requests" have tabs instead of spaces although prettier is configured otherwise.

To Reproduce

I have a .prettierrc with

{
    "useTabs": false
}

and run openapi-rq -i http://localhost:8080/swagger-ui/api.json -o src/api --format prettier.

The generated *.ts files have tabs instead of spaces.

OpenAPI spec file

n/a

Expected behavior

Spaces instead of tabs should be used according to the prettier configuration.

Additional context

  • OS: Fedora Linux 40
  • Version 1.3.0

Absolute paths are generated?

Describe the bug
When i generate my client code, absolute paths are used instead of relative.

Example of output

export const useLocalizationServiceGetTranslationTexts = <TData = Common.LocalizationServiceGetTranslationTextsDefaultResponse, TError = unknown, TQueryKey extends Array<unknown> = unknown[]>({ language, module }: {
    language: import("C:/Projekt/test/test_3.0/Frontend/src/openapi/requests/models").Language;
    module: import("C:/Projekt/test/test_3.0/Frontend/src/openapi/requests/models").Module;
}
  • OS: Windows 11 22H2
  • Version v1.1.0

Helpers to prefetch data

Is your feature request related to a problem? Please describe.
Currently, I'm prefetching data in a server component so it can later be used in a client component. But creating the code is a manual process that could be easier if we have helpers (similar to the hooks).

Describe the solution you'd like

It would be nice to have something like:

await useFooServiceFindOnePrefetch(//...keys); // or await prefetchFooServiceFindOne(//...keys)

Instead of building it from scratch:

  await queryClient.prefetchQuery({
    queryKey: [
      useFooServiceFindOnePrefetchKey,
     // more keys
    ],
    queryFn: () => {
      return FooService.findOne(//...keys);
    },
  });

Any chance to consider this?
Describe alternatives you've considered
N/A

Additional context
I'm prefetching data using the guidelines from here

Expand query keys and query keys defaults defaults

First off, I love this project. I came to GitHub today to see if anyone had already created one :)

A few things to note:

(Opened PR)

export const useCatServiceGetCatById = ({ catId }: {
    catId: string;
}, queryKeys?: any[], options?: Omit<UseQueryOptions<Awaited<ReturnType<typeof CatServive.getCatById>>, unknown, Awaited<ReturnType<typeof CatService.getCatById>>, string[]>, "queryKey" | "queryFn" | "initialData">) => {
  return useQuery([useCatServiceGetCatByIdKey, ...(queryKeys ?? [{
    catId,
  }])], () => CatService.getCatById(catId), options);
}

Upgrade to v.0.26.0 of openapi-typescript-codegen

Hi,

A new version of openapi-typescript-codegen is available (v.0.26.0), which includes upgraded dependencies. This update specifically addresses a warning related to deprecated usage of json-schema-ref-parser.

Previously, the codebase was triggering the following warning:
npm WARN deprecated [email protected]: Please switch to @apidevtools/json-schema-ref-parser

Best regards,
André

How to set cookie/headers when running query/mutation?

I really like codegens, which make the life of developers easy.

How ever with this codegen, I was unable to find a way to set cookie/header per query/mutation level. I couldnt find anything in documentation either.

Did someone achieved this?

Queries not being generated. v1.0.6

Hi
I just upgraded to 1.0.6 and i now get the following error when trying to generate:

/node_modules/@7nohe/openapi-react-query-codegen/dist/service.mjs:52
            throw new Error("Method block not found");
                  ^

Error: Method block not found
    at file:///Users/user/Repos/app/node_modules/@7nohe/openapi-react-query-codegen/dist/service.mjs:52:19
    at Array.map (<anonymous>)
    at getMethodsFromService (file:///Users/user/Repos/app/node_modules/@7nohe/openapi-react-query-codegen/dist/service.mjs:47:20)
    at file:///Users/user/Repos/app/node_modules/@7nohe/openapi-react-query-codegen/dist/service.mjs:14:22
    at Array.map (<anonymous>)
    at getServices (file:///Users/user/Repos/app/node_modules/@7nohe/openapi-react-query-codegen/dist/service.mjs:11:26)
    at createSourceFile (file:///Users/user/Repos/app/node_modules/@7nohe/openapi-react-query-codegen/dist/createSource.mjs:17:27)
    at createSource (file:///Users/user/Repos/app/node_modules/@7nohe/openapi-react-query-codegen/dist/createSource.mjs:47:77)
    at generate (file:///Users/user/Repos/app/node_modules/@7nohe/openapi-react-query-codegen/dist/generate.mjs:37:26)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)

Node.js v20.11.0

terminal: npx openapi-rq -o src/data/ -i <path_to_openapi_spec>

the request directory is generated, but no queries.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.