Giter VIP home page Giter VIP logo

connectrpc / connect-query-es Goto Github PK

View Code? Open in Web Editor NEW
208.0 11.0 14.0 2.88 MB

TypeScript-first expansion pack for TanStack Query that gives you Protobuf superpowers.

Home Page: https://connectrpc.com/docs/web/query/getting-started

License: Apache License 2.0

TypeScript 88.23% JavaScript 11.77%
react react-query reactjs solid-query svelte-query tanstack tanstack-query tanstack-react-query typescript vue-query

connect-query-es's Introduction

Connect-Query

License Build NPM Version NPM Version

Connect-Query is an wrapper around TanStack Query (react-query), written in TypeScript and thoroughly tested. It enables effortless communication with servers that speak the Connect Protocol.

Quickstart

Install

npm install @connectrpc/connect-query @connectrpc/connect-web

Note: If you are using something that doesn't automatically install peerDependencies (npm older than v7), you'll want to make sure you also have @bufbuild/protobuf, @connectrpc/connect, and @tanstack/react-query installed. @connectrpc/connect-web is required for defining the transport to be used by the client.

Usage

Connect-Query will immediately feel familiar to you if you've used TanStack Query. It provides a similar API, but instead takes a definition for your endpoint and returns a typesafe API for that endpoint.

First, make sure you've configured your provider and query client:

import { createConnectTransport } from "@connectrpc/connect-web";
import { TransportProvider } from "@connectrpc/connect-query";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";

const finalTransport = createConnectTransport({
  baseUrl: "https://demo.connectrpc.com",
});

const queryClient = new QueryClient();

function App() {
  return (
    <TransportProvider transport={finalTransport}>
      <QueryClientProvider client={queryClient}>
        <YourApp />
      </QueryClientProvider>
    </TransportProvider>
  );
}

With configuration completed, you can now use the useQuery hook to make a request:

import { useQuery } from '@connectrpc/connect-query';
import { example } from 'your-generated-code/example-ExampleService_connectquery';

export const Example: FC = () => {
  const { data } = useQuery(example);
  return <div>{data}</div>;
};

That's it!

The code generator does all the work of turning your Protobuf file into something you can easily import. TypeScript types all populate out-of-the-box. Your documentation is also converted to TSDoc.

One of the best features of this library is that once you write your schema in Protobuf form, the TypeScript types are generated and then inferred. You never again need to specify the types of your data since the library does it automatically.

Generated Code

This example shows the best developer experience using code generation. Here's what that generated code looks like:

import { MethodKind } from "@bufbuild/protobuf";
import { ExampleRequest, ExampleResponse } from "./example_pb.js";

export const example = {
  name: "Example",
  kind: MethodKind.Unary,
  I: ExampleRequest,
  O: ExampleResponse,
  service: {
    typeName: "your.company.com.example.v1.ExampleService",
  },
};

The above code doesn't have to be generated and can be manually used to describe any given endpoint.

For more information on code generation, see the documentation for protoc-gen-connect-query.

Connect-Query API

MethodUnaryDescriptor

A type that describes a single unary method. It describes the following properties:

  • name: The name of the method.
  • kind: The kind of method. In this case, it's usually MethodKind.Unary.
  • I: The input message type.
  • O: The output message type.
  • service.typeName: The fully qualified name of the service the method exists on.

This type is core to how connect-query can stay lightweight and limit the amount of code actually generated. The descriptor is expected to be passed to almost all the methods in this library.

TransportProvider

const TransportProvider: FC<
  PropsWithChildren<{
    transport: Transport;
  }>
>;

TransportProvider is the main mechanism by which Connect-Query keeps track of the Transport used by your application.

Broadly speaking, "transport" joins two concepts:

  1. The protocol of communication. For this there are two options: the Connect Protocol, or the gRPC-Web Protocol.
  2. The protocol options. The primary important piece of information here is the baseUrl, but there are also other potentially critical options like request credentials, wire serialization options, or protocol-specific options like Connect's support for HTTP GET.

With these two pieces of information in hand, the transport provides the critical mechanism by which your app can make network requests.

To learn more about the two modes of transport, take a look at the Connect-Web documentation on choosing a protocol.

To get started with Connect-Query, simply import a transport (either createConnectTransport or createGrpcWebTransport from @connectrpc/connect-web) and pass it to the provider.

A common use case for the transport is to add headers to requests (like auth tokens, etc). You can do this with a custom interceptor.

import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { TransportProvider } from "@connectrpc/connect-query";

const queryClient = new QueryClient();

export const App() {
  const transport = createConnectTransport({
    baseUrl: "<your baseUrl here>",
    interceptors: [(next) => (request) => {
      req.header.append("some-new-header", "some-value");
      // Add your headers here
      return next(request);
    }],
  });
  return (
    <TransportProvider transport={transport}>
      <QueryClientProvider client={queryClient}>
         <YourApp />
      </QueryClientProvider>
    </TransportProvider>
  );
}

For more details about what you can do with the transport, see the Connect-Web documentation.

useTransport

const useTransport: () => Transport;

Use this helper to get the default transport that's currently attached to the React context for the calling component.

useQuery

function useQuery<I extends Message<I>, O extends Message<O>>(
  methodSig: MethodUnaryDescriptor<I, O>,
  input?: DisableQuery | PartialMessage<I>,
  options?: {
    transport?: Transport;
    callOptions?: CallOptions;
  } & UseQueryOptions,
): UseQueryResult<O, ConnectError>;

The useQuery hook is the primary way to make a unary request. It's a wrapper around TanStack Query's useQuery hook, but it's preconfigured with the correct queryKey and queryFn for the given method.

Any additional options you pass to useQuery will be merged with the options that Connect-Query provides to @tanstack/react-query. This means that you can pass any additional options that TanStack Query supports.

useSuspenseQuery

Identical to useQuery but mapping to the useSuspenseQuery hook from TanStack Query. This includes the benefits of narrowing the resulting data type (data will never be undefined).

useInfiniteQuery

function useInfiniteQuery<
  I extends Message<I>,
  O extends Message<O>,
  ParamKey extends keyof PartialMessage<I>,
  Input extends PartialMessage<I> & Required<Pick<PartialMessage<I>, ParamKey>>,
