tRPC-SvelteKit

repository·main·Indexed 21 days ago

https://github.com/icflorescu/trpc-sveltekit

A SvelteKit adapter for trpc.io that provides end-to-end type-safe APIs by integrating tRPC with SvelteKit's server-side capabilities. It includes tools for creating routers and contexts, a SvelteKit handle via createTRPCHandle, and a client via createTRPCClient. The library also offers experimental WebSocket support for @sveltejs/adapter-node using createTRPCWebSocketServer and createTRPCWebSocketClient.

Tokens
5.4K
Snippets
20
Records
23
Agent score
73%

What's inside trpc-sveltekit

  1. Quickstart: Set up tRPC-SvelteKit

    main

    To integrate tRPC-SvelteKit into your SvelteKit application, follow these steps to set up the router, context, and server handle.

    1. Install dependencies:

      yarn add trpc-sveltekit @trpc/server @trpc/client
    2. Create a tRPC router (e.g., in lib/trpc/router.ts): Initialize tRPC using your context type and define your procedures.

    3. Create a tRPC context (e.g., in lib/trpc/context.ts): Define an async function that returns the context object used by your procedures.

    4. Add the tRPC handle to SvelteKit hooks (in hooks.server.ts): Use createTRPCHandle to bridge SvelteKit requests to your tRPC router.

    yarn add trpc-sveltekit @trpc/server @trpc/client
  2. Configure WebSocket support in Vite and SvelteKit

    main

    To enable the experimental WebSocket server, you must modify your build configuration files.

    In vite.config.ts:

    import { sveltekit } from '@sveltejs/kit/vite';
    import type { UserConfig } from 'vite';
    import { vitePluginTrpcWebSocket } from 'trpc-sveltekit/websocket';
    
    const config: UserConfig = {
      plugins: [
        sveltekit(),
        vitePluginTrpcWebSocket
      ]
    };
    
    export default config;

    In svelte.config.js:

    import adapter from '@sveltejs/adapter-node';
    // ...

    Create wsServer.js (next to package.json):

    import { SvelteKitTRPCWSServer } from "trpc-sveltekit/websocket";
    
    SvelteKitTRPCWSServer(import.meta.url);

    Update package.json scripts:

    {
      "scripts": {
        "start": "node ./wsServer"
      }
    }
  3. Use the tRPC client in Svelte components

    main

    To call tRPC procedures from your Svelte pages, it is recommended to define a helper function that manages a singleton client instance for the browser. This ensures you can pass the $page store to the client to maintain context.

    1. Define the client helper (e.g., in lib/trpc/client.ts): Use createTRPCClient<Router> and handle the browser singleton pattern.

    2. Call procedures in a component: Import your helper and call the procedure. Note that when using the standard HTTP client, you pass the $page store to the client call.

    <script lang="ts">
      import { page } from '$app/stores';
      import { trpc } from '$lib/trpc/client';
    
      let greeting = 'press the button to load data';
      let loading = false;
    
      const loadData = async () => {
        loading = true;
        greeting = await trpc($page).greeting.query();
        loading = false;
      };
    </script>
    
    <a href="#load" role="button" on:click|preventDefault={loadData}>Load</a>
    <p>{greeting}</p>
    // lib/trpc/client.ts
    import type { Router } from '$lib/trpc/router';
    import { createTRPCClient, type TRPCClientInit } from 'trpc-sveltekit';
    
    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;
    }
  4. Experimental WebSocket support

    main

    If you are using @sveltejs/adapter-node, you can enable experimental WebSocket support to handle tRPC procedure calls via WebSockets.

    Important Caveats:

    • Works exclusively with @sveltejs/adapter-node.
    • The URL is hardcoded to /trpc.
    • All tRPC methods are handled via WebSockets (not just subscriptions).
    • Prerendering is not supported.

    Setup Steps:

    1. Install dependencies:

      yarn add trpc-sveltekit @trpc/server @trpc/client @sveltejs/adapter-node ws
    2. Configure Vite (vite.config.ts) to include vitePluginTrpcWebSocket.

    3. Configure SvelteKit (svelte.config.js) to use @sveltejs/adapter-node.

    4. Create a server entrypoint (wsServer.js) using SvelteKitTRPCWSServer.

    5. Update package.json to start the server via the entrypoint (e.g., node ./wsServer).

    6. Initialize the WebSocket server in hooks.server.ts using createTRPCWebSocketServer. You must guard this call with !building to prevent errors during prerendering.

    7. Use the WebSocket client in your pages using createTRPCWebSocketClient.

    yarn add trpc-sveltekit @trpc/server @trpc/client @sveltejs/adapter-node ws
  5. Use the tRPC WebSocket client

    main

    When using WebSocket support, use createTRPCWebSocketClient to create the proxy client. Unlike the standard HTTP client, this client does not require the $page store passed to its method calls.

    // lib/trpc/client.ts
    import type { Router } from '$lib/trpc/router';
    import { createTRPCWebSocketClient } from "trpc-sveltekit/websocket";
    
    let browserClient: ReturnType<typeof createTRPCWebSocketClient<Router>>;
    
    export function trpc() {
      const isBrowser = typeof window !== 'undefined';
      if (isBrowser && browserClient) return browserClient;
      const client = createTRPCWebSocketClient<Router>();
      if (isBrowser) browserClient = client;
      return client;
    }
    
    // Usage in +page.svelte
    // const greeting = await trpc().greeting.query();
  6. Create a tRPC context

    main

    The context is an object passed to every tRPC procedure. In SvelteKit, you typically create this using the RequestEvent from @sveltejs/kit.

    // lib/trpc/context.ts
    import type { RequestEvent } from '@sveltejs/kit';
    import type { inferAsyncReturnType } from '@trpc/server';
    
    export async function createContext(event: RequestEvent) {
      return {
        // context information
      };
    }
    
    export type Context = inferAsyncReturnType<typeof createContext>;
    // lib/trpc/context.ts
    import type { RequestEvent } from '@sveltejs/kit';
    import type { inferAsyncReturnType } from '@trpc/server';
    
    // we're not using the event parameter is this example,
    // hence the eslint-disable rule
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    export async function createContext(event: RequestEvent) {
      return {
        // context information
      };
    }
    
    export type Context = inferAsyncReturnType<typeof createContext>;
  7. Create a tRPC router

    main

    Define your API surface by creating a router using initTRPC. You must pass your context type to initTRPC.context<Context>() to ensure end-to-end type safety.

    // lib/trpc/router.ts
    import type { Context } from '$lib/trpc/context';
    import { initTRPC } from '@trpc/server';
    import delay from 'delay';
    
    export const t = initTRPC.context<Context>().create();
    
    export const router = t.router({
      greeting: t.procedure.query(async () => {
        await delay(500); // simulate an expensive operation
        return `Hello tRPC v10 @ ${new Date().toLocaleTimeString()}`;
      })
    });
    
    export type Router = typeof router;
    // lib/trpc/router.ts
    import type { Context } from '$lib/trpc/context';
    import { initTRPC } from '@trpc/server';
    import delay from 'delay';
    
    export const t = initTRPC.context<Context>().create();
    
    export const router = t.router({
      greeting: t.procedure.query(async () => {
        await delay(500); // 👈 simulate an expensive operation
        return `Hello tRPC v10 @ ${new Date().toLocaleTimeString()}`;
      })
    });
    
    export type Router = typeof router;
  8. Configure SvelteKit hooks with createTRPCHandle

    main

    To enable tRPC endpoints in your SvelteKit app, you must register the tRPC handler in your hooks.server.ts file using createTRPCHandle.

    // hooks.server.ts
    import { createContext } from '$lib/trpc/context';
    import { router } from '$lib/trpc/router';
    import type { Handle } from '@sveltejs/kit';
    import { createTRPCHandle } from 'trpc-sveltekit';
    
    export const handle: Handle = createTRPCHandle({ router, createContext });
  9. Initialize WebSocket server in SvelteKit hooks

    main

    In hooks.server.ts, you must initialize the WebSocket server. It is critical to wrap this in a !building check to avoid failures during the SvelteKit build/prerender process.

    // hooks.server.ts
    import { createContext } from '$lib/trpc/context';
    import { router } from '$lib/trpc/router';
    import { createTRPCWebSocketServer } from "trpc-sveltekit/websocket";
    import { building } from '$app/environment';
    
    if (!building) createTRPCWebSocketServer({ router, createContext })
  10. Configure the Vite plugin for tRPC WebSocket support

    main

    To enable experimental WebSocket support during development, add vitePluginTrpcWebSocket to your vite.config.ts plugins array. This plugin initializes a global WebSocket server instance and attaches an upgrade handler to the Vite development server's HTTP server, allowing tRPC WebSocket connections to be handled during local development.

    import { sveltekit } from '@sveltejs/kit/vite';
    import { defineConfig } from 'vite';
    import { vitePluginTrpcWebSocket } from 'trpc-sveltekit/websocket'; // Note: exact import path depends on package exports
    
    export default defineConfig({
      plugins: [
        sveltekit(),
        vitePluginTrpcWebSocket
      ]
    });
  11. Configure onError in createTRPCHandle

    main

    The onError option allows you to intercept and handle tRPC errors globally. The callback receives an object with these properties:

    • ctx: The tRPC context.
    • error: The TRPCError instance.
    • path: The procedure path.
    • input: The input provided to the procedure.
    • req: The RequestInit object.
    • type: The ProcedureType or 'unknown'.
  12. Import tRPC-SvelteKit client and server modules

    main

    The trpc-sveltekit package exports all functionality from its client and server modules. You can import the necessary tools for both the tRPC server-side setup (routers, context, handlers) and the client-side usage (tRPC client) directly from the package entrypoint.

    import { ... } from 'trpc-sveltekit';