zenstack icon indicating copy to clipboard operation
zenstack copied to clipboard

[Feature Request] Querying genererated API with fetch

Open tmax22 opened this issue 9 months ago • 3 comments

zenstack already provides amazing typesafe auto-generated hooks with react query or swr. but sometimes we simply need the raw fetch call used behind these hooks. in my case, i need to call fetch inside recoiljs effeect which is loaded outside of react component.

it could have been awesome if zenstack could provide plugin for generated fetch functions, like fetchMany<model>, fetchFindFirst<model> and so on. i would accept that these fetch functions would receive the same

I noticed that almost all (or all) hooks are using makeUrl(url, data) utility, i can also see that the url is constructed by using endpoint which is extracted from getHooksContext which uses useContext, can you provide some insights about how to use this utility to implement a non-hook typesafe fetch functions?

thanks!

for now, i was able to make a not typesafe workaround:

import { serialize } from "@zenstackhq/runtime/browser";

// i had to copy paste the implementation from https://github.com/zenstackhq/zenstack/blob/71a389c068ec3ca4962d9f05f9d69c0f81be114e/packages/plugins/tanstack-query/src/runtime/common.ts#L224
// makeUrl is not exported at the built version
export function makeUrl(url: string, args: unknown) {
  if (!args) {
    return url;
  }

  const { data, meta } = serialize(args);
  let result = `${url}?q=${encodeURIComponent(JSON.stringify(data))}`;
  if (meta) {
    result += `&meta=${encodeURIComponent(JSON.stringify({ serialization: meta }))}`;
  }
  return result;
}
const test= () =>{
    fetch(
      makeUrl(`/api/model/product/findUnique`, {
        where: {
          name: product,
        },
        select: {
          features: true,
        },
      }),
    ).then((res) => {
      res.json().then((data) => console.log(data));
    })
}

update: example for typesafe update mutation:

import { serialize } from "@zenstackhq/runtime/browser";
import type { CheckSelect, ExtraMutationOptions, QueryError } from "@zenstackhq/tanstack-query/runtime-v5";
import type { UseMutationOptions } from "@tanstack/react-query";
// this is mapped into the @prisma/client folder in the backend via vite.config
import type { Prisma, ProductClearance } from "prisma-models";

export async function updateProductClearance<T extends Prisma.ProductClearanceUpdateArgs>(
  args: Prisma.SelectSubset<T, Prisma.ProductClearanceUpdateArgs>,
  options?: Omit<
    UseMutationOptions<
      CheckSelect<T, ProductClearance, Prisma.ProductClearanceGetPayload<T>> | undefined,
      DefaultError,
      Prisma.SelectSubset<T, Prisma.ProductClearanceUpdateArgs>
    > &
      ExtraMutationOptions,
    "mutationFn"
  >,
): Promise<CheckSelect<T, ProductClearance, Prisma.ProductClearanceGetPayload<T>> | undefined> {
  const response = await fetch(`/api/model//productClearance/update`, {
    method: "PUT",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify(args),
  });
  if (!response.ok) {
    throw new Error("Network response was not ok");
  }
  const data = await response.json();
  return data as CheckSelect<T, ProductClearance, Prisma.ProductClearanceGetPayload<T>> | undefined;
}

tmax22 avatar May 27 '24 07:05 tmax22