>(
  methodSig: MethodUnaryDescriptor<I, O>,
  input: DisableQuery | Input,
  options: {
    pageParamKey: ParamKey;
    transport?: Transport;
    callOptions?: CallOptions;
    getNextPageParam: GetNextPageParamFunction<PartialMessage<I>[ParamKey], O>;
  },
): UseInfiniteQueryResult<InfiniteData<O>, ConnectError>;

The useInfiniteQuery is a wrapper around TanStack Query's useInfiniteQuery hook, but it's preconfigured with the correct queryKey and queryFn for the given method.

There are some required options for useInfiniteQuery, primarily pageParamKey and getNextPageParam. These are required because Connect-Query doesn't know how to paginate your data. You must provide a mapping from the output of the previous page and getting the next page. All other options passed to useInfiniteQuery will be merged with the options that Connect-Query provides to @tanstack/react-query. This means that you can pass any additional options that TanStack Query supports.

useSuspenseInfiniteQuery

Identical to useInfiniteQuery but mapping to the useSuspenseInfiniteQuery hook from TanStack Query. This includes the benefits of narrowing the resulting data type (data will never be undefined).

useMutation

function useMutation<I extends Message<I>, O extends Message<O>>(
  methodSig: MethodUnaryDescriptor<I, O>,
  options?: {
    transport?: Transport;,
    callOptions?: CallOptions;
  },
): UseMutationResult<O, ConnectError, PartialMessage<I>>

The useMutation is a wrapper around TanStack Query's useMutation hook, but it's preconfigured with the correct mutationFn for the given method.

Any additional options you pass to useMutation will be merged with the options that Connect-Query provides to @tanstack/react-query. This means that you can pass any additional options that TanStack Query supports.

createConnectQueryKey

function createConnectQueryKey<I extends Message<I>, O extends Message<O>>(
  methodDescriptor: Pick<MethodUnaryDescriptor<I, O>, "I" | "name" | "service">,
  input?: DisableQuery | PartialMessage<I> | undefined,
): ConnectQueryKey<I>;

This helper is useful to manually compute the queryKey sent to TanStack Query. This function has no side effects.

createConnectInfiniteQueryKey

function createConnectInfiniteQueryKey<
  I extends Message<I>,
  O extends Message<O>,
>(
  methodDescriptor: Pick<MethodUnaryDescriptor<I, O>, "I" | "name" | "service">,
  input: DisableQuery | PartialMessage<I>,
  pageParamKey: keyof PartialMessage<I>,
): ConnectInfiniteQueryKey<I>;

This function is not really necessary unless you are manually creating infinite query keys. When invalidating queries, it usually makes more sense to use the createConnectQueryKey function instead since it will also invalidate the regular queries (as well as the infinite queries).

callUnaryMethod

function callUnaryMethod<I extends Message<I>, O extends Message<O>>(
  methodType: MethodUnaryDescriptor<I, O>,
  input: PartialMessage<I> | undefined,
  {
    callOptions,
    transport,
  }: {
    transport: Transport;
    callOptions?: CallOptions | undefined;
  },
): Promise<O>;

This API allows you to directly call the method using the provided transport. Use this if you need to manually call a method outside of the context of a React component, or need to call it where you can't use hooks.

createProtobufSafeUpdater

Creates a typesafe updater that can be used to update data in a query cache. Used in combination with a queryClient.

import { createProtobufSafeUpdater } from '@connectrpc/connect-query';
import { useQueryClient } from "@tanstack/react-query";

...
const queryClient = useQueryClient();
queryClient.setQueryData(
  createConnectQueryKey(example),
  createProtobufSafeUpdater(example, (prev) => {
    return {
      ...prev,
      completed: true,
    };
  })
);

createQueryOptions

function createQueryOptions<I extends Message<I>, O extends Message<O>>(
  methodSig: MethodUnaryDescriptor<I, O>,
  input: DisableQuery | PartialMessage<I> | undefined,
  {
    transport,
    callOptions,
  }: ConnectQueryOptions & {
    transport: Transport;
  },
): {
  queryKey: ConnectQueryKey<I>;
  queryFn: QueryFunction<O, ConnectQueryKey<I>>;
  enabled: boolean;
};

A functional version of the options that can be passed to the useQuery hook from @tanstack/react-query. When called, it will return the appropriate queryKey, queryFn, and enabled flag. This is useful when interacting with useQueries API or queryClient methods (like ensureQueryData, etc).

An example of how to use this function with useQueries:

import { useQueries } from "@tanstack/react-query";
import { createQueryOptions, useTransport } from "@connectrpc/connect-query";
import { example } from "your-generated-code/example-ExampleService_connectquery";

const MyComponent = () => {
  const transport = useTransport();
  const [query1, query2] = useQueries([
    createQueryOptions(example, { sentence: "First query" }, { transport }),
    createQueryOptions(example, { sentence: "Second query" }, { transport }),
  ]);
  ...
};

createInfiniteQueryOptions

function createInfiniteQueryOptions<
  I extends Message<I>,
  O extends Message<O>,
  ParamKey extends keyof PartialMessage<I>,
  Input extends PartialMessage<I> & Required<Pick<PartialMessage<I>, ParamKey>>,
>(
  methodSig: MethodUnaryDescriptor<I, O>,
  input: DisableQuery | Input,
  {
    transport,
    getNextPageParam,
    pageParamKey,
    callOptions,
  }: ConnectInfiniteQueryOptions<I, O, ParamKey>,
): {
  getNextPageParam: ConnectInfiniteQueryOptions<
    I,
    O,
    ParamKey
  >["getNextPageParam"];
  queryKey: ConnectInfiniteQueryKey<I>;
  queryFn: QueryFunction<
    O,
    ConnectInfiniteQueryKey<I>,
    PartialMessage<I>[ParamKey]
  >;
  initialPageParam: PartialMessage<I>[ParamKey];
  enabled: boolean;
};

A functional version of the options that can be passed to the useInfiniteQuery hook from @tanstack/react-query.When called, it will return the appropriate queryKey, queryFn, and enabled flags, as well as a few other parameters required for useInfiniteQuery. This is useful when interacting with some queryClient methods (like ensureQueryData, etc).

