fabien0102 / openapi-codegen

A tool for generating code base on an OpenAPI schema.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Generate export query.client functions

el-j opened this issue · comments

Hi, i try to find a way using the generated query.client types and functions directly without any useHook implementation, so i can use the react-router v6 way of loading data before rendering any component.

there for it is needed to write the router like mentioned in the example as it will handle the data loading in "loaders" before react lifecycle is used. (cannot use useHooks and needs to use the queryclient directly)
https://reactrouter.com/en/main/guides/data-libs#loading-data

import { queryClient } from "./query-client";

export const loader = ({ params }) => {
  return queryClient.fetchQuery(queryKey, queryFn, {
    staleTime: 10000,
  });
};

or more specific example of a loader:

export const loader =
  (queryClient) =>
  async ({ request }) => {
    const url = new URL(request.url);
    const q = url.searchParams.get("q");
    if (!queryClient.getQueryData(contactListQuery(q).queryKey)) {
      await queryClient.fetchQuery(contactListQuery(q));
    }
    return { q };
  };

Is there any way getting the queries with queryKey and queryFn from the codegen?
thx in advance

Hi,
Interesting usecase!

I'm not sure I have all the moving parts, but let's try! First of all, the {namespace}Context.ts is just a template (generated only if the file doesn't exist), so you can extract the queryKeyFn in a separate exported function (

queryKeyFn: (operation) => {
const queryKey: unknown[] = hasPathParams(operation)
? operation.path
.split("/")
.filter(Boolean)
.map((i) => resolvePathParam(i, operation.variables.pathParams))
: operation.path.split("/").filter(Boolean);
if (hasQueryParams(operation)) {
queryKey.push(operation.variables.queryParams);
}
if (hasBody(operation)) {
queryKey.push(operation.variables.body);
}
return queryKey;
}
}
)

I tried quickly with https://petstore3.swagger.io/api/v3/openapi.json as example, and this is how I could have the types compiled:

import { QueryClient } from "@tanstack/react-query";

import {
  fetchFindPetsByStatus,
  FindPetsByStatusVariables,
} from "./petstore/petstoreComponents";
import { queryKeyFn } from "./petstore/petstoreContext";

const queryClient = new QueryClient();

const variables: FindPetsByStatusVariables = {};

queryClient.fetchQuery(
  queryKeyFn({
    operationId: "findPetsByStatus",
    path: "/pet/findByStatus",
    variables,
  }),
  ({signal}) => fetchFindPetsByStatus(variables, signal),
  {
    staleTime: 1000,
  }
);

I hope this is helping, otherwise I will try a more complete setup 😉

thank you for the quick reply!
yes this was exactly what i was doing here.

  1. extracting the queryKeyFn
  2. try to use the query directly

i opened the issue, because i was looking for a nice way in useing the generated code.
so i would wish to have the 'queries' itself exported for use.

e.g. from your example this would be the code to write:

import { fetchFindPetsByStatusQuery } from 'queries'

queryClient.fetchQuery(fetchFindPetsByStatusQuery(variables),
  {
    staleTime: 1000,
  }
);

and the generator puts out:
{namespace}Queries.ts

export const fetchFindPetsByStatusQuery = (variables) => {
return(
 queryKeyFn({
    operationId: "findPetsByStatus",
    path: "/pet/findByStatus",
    variables,
  }),
  ({signal}) => fetchFindPetsByStatus(variables, signal)})

from my point of view, with this approach the generated Component hooks could rely on these queries as well
and the codegen would be much for flexible.
or am i thinking something wrong here?

so the {namespace}Componets.ts file would look some think like:

import queries  from 'queries'
...

export const useFindPetsByStatus = <TData = GetFindPetsByStatusResponse>(
  variables: FindPetsByStatusVariables,
  options?: Omit<
    reactQuery.UseQueryOptions<GetFindPetsByStatusResponse, GetFindPetsByStatusError, TData>,
    "queryKey" | "queryFn"
  >
) => {
  const { fetcherOptions, queryOptions, queryKeyFn } =
    useDevApiContext(options);
  return reactQuery.useQuery(queries.fetchFindPetsByStatusQuery(variables)),
    {
      ...options,
      ...queryOptions,
    }
  );
};

This is quite specific to react-router, but nothing stops us to add a generateReactRouterQueries() function, you can actually do what ever you want inside openapi-codegen.config.ts

diff --git a/openapi-codegen.config.ts b/openapi-codegen.config.ts
index 3035058..c26a2ed 100644
--- a/openapi-codegen.config.ts
+++ b/openapi-codegen.config.ts
@@ -19,6 +19,11 @@ export default defineConfig({
         filenamePrefix,
         schemasFiles,
       });
+
+      await generateReactRouterComponents(context, {
+        filenamePrefix,
+        schemasFiles,
+      });
     },
   },
 });

This should be quite straightforward to implement, do you want to give a try?

sooo i have done what i needed.
could u please check the pull-request i have opened:
#122

what do you think?

ok so for the useMutation there is something wrong ... i think it's because the return types are just "any" and not really set...

@fabien0102 so i think i am done now, there is now a new plugin creating a {namespace}Functions.ts file next to the other files. it holds the query-functions for queryClient.fetchQuery()

would be nice if you can review ;)