/solid-trpc

tRPC Adapter For SolidJS Using Tanstack Solid Query.

Primary LanguageTypeScript

Solid tRPC

ssr is labled under the ssr tag because it is using @adeora/solid-query instead of @tanstack/solid-query - It is planned to be released under the tanstack org on version 5. Check out the ssr branch

Getting Started

I recommend using Create JD App but if you want to create a project from scratch, you can follow the steps below:

Installation

npm install @trpc/client @trpc/server solid-trpc@next @tanstack/solid-query

Creating A Client

// utils/trpc.ts
import { IAppRouter } from "@/whereMyRouterAt"; // your router type
import { createTRPCSolid } from "solid-trpc";
import { httpBatchLink } from "@trpc/client";
import { QueryClient } from "@tanstack/solid-query";

export const trpc = createTRPCSolid<IAppRouter>();
export const client = trpc.createClient({
  links: [
    httpBatchLink({
      url: "/api/trpc",
    }),
  ],
});
export const queryClient = new QueryClient();

TRPC Provider

// entry-client.tsx
import { mount, StartClient } from "solid-start/entry-client";
import { client, queryClient, trpc } from "./utils/trpc";

mount(
  () => (
    <trpc.Provider client={client} queryClient={queryClient}>
        <StartClient />
    </trpc.Provider>
  ),
  document
);

Example

// routes/example.tsx
import { trpc } from "./utils/trpc";
import { createSignal } from "solid-js";
const [name, setName] = createSignal("");
const res = trpc.queryName.useQuery(() => ({ name: name() })); // this will be called onMount and when name changes

export default function Example() {
   return (
        {...}
     )
}

Reactivity

Input

Solid tRPC input is considered to be a Solid accessor, meaning that you are required to pass a callback and SQ will rerun the tRPC endpoint whenever the signal changes.

const [name, setName] = createSignal("John");

trpc.example.hello.useQuery(name); // ✅
trpc.example.hello.useQuery(name()); // ❌

trpc.example.useQuery(()=> name().slice(1)); // ✅
// or even
trpc.example.useQuery(()=> "John"); // ✅ will be called once

Output

To use the output of the query, you must remember to not destructure elements. Doing so violates the reactivity of the query result. This principle is general to Solid, please read more here solidjs/solid#408 (comment).

const { data, refetch } = trpc.example.hello.useQuery(name()); // ❌ data will be evaluated only once and not update

const NavBar = () => {
  return <div>{data}</div>;
}

// valid

const queryRes = trpc.example.hello.useQuery(name()); // ✅ 

const NavBar = () => {
  return <div>{queryRes.data}</div>;
}

Enabled Property

If you are using the enabled property make sure you follow Solid Query rules:

const [enabled, setEnabled] = createSignal(false);
const query = trpc.queryName.useQuery(() => "hey there", {
  // ❌ passing a signal directly is not reactive
  // enabled: enabled(),

  // ✅ passing a function that returns a signal is reactive
  get enabled() {
    return enabled();
  },
});

Query Invalidation

To invalidate queries, please see the following example of using utils which come as part of the trpc object you created through createTRPCSolid.

// NavBar.tsx
const queryRes = trpc.example.hello.useQuery(name()); // ✅ this will refetch on invalidation in Footer.tsx

const NavBar = () => {
  return <div>{queryRes.data}</div>;
}
// Footer.tsx
const Footer = () => {
  const trpcUtils = trpc.useContext()
  
  const sayHelloAgain = () => {
    trpcUtils.example.hello.invalidate()
  }
  
  return <button onClick={sayHelloAgain}>say hello again</div>;
}