ConnectQueryKey

type ConnectQueryKey<I extends Message<I>> = [
  serviceTypeName: string,
  methodName: string,
  input: PartialMessage<I>,
];

TanStack Query requires query keys in order to decide when the query should automatically update.

QueryKeys in TanStack Query are usually arbitrary, but Connect-Query uses the approach of creating a query key that begins with the least specific information: the service's typeName, followed by the method name, and ending with the most specific information to identify a particular request: the input message itself.

For example, a query key might look like this:

[
  "example.v1.ExampleService",
  "GetTodos",
  { id: "0fdf2ebe-9a0c-4366-9772-cfb21346c3f9" },
];

For example, a partial query key might look like this:

["example.v1.ExampleService", "GetTodos"];

ConnectInfiniteQueryKey

Similar to ConnectQueryKey, but for infinite queries.

type ConnectInfiniteQueryKey<I extends Message<I>> = [
  serviceTypeName: string,
  methodName: string,
  input: PartialMessage<I>,
  "infinite",
];

Testing

Connect-query (along with all other javascript based connect packages) can be tested with the createRouterTransport function from @connectrpc/connect. This function allows you to create a transport that can be used to test your application without needing to make any network requests. We also have a dedicated package, @connectrpc/connect-playwright for testing within playwright.

For playwright, you can see a sample test here.

Frequently Asked Questions

How do I pass other TanStack Query options?

Each function that interacts with TanStack Query also provides for options that can be passed through.

import { useQuery } from '@connectrpc/connect-query';
import { example } from 'your-generated-code/example-ExampleService_connectquery';

export const Example: FC = () => {
  const { data } = useQuery(example, undefined, {
    // These are typesafe options that are passed to underlying TanStack Query.
    refetchInterval: 1000,
  });
  return <div>{data}</div>;
};

Why was this changed from the previous version of Connect-Query?

Originally, all we did was pass options to TanStack Query. This was done as an intentional way to keep ourselves separate from TanStack Query. However, as usage increased, it became obvious that were still tied to the API of TanStack Query, and it only meant that we increased the burden on the developer to understand that underlying connection. This new API removes most of that burden and reduces the surface area of the API significantly.

Is this ready for production?

Buf has been using Connect-Query in production for some time. Also, there is 100% mandatory test coverage in this project which covers quite a lot of edge cases.

Using BigInt with RPC inputs

Since Connect-Query use the inputs as keys for the query, if you have a field with type int64, those fields will cause serialization problems. For this reason, Connect-Query ships with defaultOptions that can be passed to the QueryClient to make sure serializing BigInt fields is done properly:

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

const queryClient = new QueryClient({ defaultOptions });

What is Connect-Query's relationship to Connect-Web and Protobuf-ES?

Here is a high-level overview of how Connect-Query fits in with Connect-Web and Protobuf-ES:

Expand to see a detailed dependency graph

connect-query_dependency_graph

Your Protobuf files serve as the primary input to the code generators protoc-gen-connect-query and protoc-gen-es. Both of these code generators also rely on primitives provided by Protobuf-ES. The Buf CLI produces the generated output. The final generated code uses Transport from Connect-Web and generates a final Connect-Query API.

What is Transport

Transport is a regular JavaScript object with two methods, unary and stream. See the definition in the Connect-Web codebase here. Transport defines the mechanism by which the browser can call a gRPC-web or Connect backend. Read more about Transport on the connect docs.

What if I already use Connect-Web?

You can use Connect-Web and Connect-Query together if you like!

What if I use gRPC-web?

Connect-Query also supports gRPC-web! All you need to do is make sure you call createGrpcWebTransport instead of createConnectTransport.

That said, we encourage you to check out the Connect protocol, a simple, POST-only protocol that works over HTTP/1.1 or HTTP/2. It supports server-streaming methods just like gRPC-Web, but is easy to debug in the network inspector.

Do I have to use a code generator?

No. The code generator just generates the method descriptors, but you are free to do that yourself if you wish.

What if I have a custom Transport?

If the Transport attached to React Context via the TransportProvider isn't working for you, then you can override transport at every level. For example, you can pass a custom transport directly to the lowest-level API like useQuery or callUnaryMethod.

Does this only work with React?

Connect-Query does require React, but the core (createConnectQueryKey and callUnaryMethod) is not React specific so splitting off a connect-solid-query is possible.

How do I do Prefetching?

When you might not have access to React context, you can use the create series of functions and provide a transport directly. For example:

import { say } from "./gen/eliza-ElizaService_connectquery";

function prefetch() {
  return queryClient.prefetchQuery({
    queryKey: createConnectQueryKey(say, { sentence: "Hello" }),
    queryFn: () =>
      callUnaryMethod(say, { sentence: "Hello" }, { transport: myTransport }),
  });
}

What about Streaming?

Connect-Query currently only supports Unary RPC methods, which use a simple request/response style of communication similar to GET or POST requests in REST. This is because it aligns most closely with TanStack Query's paradigms. However, we understand that there may be use cases for Server Streaming, Client Streaming, and Bidirectional Streaming, and we're eager to hear about them.

At Buf, we strive to build software that solves real-world problems, so we'd love to learn more about your specific use case. If you can provide a small, reproducible example, it will help us shape the development of a future API for streaming with Connect-Query.

To get started, we invite you to open a pull request with an example project in the examples directory of the Connect-Query repository. If you're not quite sure how to implement your idea, don't worry - we want to see how you envision it working. If you already have an isolated example, you may also provide a simple CodeSandbox or Git repository.

If you're not yet at the point of creating an example project, feel free to open an issue in the repository and describe your use case. We'll follow up with questions to better understand your needs.

Your input and ideas are crucial in shaping the future development of Connect-Query. We appreciate your input and look forward to hearing from you.

Legal

Offered under the Apache 2 license.

connect-query-es's People

Contributors

anuraaga avatar chrispine avatar dependabot[bot] avatar dimitropoulos avatar mckelveygreg avatar paul-sachs avatar rubensf avatar smallsamantha avatar smaye81 avatar srikrsna-buf avatar timostamm avatar vctqs1 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

connect-query-es's Issues

Availability of createUseQueryOptions and createUseInfiniteQueryOptions in v1

