npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

trpc-svelte-query-adapter

v2.3.14

Published

A simple adapter to use `@tanstack/svelte-query` with trpc, similar to `@trpc/react-query`.

Downloads

13,893

Readme

tRPC - svelte-query Adapter

NPM version License Last commit

[!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.

Installation

# npm
npm install trpc-svelte-query-adapter @trpc/client @trpc/server @tanstack/svelte-query

# yarn
yarn add trpc-svelte-query-adapter @trpc/client @trpc/server @tanstack/svelte-query

# pnpm
pnpm add 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
  • getQueryKey

You 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
  • createServerQueries

As 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:

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;

Client-Only Svelte

  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:
// 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<Router>({
	links: [
		httpBatchLink({
			// Replace this URL with that of your tRPC server
			url: 'http://localhost:5000/api/v1/trpc/',
		}),
	],
});

export const trpc = svelteQueryWrapper<Router>({ client });
  1. The exported tRPC client can then be used in svelte components as follows:
<script lang="ts">
  import { trpc } from "/path/to/lib/trpc";

  const foo = trpc.greeting.createQuery('foo', { retry: false });
</script>

{#if $foo.isPending}
  Loading...
{:else if $foo.isError}
  Error: {$foo.error.message}
{:else if $foo.data}
  {$foo.data.message}
{/if}

SvelteKit and SSR

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:

import type { QueryClient } from '@tanstack/svelte-query';

const client = createTRPCProxyClient<Router>({
	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<Router>({
		client,
		queryClient,
	});
}

Which can then be used in a component as such:

<!-- routes/+page.svelte -->
<script lang="ts">
  import { trpc } from "$lib/trpc/client";

  const client = trpc();
  const foo = client.greeting.createQuery("foo", { retry: false });
</script>

<p>
  {#if $foo.isPending}
    Loading...
  {:else if $foo.isError}
    Error: {$foo.error.message}
  {:else}
    {$foo.data}
  {/if}
</p>

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:

let browserClient: ReturnType<typeof createTRPCClient<Router>>;

export function trpc(init?: TRPCClientInit) {
	const isBrowser = typeof window !== 'undefined';
	if (isBrowser && browserClient) return browserClient;
	const client = createTRPCClient<Router>({ init });
	if (isBrowser) browserClient = client;
	return client;
}

to this:

import { svelteQueryWrapper } from 'trpc-svelte-query-adapter';
import type { QueryClient } from '@tanstack/svelte-query';

let browserClient: ReturnType<typeof svelteQueryWrapper<Router>>;

export function trpc(init?: TRPCClientInit, queryClient?: QueryClient) {
	const isBrowser = typeof window !== 'undefined';
	if (isBrowser && browserClient) return browserClient;
	const client = svelteQueryWrapper<Router>({
		client: createTRPCClient<Router>({ 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.

// +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:

<!-- +page.svelte -->
<script lang="ts">
  import { page } from "$app/stores";
  import type { PageData } from "./$types";

  export let data: PageData;

  const foo = data.foo();
  const queries = data.queries();
</script>

{#if $foo.isPending}
  Loading...
{:else if $foo.isError}
  {$foo.error}
{:else if $foo.data}
  {$foo.data}
{/if}
<br /><br />

{#each $queries as query}
  {#if query.isPending}
    Loading...
  {:else if query.isError}
    {query.error.message}
  {:else if query.data}
    {query.data}
  {/if}
  <br />
{/each}

You can also optionally pass new inputs to the queries and infinite queries from the client side(see #34, #47) like so:

<script lang="ts">
  import { page } from "$app/stores";
  import type { PageData } from "./$types";

  import { derived, writable } from '@svelte/store';

  export let data: PageData;

  const name = writable('foo');
  const newNames = writable<string[]>([]);

  const foo = data.foo($name);

  // You can also access the default input if you pass in a callback as the new input:
  // const foo = data.foo((old) => derived(name, ($name) => old + name));

  const queries = data.queries((t, old) => derived(newNames, ($newNames) => [...old, ...$newNames.map((name) => t.greeting(name))]));
</script>

<div>
  {#if $foo.isPending}
    Loading...
  {:else if $foo.isError}
    {$foo.error}
    {:else if $foo.data}
    {$foo.data}
  {/if}
  <input bind:value={$name} />
</div>

<br />

<div>
  {#each $queries as query}
    {#if query.isPending}
      Loading...
    {:else if query.isError}
      {query.error.message}
      {:else if query.data}
      {query.data}
    {/if}
    <br />
  {/each}

  <form on:submit|preventDefault={(e) => {
    const data = new FormData(e.currentTarget).get('name');
    if (typeof data === 'string') $newNames.push(data);
    $newNames = $newNames;
  }}>
    <input name="name" />
    <button type="submit">Submit</button>
  </form>
</div>

For more usage examples, you can refer to the example app provided in the repo.