A simple adapter to use `@tanstack/svelte-query` with trpc, similar to `@trpc/react-query`.
npm install trpc-svelte-query-adaptertRPC - svelte-query Adapter[![NPM version][npm-image]][npm-url]
[![License][license-image]][license-url]
[![Last commit][last-commit-image]][repo-url]
> [!NOTE]
> The README on npmjs might not be fully up to date. Please refer to
> the README on the Github Repo for the latest setup instructions.
An adapter to call tRPC procedures wrapped with @tanstack/svelte-query, similar to @trpc/react-query. This is made possible using proxy-deep.
``shnpm
npm install trpc-svelte-query-adapter @trpc/client @trpc/server @tanstack/svelte-query
If you are using client-side Svelte, you would need to install
@trpc/server as a devDependency using --save-dev.Available Functions
The following functions from
@trpc/react-query are ported over:-
useQuery -> createQuery
- useInfiniteQuery -> createInfiniteQuery
- useMutation -> createMutation
- useSubscription -> createSubscription
- useQueries -> createQueries
- useUtils -> createUtils
- getQueryKeyYou can refer to tanstack-query docs and @trpc/react-query docs for documentation on how to use them.
There are also some new procedures that are only relevant for SvelteKit:
-
createServerQuery
- createServerInfiniteQuery
- createServerQueriesAs for these procedures, you can refer to the Server-Side Query Pre-Fetching section.
Usage
The following instructions assume the
tRPC router to have the following procedures:`typescript
export const router = t.router({
greeting: t.procedure
.input((name: unknown) => {
if (typeof name === 'string') return name; throw new Error(
Invalid input: ${typeof name});
})
.query(async ({ input }) => {
return Hello, ${input} from tRPC v10 @ ${new Date().toLocaleTimeString()};
}),
});export type Router = typeof router;
`$3
1. Setup
@tanstack/svelte-query as per svelte-query docs.
2. Setup @trpc/client and export the tRPC client.
3. Wrap the exported tRPC client with svelteQueryWrapper from trpc-svelte-query-adapter, as demonstrated in the example below:`typescript
// src/lib/trpc.ts
import type { Router } from '/path/to/trpc/router';
import { createTRPCProxyClient, httpBatchLink } from '@trpc/client';import { svelteQueryWrapper } from 'trpc-svelte-query-adapter';
const client = createTRPCProxyClient({
links: [
httpBatchLink({
// Replace this URL with that of your tRPC server
url: 'http://localhost:5000/api/v1/trpc/',
}),
],
});
export const trpc = svelteQueryWrapper({ client });
`4. The exported
tRPC client can then be used in svelte components as follows:`svelte
{#if $foo.isPending}
Loading...
{:else if $foo.isError}
Error: {$foo.error.message}
{:else if $foo.data}
{$foo.data.message}
{/if}
`$3
For SvelteKit, the process is pretty much the same as for client-only svelte. However, if you intend to call queries from the server in a
load function, you would need to setup @tanstack/svelte-query according to the the ssr example in the svelte-query docs.Upon doing that, you would also need to pass in the
queryClient to svelteQueryWrapper when initializing on the server, which you can get by calling the event.parent method in the load function. You can see an example of this in the Server-Side Query Pre-Fetching section. For this purpose, you might also want to export your client wrapped in a function that optionally takes in queryClient and passes it onto svelteQueryWrapper.Here is an example of what that might look like:
`typescript
import type { QueryClient } from '@tanstack/svelte-query';const client = createTRPCProxyClient({
links: [
httpBatchLink({
// Replace this URL with that of your tRPC server
url: 'http://localhost:5000/api/v1/trpc/',
}),
],
});
export function trpc(queryClient?: QueryClient) {
return svelteQueryWrapper({
client,
queryClient,
});
}
`Which can then be used in a component as such:
`svelte
{#if $foo.isPending}
Loading...
{:else if $foo.isError}
Error: {$foo.error.message}
{:else}
{$foo.data}
{/if}
`The main thing that needs to passed in to
svelteQueryWrapper is the tRPC client itself. So, this adapter should support different implementations of tRPC for Svelte and SvelteKit. For example, if you are using trpc-sveltekit by icflorescu, all you would need to do after setting it up would be to change the client initialization function from something like this:`typescript
let browserClient: ReturnType>;export function trpc(init?: TRPCClientInit) {
const isBrowser = typeof window !== 'undefined';
if (isBrowser && browserClient) return browserClient;
const client = createTRPCClient({ init });
if (isBrowser) browserClient = client;
return client;
}
`to this:
`typescript
import { svelteQueryWrapper } from 'trpc-svelte-query-adapter';
import type { QueryClient } from '@tanstack/svelte-query';let browserClient: ReturnType>;
export function trpc(init?: TRPCClientInit, queryClient?: QueryClient) {
const isBrowser = typeof window !== 'undefined';
if (isBrowser && browserClient) return browserClient;
const client = svelteQueryWrapper({
client: createTRPCClient({ init }),
queryClient,
});
if (isBrowser) browserClient = client;
return client;
}
`Which can then be initialized and used in the way that it is described in its docs.
#### Server-Side Query Pre-Fetching
This adapter provides 3 additional procedures:
createServerQuery, createServerInfiniteQuery and createServerQueries, which can be used to call their counterpart procedures in the load function in either a +page.ts or +layout.ts. These procedures return a promise and therefore can only really be called on the server.By default, these 3 procedures will pre-fetch the data required to pre-render the page on the server. However, if you wish to disable this behaviour on certain queries, you can do so by setting the
ssr option to false.These procedures can be used as such:
> [!NOTE]
> Gotta await top-level promises to pre-fetch data from SvelteKit v2.
`typescript
// +page.ts
// tRPC is setup using trpc-sveltekit for this example.
import { trpc } from '$lib/trpc/client';
import type { PageLoad } from './$types';export const load = (async (event) => {
const { queryClient } = await event.parent();
const client = trpc(event, queryClient);
return {
foo: await client.greeting.createServerQuery('foo'),
queries: await client.createServerQueries(
(t) =>
['bar', 'baz'].map((name) => t.greeting(name, { ssr: name !== 'baz' })) // pre-fetching disabled for the
baz query.
),
};
}) satisfies PageLoad;
`Then, in the component:
`svelte
{#if $foo.isPending}
Loading...
{:else if $foo.isError}
{$foo.error}
{:else if $foo.data}
{$foo.data}
{/if}
{#each $queries as query}
{#if query.isPending}
Loading...
{:else if query.isError}
{query.error.message}
{:else if query.data}
{query.data}
{/if}
{/each}
`You can also optionally pass new inputs to the queries and infinite queries from the client side(see #34, #47) like so:
`svelte
{#if $foo.isPending}
Loading...
{:else if $foo.isError}
{$foo.error}
{:else if $foo.data}
{$foo.data}
{/if}
{#each $queries as query}
{#if query.isPending}
Loading...
{:else if query.isError}
{query.error.message}
{:else if query.data}
{query.data}
{/if}
{/each}
``For more usage examples, you can refer to the example app provided in the repo.
[npm-url]: https://npmjs.org/package/trpc-svelte-query-adapter
[npm-image]: https://img.shields.io/npm/v/trpc-svelte-query-adapter.svg
[license-url]: LICENSE
[license-image]: http://img.shields.io/npm/l/trpc-svelte-query-adapter.svg
[repo-url]: https://github.com/vishalbalaji/trpc-svelte-query-adapter
[last-commit-image]: https://img.shields.io/github/last-commit/vishalbalaji/trpc-svelte-query-adapter