Is the absence of createUseQueryOptions and createUseInfiniteQueryOptions in v1 intentional? These methods appear to be extremely important when React context is not available.

https://github.com/connectrpc/connect-query-es/blob/main/packages/connect-query/src/index.ts#L19-L24

The README mentions createUseQueryOptions:

What if I have a custom Transport?
If the Transport attached to React Context via the TransportProvider isn't working for you, then you can override transport at every level. For example, you can pass a custom transport directly to the lowest-level API like useQuery or createUseQueryOptions.

Does this only work with React?
Connect-Query does require React, but the core (createUseQueryOptions) is not React specific so splitting off a connect-solid-query is possible.

(README Link: https://github.com/connectrpc/connect-query-es?tab=readme-ov-file#does-this-only-work-with-react)

Is there an alternative method now, or was their removal unintentional? If it's an unintentional, I'm willing to help with a PR.

Issue with @connectrpc/connect-query: Module not found: Error: Can't resolve './use-transport'

Hi there 👋

Unfortunately after upgrading to use @connectrpc/connect-query, I found that my app stopped working. I am using a regular CRA setup, and it seems that despite the package exporting both CommonJS and ESM, some of the modules cannot be found, use-transport, create-use-query-options and create-use-infinite-query-options to name a few.

I tried using

import { useQuery } from '@connectrpc/connect-query/dist/cjs/use-query';

instead, but it doesn't seem to alleviate the issue.

Also it might be worth mentioning that npmjs has a @connectrpc/connect-react-query release 1.0.0-rc.1 which might confuse some people, perhaps it might be worth removing it if @connectrpc/connect-query should be used instead?

Please find the issue below:

ERROR in ./node_modules/@connectrpc/connect-query/dist/esm/use-mutation.js 16:0-47
Module not found: Error: Can't resolve './use-transport' in 'app/node_modules/@connectrpc/connect-query/dist/esm'
Did you mean 'use-transport.js'?

BREAKING CHANGE: The request './use-transport' failed to resolve only because it was resolved as fully specified
(probably because the origin is strict EcmaScript Module, e. g. a module with javascript mimetype, a '*.mjs' file, or a '*.js' file where the package.json contains '"type": "module"').
The extension in the request is mandatory for it to be fully specified.
Add the extension to the request.
ERROR in ./node_modules/@connectrpc/connect-query/dist/esm/use-query.js 15:0-67
Module not found: Error: Can't resolve './create-use-query-options' in 'app/node_modules/@connectrpc/connect-query/dist/esm'
Did you mean 'create-use-query-options.js'?

BREAKING CHANGE: The request './create-use-query-options' failed to resolve only because it was resolved as fully specified
(probably because the origin is strict EcmaScript Module, e. g. a module with javascript mimetype, a '*.mjs' file, or a '*.js' file where the package.json contains '"type": "module"').
The extension in the request is mandatory for it to be fully specified.
Add the extension to the request.
ERROR in ./node_modules/@connectrpc/connect-query/dist/esm/use-query.js 16:0-47
Module not found: Error: Can't resolve './use-transport' in 'app/node_modules/@connectrpc/connect-query/dist/esm'
Did you mean 'use-transport.js'?

BREAKING CHANGE: The request './use-transport' failed to resolve only because it was resolved as fully specified
(probably because the origin is strict EcmaScript Module, e. g. a module with javascript mimetype, a '*.mjs' file, or a '*.js' file where the package.json contains '"type": "module"').
The extension in the request is mandatory for it to be fully specified.
Add the extension to the request.
ERROR in ./node_modules/@connectrpc/connect-query/dist/esm/use-infinite-query.js 15:0-84
Module not found: Error: Can't resolve './create-use-infinite-query-options' in 'app/node_modules/@connectrpc/connect-query/dist/esm'
Did you mean 'create-use-infinite-query-options.js'?

BREAKING CHANGE: The request './create-use-infinite-query-options' failed to resolve only because it was resolved as fully specified
(probably because the origin is strict EcmaScript Module, e. g. a module with javascript mimetype, a '*.mjs' file, or a '*.js' file where the package.json contains '"type": "module"').
The extension in the request is mandatory for it to be fully specified.
Add the extension to the request.

Timestamps in query key can trigger infinite query loops

Using the following query will cause an infinite loading query:

import { Timestamp } from "@bufbuild/protobuf";
...
useSuspenseQuery(
  someMethod,
  {
    start: Timestamp.fromDate(new Date("01/01/01")),
    end: Timestamp.fromDate(new Date("02/01/01"))
  }
);

This is likely due to the handling of class objects in stable-hash.

Does this work with @tanstack/vue-query or other variants?

Since the official doc uses react-query as an example, and the code here uses hard-coded @tanstack/react-query:

import type {
UseMutationOptions as TSUseMutationOptions,
UseMutationResult,
} from "@tanstack/react-query";
import { useMutation as tsUseMutation } from "@tanstack/react-query";

I wonder whether this package work with @tanstack/vue-query, @tanstack/solid-query or @tanstack/svelte-query. If not, is it possible to let users inject the useQuery or other functions themselves?

`useMutation` should assume input is required

The following example currently throws a typescript error:

const deleteOrg = useMutation({
  ...deleteOrganizationByName.useMutation(),
  onSuccess: (_, { name }) => {
    showToast(`Successfully deleted organization "${name ?? ""}"`);
  },
}); 

Because it's assumed params is optional. We can work around this via something like:

const deleteOrg = useMutation({
  ...deleteOrganizationByName.useMutation(),
  onSuccess: (_, input) => {
    if (input === undefined) {
      return;
    }
    const { name } = input;
    showToast(`Successfully deleted organization "${name ?? ""}"`);
  },
}); 

But given that most mutation should have some kind of input, this feels unnecessary. We already have to default name to "" because we can't assume validation but that's a larger protobuf-es thing to tackle.

Support cacheable rpcs with tanstack query

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

See https://buf.build/blog/introducing-connect-cacheable-rpcs/ when using in combination with @tanstack/react-query GET is not used properly

export const transport = createConnectTransport({
  baseUrl: "...",
  useHttpGet: true,
});

return (
    <>
      <ThemeProvider attribute="class" defaultTheme="light" enableSystem>
        <TransportProvider transport={transport}>
          <QueryClientProvider client={queryClient}>
            <AppProvider>
              <Toaster />
              {getLayout(<Component {...pageProps} />)}
            </AppProvider>
          </QueryClientProvider>
        </TransportProvider>
      </ThemeProvider>
    </>
  );
 rpc GetAnalyticsView(GetAnalyticsViewRequest) returns (GetAnalyticsViewResponse) {
    option idempotency_level = NO_SIDE_EFFECTS;
  }

Latest release 0.4.0 breaks

After upgrading to 0.4.0 we see the following error

[unknown] To use Connect, you must provide a `Transport`: a simple object that handles `unary` and `stream` requests. `Transport` objects can easily be created by using `@bufbuild/connect-web`'s exports `createConnectTransport` and `createGrpcWebTransport`. see: https://connect.build/docs/web/getting-started for more

Revert to 0.3.0 fix the issue.

Versions:

    "@bufbuild/connect": "^0.12.0",
    "@bufbuild/connect-query": "^0.4.0",
    "@bufbuild/connect-web": "^0.12.0",
    "@bufbuild/protobuf": "^1.3.0",

Used in Next.js

`const ${localName(method)}` can result in a not allowed variable name

I have a case where a service has a "Delete" method. Since the generator will output export const ${localName(method)} = ..., it will output export const delete = ..., which is a reserved keyword in javascript and cannot be used as a variable name in strict mode. This will make typescript compilation fail with no way of ignoring the issue.

While "delete" is the more likely case to happen in this context, all methods named after a reserved word will also generate invalid code as such.

A simple solution could be using PascalCase for the variable declaration, but that would also break any code using this after the upgrade.

What are your thought on this?

In the meantime i'll just automatically patch the file after generation with a sed command.

Thanks a lot by the way, this toolchain is a game changer for us compared to using grpc-web! :)

Simplify usage with prefetchQuery

The generated hooks doesn't provide a very ergonomic way to interact with queryClient.prefetchQuery. See slack thread here.

If you want to build a function to prefetch the query, you need to use a hook to initialize the options and this limits the places we can do that. This problem is somewhat related to #55 , wherein we want methods that don't depend on context to be usable.

Support/documentation for stream processing with tanstack

Is your feature request related to a problem? Please describe.
Right now tanstack docs for connect-web talk about unary requests, which isn't the big unlock that gRPC/connect provides.

Describe the solution you'd like
What would you like to have connect-es do? How should we solve the problem?

connect-es should have documentation/examples/support for async stream processing with tanstack

Please specify whether the request is for Connect for Web or Connect for
Node.js.

Web

Describe alternatives you've considered
If you've proposed a solution, are there any alternatives? Why are they worse
than your preferred approach?

Manual stream processing

Does the code-gen support stream service?

I have a

service SampleService{
 rpc WatchServerEvents (WatchServerEventsRequest) returns (stream WatchServerEventsResponse) {}
}

But the generated SampleService_connectquery.ts is empty.

The code-gen works for unary calls.

Is streaming supported? Thanks!

`useMutation` error field infers to unknown instead of ConnectError

Errors thrown during a mutation will always be of type ConnectError and we can build that information into useMutation.

const deleteOrg = useMutation({
    ...deleteOrganizationByName.useMutation(),
});
const { error } = deleteOrg

error should be inferred as ConnectError but currently it shows as unknown.

image

How do you get queryKey for Infinit queries

Hello There,

I'm trying to migrate to version 1.0.0 of connect-query-es and one issue i stumbled upon is how do i get the query key for an infinit query. I've already noticed createConnectQueryKey for a normal query key, but i don't know how it can be adapted to infinit queries. Or maybe expose a method such as createInfinitConnectQueryKey if something similar doesn't exist.

Thanks in advance

Very easy to create collisions between infinite and non-infinite queries

It is very easy for users to create collisions between infinite and non-infinite queries.

Consider a list API where we use token based pagination. The token is a string and an empty token means we want the first page.
I may call the API with useQuery and no page_token to get a couple results from the first page.
I may also call the API with useInfiniteQuery and a page_token for an infinite list of results.
The issue is both these calls produce an identical query key.

You can see why here... the connect code sets page_token to undefined for infinite query results.

Example Proto

rpc ListUsers(ListUsersRequest) returns (...) {

}

message ListUsersRequest {
  optional string page_token = 1;
}

Example keys that are created

import { hashKey } from "@tanstack/react-query";
const useQueryKey = createConnectQueryKey(listUsers, {
  pageSize: 10,
});

const useInfiniteQueryKey = createConnectQueryKey(listUsers, {
  pageSize: 10,
  pageToken: undefined,
});

console.log(hashKey(useQueryKey) === hashKey(useInfiniteQueryKey))
// true

I'm not sure if there is a good solution for this but if there is it should be documented. For now one option is to manually check that every time I call useQuery I pass a page_token and set it to an empty string.

Plugin output scheme

Less an issue and more of a discussion point. I've noticed the scheme for our generated file structure looks like:

import {
  deleteOrganizationByName,
  listOrganizations,
} from "@bufbuild/apigen/buf/alpha/registry/v1alpha1/organization-OrganizationService_connectquery";

I love that we can just import the specific methods we use, which can reduce bundle size, but I wonder about the scheme of {service}-{serviceName}_connectquery. Since this is effectively an api surface, maybe it makes sense to instead do:

import {
  deleteOrganizationByName,
  listOrganizations,
} from "@bufbuild/apigen/buf/alpha/registry/v1alpha1/organization_connectquery/organization_service";

This way we can stay consistent with our existing plugins which generate one file/folder for each protofile. And we can just nest the multiple services inside.

Parallel queries with useQueries

Hi, I was wondering if anyone has taken an attempt at using this package along with useQueries?

I have tried but I am running into a wall with an error:

React has detected a change in the order of Hooks called by Hierarchy. This will lead to bugs and errors if not fixed. For more information, read the Rules of Hooks: https://reactjs.org/link/rules-of-hooks

I'm not entirely sure if this is stemming from this package but I wanted to seek advice on the best path of using useQueries along with connect-query

Enum value becomes a string when SSR

I posted a similar issue on "protobuf-es" and received a response that there might be a problem with "connect-query-es", so I posted an issue on here as well.

The problem is that the enum type value becomes a string when it is an SSR and a number when it is a CSR.

Please check the issue ticket below for more information on the situation.
bufbuild/protobuf-es#784

thanks.

Export `createUseQueryOptions` and `createUseInfiniteQueryOptions` from the library

First off, thank you for all the work you've put into this package!

I would like to propose some use cases for createUseQueryOptions and createUseInfiniteQueryOptions as a continuation of the conversation from #296.

// fetching a list of suspense queries
const suspenseQueries = useSuspenseQueries({
  queries: userIds.map((userId) =>
    createUseQueryOptions(getUser, { userId }, { transport })
  ),
});

// fetching a list of standard queries
const queries = useQueries({
  queries: userIds.map((userId) =>
    createUseQueryOptions(getUser, { userId }, { transport })
  ),
});

// ensuring a query is loaded
await queryClient.ensureQueryData(
  createUseQueryOptions(getUser, { userId: 1 }, { transport })
);

// prefetching a query
queryClient.prefetchQuery(
  createUseQueryOptions(getUser, { userId: 1 }, { transport })
);

So far I do not have a use case for createUseInfiniteQueryOptions but other people may need createUseInfiniteQueryOptions for these same use cases, specifically prefetching and ensuring a query is loaded.

I can use createConnectQueryKey and callUnaryMethod to build my own createUseQueryOptions but once I've made that code typesafe and reusable its basically a copy of the internal methods.

Export ESM format

Hey there! Wondering have you consider to compile and export esm format like other @bufuild packages did?

Currently I'm using both connect-query and connect-web, noticed seems there is one bundle size impact due to the different export format(cjs) connect-query is using:
Screenshot 2023-06-30 at 1 03 49 pm

Using Webpack 5, some functions in @bufbuild/protobuf and @bufbuild/connect were bundled twice. All conect-query's dependences will be imported from cjs and the same function used in my source code via import will be imported from esm.

Ran a quick local test, added esm to connect-query and got this:
Screenshot 2023-06-30 at 1 10 48 pm

Would be good to do so? Thank you :)

Add API to allow permissive query keys

As of version 1.4.1, query keys are always generated with their default values populated. While this works really well for preventing duplicate queries, it prevents some use cases of createConnectQueryKey, primarily when used along with queryClient.invalidateQueries. See slack thread.

We should expose a standardized way of creating permissive keys that allow more generalized matching where we want to match multiple queries.

How to use multiple transports effectively?

Currently I am using 1 TransportProvider - is it possible to use more than 1 transport without overriding the previous provider? I would like to choose on a query/mutation basis whether to use a transport #1 or #2, what would be the cleanest way to achieve this?

Remove pageParamKey from queryKey in useInfiniteQuery

Related to #43 there is some potential confusion because we encode the contents of the incoming message into the key of the query, including pagination information which changes actively due to change of pagination.

Omitting pageParamKey may be sufficient but also may not cover everything (like if the page param is deeply nested in the message).

Support NextJS RSC

Currently all the apis exposed depend on context to get the transport. This causes issues with React Server Components. One potential solution to this problem would be to create a context free generated version of the apis that requires the transport as an argument.

Compilation error with code generated from protoc-gen-connect-query

With v0.4.3, protoc-gen-connect-query generates the following TypeScript (with the plugin option target=ts):

// ...
export const ElizaService = {
  typeName: "connectrpc.eliza.v1.ElizaService",
  methods: {
    // elided
  }
} as const;
// ...

With the plugin option target=js, the plugin also generates as const, which is a syntax error in JavaScript.

useQueries with current setup

Hi there!

#67 has previously mentioned using createQueryOptions to allow useQueries to work, however that was an issue prior to v1 being released.

With the current version of the connect library, what would be the best example to follow to apply useQueries?

I imagine useQueries types within @connectrpc/connect-query would have to match with @tanstack/react-query. Is there any other blocker from the architecture perspective that might not allow useQueries, for example using more than 1 transport?

Returned data should be possibly undefined

The type for the returned data in a useQuery should be potentially undefined to take into account loading and error states.

  const { data } = useQuery(
    getPrices.useQuery(),
  );

feat: Extend connect-query-es to other Tanstack Query variants

Buf Slack link: https://bufbuild.slack.com/archives/CRZ680FUH/p1705412823808149

As discussed on Slack this issue shall track the initial addition of more than just the React variant of Tanstack Query. The v1 release already went ahead and decoupled the codegen from react but several more steps will be needed to fully implement compatibility with other variants. To quote @paul-sachs on the approach:

The first steps I’d recommend would be to create your own package that depends on connect-query and uses only the non-react specific apis (createConnectQueryKey and callUnaryMethod) and create your own version of useQuery based on that. As much as I think the apis are similar, I think the types (and use of suspense) are probably different. Once you figure out the common bits that your package needs, the first step for us would probably be to create a connect-query-core package that just exports those and we can work towards removing the unnecessary dependencies.

Proposal: API improvements

The user for facing API today looks like:

import { useQuery } from '@tanstack/react-query';
import { example } from 'your-generated-code/example-ExampleService_connectquery';

export const Example: FC = () => {
  const { data } = useQuery(example.useQuery({}));
  return <div>{data}</div>;
};

The generated code is as follows:

import {
  createQueryService,
  createUnaryHooks,
} from "@connectrpc/connect-query";
import { MethodKind } from "@bufbuild/protobuf";
import { ExampleRequest, ExampleResponse } from "./example_pb.js";

export const typeName = "your.company.com.example.v1.ExampleService";

export const ExampleService = {
  methods: {
    example: {
      name: "Example",
      kind: MethodKind.Unary,
      I: ExampleRequest,
      O: ExampleResponse,
    },
  },
  typeName,
} as const;

const $queryService = createQueryService({ service: ExampleService });

export const say = {
  ...$queryService.example,
  ...createUnaryHooks($queryService.example),
};

Issues with the above API and generated code:

  • The use of useQuery twice is verbose and confusing, if one has to set other fields that the query APIs accept it gets even more verbose with a slightly different looking call: useQuery({example.useQuery({})..., extra: value})
  • It is not very apparent what values are set by the generated hook and which of them are safe to override.
  • Query APIs are endpoint oriented and as such the generated code correctly exports a unique symbol for each rpc. However, the way the code is generated today is not ideal for tree-shaking as it will include the entire service object (including all other methods). Not only that but the entire runtime is included even if only one of the hooks is used.
  • For contributors to add support for other frameworks the example set by the react approach is to write a framework specific plugin and accompanying runtime package. This is a barrier for new contributors as they have to know about protobuf plugins and how to write one.
  • Probably a consequence of how the code is generated, the API surface of the runtime package is more than ideal.
  • There are multiple ways to create the query hooks. One can use the generated code or they can simple use the generated code by the connect plugin and use the runtime to create their own hooks.
  • Mocking the API requires them to use the service definition, this is different from how users use the hooks.

Possible solution

I propose a framework agnostic plugin accompanied by a new react specific API that wraps the original hooks. The generated code will look like:

import { MethodKind } from "@bufbuild/protobuf";
import { ExampleRequest, ExampleResponse } from "./example_pb.js";

export const typeName = "your.company.com.example.v1.ExampleService";

export const example = {
  localName: "example",
  name: "Example",
  kind: MethodKind.Unary,
  I: ExampleRequest,
  O: ExampleResponse,
  service: { typeName },
} as const;

This solves the tree-shaking problem by just generating method specific definitions of the rpcs. This is also framework agnostic as it only generates the rpc definitions without any imports of query APIs.

The runtime API will take the form:

import { useQuery } from '@connectrpc/connect-react-query';
import { example } from 'your-generated-code/example-ExampleService_connectquery';

export const Example: FC = () => {
  const { data } = useQuery(example, {});
  return <div>{data}</div>;
};

In this API, the second parameter is for the request, the optional third is to provide additional parameters to the underlying Hooks API. This way we can only expose safe to override properties. This will also make wrapping the hooks to modify the types easier (connectrpc/connect-es#694 (comment)). We should also expose supplementary API for things like calculating query keys and other bits that help us construct the hooks.

Along with this we should expose a mocking wrapper similar to createRouterTransport that is rpc oriented where users can import the connect-query generated constants to mock rpcs.

With this structure contributors only need to create the runtime package and not bother about the plugin.

Implementation

This is a breaking change and a significant shift from our current API. I am not entirely sure if we can write a migrator but even if we did it won't be trivial.

One option is to hold off on #219 and #179 and release a parallel package for Tanstack v5. We continue to support the current version for Tanstack v4 and users wanting to upgrade to Tanstack v5 can update to the new react specific package. This gives us some time to work on the migrator and at the same time new users will start using the new APIs.

@paul-sachs @timostamm thoughts?

How to get query key?

Is getting the query key exposed in the public API? Since the tanstack v5 seems to only have usable API for invalidating APIs using the keys. (I am aware that I can construct it via the name of service/ method, params, but I have a bad feeling renaming something will cause unfortunate bugs, I'd prefer to keep it typesafe and automatic)

Question: Recommended ways for testing/mocking services

I'm looking for docs on mocking rpc function responses from the frontend for unit tests and cant find many/any references on approaches with connect-query. The only way I have seen so far is to make another abstraction ontop of the client and then mock that.

Would you be able to provide some tips on how to mock responses (errors and successes) of the gRPC calls alongside tanstack/react-query ?

eg something along the lines of this?

const mutationPromise = Promise.resolve(new SomeActionResponse());
const mockMutate = jest.fn().mockReturnValue(mutationPromise);

jest.spyOn(service.SomeAction, "useMutation").mockReturnValue({ mutationFn: mockMutate });

render(<Component />);

Generated code imports not compatible with types generated using "target=ts"

I may just be missing a configuration option, but it seems that the generated "_connectquery.ts" files always try to import the types from a ".js" file, which makes them incompatible with types files generated using "target=ts". Here's the relevant section from my buf.gen.yaml

  - plugin: es
    out: react_app/src
    opt: target=ts
  - plugin: connect-query
    out: react_app/src
    opt: target=ts

For now I've simply dropped the target=ts option from the es plugin to get it to work, but I assume that simply dropping the ".js" extension from the import would allow it to work in both places (at least it does for me using webpack).

useMutation() doesn't automatically invalidate useQuery()

The useMutation hook doesn't automatically invalidate useQuery cache when used on the same service.

Example:
data is not updated in this case:
image

data is updated when invalidating tags manually:
image

Should the useMutation invalidate the cache automatically since it is part of the same service?

0.5.2 breaks in webpack

0.5.1 works fine.

After upgrading to 0.5.2 webpack fails to build with these errors:

Module not found: Error: Can't resolve './create-query-service' in '/Users/daniel/project/node_modules/@connectrpc/connect-query/dist/esm'
Did you mean 'create-query-service.js'?
BREAKING CHANGE: The request './create-query-service' failed to resolve only because it was resolved as fully specified
(probably because the origin is strict EcmaScript Module, e. g. a module with javascript mimetype, a '*.mjs' file, or a '*.js' file where the package.json contains '"type": "module"').
The extension in the request is mandatory for it to be fully specified.
Add the extension to the request.

ERROR in ../../node_modules/@connectrpc/connect-query/dist/esm/index.js 15:0-105
Module not found: Error: Can't resolve './create-query-functions' in '/Users/daniel/project/node_modules/@connectrpc/connect-query/dist/esm'
Did you mean 'create-query-functions.js'?
BREAKING CHANGE: The request './create-query-functions' failed to resolve only because it was resolved as fully specified
(probably because the origin is strict EcmaScript Module, e. g. a module with javascript mimetype, a '*.mjs' file, or a '*.js' file where the package.json contains '"type": "module"').
The extension in the request is mandatory for it to be fully specified.
Add the extension to the request.

ERROR in ../../node_modules/@connectrpc/connect-query/dist/esm/index.js 16:0-64
Module not found: Error: Can't resolve './create-unary-functions' in '/Users/daniel/project/node_modules/@connectrpc/connect-query/dist/esm'
Did you mean 'create-unary-functions.js'?
BREAKING CHANGE: The request './create-unary-functions' failed to resolve only because it was resolved as fully specified
(probably because the origin is strict EcmaScript Module, e. g. a module with javascript mimetype, a '*.mjs' file, or a '*.js' file where the package.json contains '"type": "module"').
The extension in the request is mandatory for it to be fully specified.
Add the extension to the request.

ERROR in ../../node_modules/@connectrpc/connect-query/dist/esm/index.js 17:0-39
Module not found: Error: Can't resolve './utils' in '/Users/daniel/project/node_modules/@connectrpc/connect-query/dist/esm'
Did you mean 'utils.js'?
BREAKING CHANGE: The request './utils' failed to resolve only because it was resolved as fully specified
(probably because the origin is strict EcmaScript Module, e. g. a module with javascript mimetype, a '*.mjs' file, or a '*.js' file where the package.json contains '"type": "module"').
The extension in the request is mandatory for it to be fully specified.
Add the extension to the request.

ERROR in ../../node_modules/@connectrpc/connect-query/dist/esm/index.js 18:0-66
Module not found: Error: Can't resolve './use-transport' in '/Users/daniel/project/node_modules/@connectrpc/connect-query/dist/esm'
Did you mean 'use-transport.js'?
BREAKING CHANGE: The request './use-transport' failed to resolve only because it was resolved as fully specified
(probably because the origin is strict EcmaScript Module, e. g. a module with javascript mimetype, a '*.mjs' file, or a '*.js' file where the package.json contains '"type": "module"').
The extension in the request is mandatory for it to be fully specified.
Add the extension to the request.

ERROR in ../../node_modules/@connectrpc/connect-query/dist/esm/index.js 19:0-56
Module not found: Error: Can't resolve './create-unary-hooks' in '/Users/daniel/project/node_modules/@connectrpc/connect-query/dist/esm'
Did you mean 'create-unary-hooks.js'?
BREAKING CHANGE: The request './create-unary-hooks' failed to resolve only because it was resolved as fully specified
(probably because the origin is strict EcmaScript Module, e. g. a module with javascript mimetype, a '*.mjs' file, or a '*.js' file where the package.json contains '"type": "module"').
The extension in the request is mandatory for it to be fully specified.
Add the extension to the request.

webpack 5.88.2 compiled with 20 errors in 19953 ms

Manually editing node_modules/@connectrpc/connect-query/package.json and removing "type"="module" solves the issue

the client is generated with target=ts and then built to an internal module with typescript as a dual ESM/CJS package

I'm pinning the version to 0.5.1 for now

How to set `QueryObserverOptions`?

I end up customizing refetchInterval per query pretty often with Tanstack Query's useQuery. On some pages, I want rapid 1-second polls of a specific unary call, and on others just once a minute or so.

Is there a mechanism for setting any of the QueryObserverOptions, like refetchInterval, with a useQuery generated from @bufbuild/connect-query?

This doesn't appear to work at the moment, but should the full array of Tanstack Query's useQuery options (https://tanstack.com/query/v4/docs/react/reference/useQuery) be available to UnaryHooks.useQuery.options?

const { data: things } = useQuery(getThings.useQuery({}, { refetchInterval: 1000 }));

disableQuery unique symbol stopping typescript declaration

We use this lib to publish a client to npm that our different code bases can share. However, there seems to be issues I can't figure out surrounding unique symbol's and typescript declaration.

I noticed you have an exception for this case in your eslint, and it is for a test file to consume the generated lib.
Do you know of a work around beyond having an unused disableQuery import everywhere I'd want to use the generated code?

Getting "No QueryClient set, use QueryClientProvider to set one" error in version 1.0.0

Attempting to use connect-query by following the Quickstart documentation in a Next JS app but having issues. Receiving this error when useQuery is called…

No QueryClient set, use QueryClientProvider to set one

The problem appears to be with the wrapper around the @tanstack/react-query useQuery as using the original hook does not result in this error.

My app uses Yarn workspaces so thought it might be an issue with module resolution with my specific environment however I have tried creating a brand new Next JS app with NPM and still getting the same error. I have also tried different node versions and @tanstack/react-query versions.

This is the code, which is very close to the example in the docs…

import { TransportProvider, useQuery } from "@connectrpc/connect-query";
import { createConnectTransport } from "@connectrpc/connect-web";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { getUsers } from "clients/dist/user/user-UserService_connectquery";

const finalTransport = createConnectTransport({
  baseUrl: "",
});

const queryClient = new QueryClient();

export default function Web() {
  return (
    <TransportProvider transport={finalTransport}>
      <QueryClientProvider client={queryClient}>
        <Content />
      </QueryClientProvider>
    </TransportProvider>
  );
}

const Content = () => {
  const query = useQuery(getUsers);
  return <div>{query.data?.users.length}</div>;
};
// package.json
"@bufbuild/protobuf": "~1.3.0",
"@connectrpc/connect": "^1.1.2",
"@connectrpc/connect-query": "^1.0.0",
"@connectrpc/connect-web": "^1.1.2",
"@tanstack/react-query": "^5.13.4",

Any help appreciated! 😄

ES Module shipped in a CommonJS package?

Hey! Love the direction of this library and just did a huge refactor to convert all of our grpc calls to using connect query!

I'm in the middle of fixing up our tests and got this message from vistest:

Module /Users/greg/code/phc/location-fe/node_modules/@bufbuild/connect-query/dist/src/index.js:14 seems to be an ES Module but shipped in a CommonJS package. You might want to create an issue to the package "@bufbuild/connect-query" asking them to ship the file in .mjs extension or add "type": "module" in their package.json.

I'm not sure what the best path forward for you lib is, but I thought I would let you know about the warning.

Vitest also posted a work around, so I think I'm fine for now:

As a temporary workaround you can try to inline the package by updating your config:

// vitest.config.js
export default {
  test: {
    deps: {
      inline: [
        "@bufbuild/connect-query"
      ]
    }
  }
}

Cheers and thanks for all the work! 🎉

Increase deadline/timeout of the RPC call

Currently, I'm getting a 504 timeout error because the request takes too long. I haven't found any documentation on how to configure it.
Im using createGrpcWebTransport to init the transport but AFAIK it doens't have an option to set a timeout (related connectrpc/connect-es#799)
How do I increase the deadline/timeout of my RPC Call while using tanstack?

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.