tRPC-ified SWR hooks

Overview

trpc-swr

tRPC-ified SWR hooks

Installation

npm install trpc-swr @trpc/client

Usage

First, create your fully typed hooks using your router type:

// trpc.ts
import { createSWRHooks } from 'trpc-swr'
// `import type` ensures this import is fully erased at runtime
import type { AppRouter } from './router'

export const trpc = createSWRHooks<AppRouter>()

Then, add the trpc.TRPCProvider to your root App component:

// _app.tsx
import { createTRPCClient } from '@trpc/client'
import { trpc } from '../utils/trpc'

const App = () => {
  // create a tRPC vanilla client
  // see https://trpc.io/docs/vanilla
  // note that you should pass data transformers (https://trpc.io/docs/data-transformers) here
  const [client] = useState(() =>
    createTRPCClient({ url: 'http://localhost:3000/api/trpc' })
  )

  return (
    <trpc.TRPCProvider client={client}>
      <Component {...pageProps} />
    </trpc.TRPCProvider>
  )
}

Tip: For SWR's global configuration, wrap this provider with SWRConfig.

useSWR

Now use trpc to query in a component:

// profile.tsx
import { trpc } from './trpc'

const Profile = (props: { userId: string }) => {
  const { data, isValidating } = trpc.useSWR(['user.get', { id: props.userId }])

  return (
    <div>
      Name: {!data && isValidating
        ? 'loading...'
        : data
        ? data.name
        : 'User does not exist'}
    </div>
  )
}

trpc.useSWR functions the same and accepts all the options that SWR's useSWR hook does. It is only a very small wrapper that adds tRPC types and creates a fetcher using tRPC's vanilla client.

Mutations

You can use trpc.useContext to get a tRPC client for mutations:

// profile.tsx
import { trpc } from './trpc'

const Profile = (props: { userId: string }) => {
  // get `mutate` from trpc.useSWR
  // this is a bound mutate (https://swr.vercel.app/docs/mutation#bound-mutate)
  const { data, mutate, isValidating } = trpc.useSWR(['user.get', {
    id: props.userId,
  }])
  const { client } = trpc.useContext()

  return (
    <div>
      <div>
        Name: {!data && isValidating
          ? 'loading...'
          : data
          ? data.name
          : 'User does not exist'}
      </div>

      <button
        onClick={() => {
          // you would typically get this from user input
          // but it is hardcoded here to simplify the example
          const newName = 'Jack'

          // `mutate` revalidates the `user.get` key above
          // so it is refetched after the mutation is complete
          mutate(
            () => {
              return client.mutation('user.changeName', {
                id: props.userId,
                newName,
              })
            }, // use optimisticData to show new name before mutation completes
            { optimisticData: { name: newName } },
          )
        }}
      >
      </button>
    </div>
  )
}

You can also use trpc.useContext to get a mutate function which is the same as SWR's global mutate. However, you will have the pass the same key, meaning the query path and input that you passed to useSWR. Here it is with the same example as above:

// profile.tsx
import { trpc } from './trpc'

const Profile = (props: { userId: string }) => {
  const { data, isValidating } = trpc.useSWR(['user.get', {
    id: props.userId,
  }])

  // get `mutate` from `trpc.useContext`
  const { client, mutate } = trpc.useContext()

  return (
    <div>
      <div>
        Name: {!data && isValidating
          ? 'loading...'
          : data
          ? data.name
          : 'User does not exist'}
      </div>

      <button
        onClick={() => {
          const newName = 'Jack'

          mutate(
            // must pass in exact same query path and input
            // to revalidate the query key
            // note that you can use `matchMutate` to
            // revalidate query keys with the same path
            ['user.get', { id: props.userId }],
            () => {
              return client.mutation('user.changeName', {
                id: props.userId,
                newName,
              })
            },
            { optimisticData: { name: newName } },
          )
        }}
      >
      </button>
    </div>
  )
}

useSWRInfinite

trpc-swr also provides a useSWRInfinite wrapper. Create your typed useSWRInfinite:

// trpc.ts
import { createSWRHooks } from 'trpc-swr'
import { getUseSWRInfinite } from 'trpc-swr/infinite'

// `import type` ensures this import is fully erased at runtime
import type { AppRouter } from './router'

export const trpc = createSWRHooks<AppRouter>()
export const useSWRInfinite = getUseSWRInfinite<AppRouter>()

This requires using getUseSWRInfinite and passing in the AppRouter type again, so we can take full advantage of tree shaking and remove the functions that your app does not use.

Now use it in a component:

// users.tsx
import { useSWRInfinite } from './trpc'

const Users = () => {
  const { data, size, setSize } = useSWRInfinite(
    // pass in path
    'user.get',
    (index, previousPageData) => {
      if (index !== 0 && !previousPageData) return null

      // return a value for the input of the path you passed
      // `user.get` in this case
      return [{ id: index }]
    },
  )

  if (!data) {
    return <div>Loading...</div>
  }

  return (
    <>
      <div>
        {data.map((user) => {
          return <p key={user.name}>{user.name}</p>
        })}
      </div>

      <button onClick={() => setSize(size + 1)}>Load More Users</button>
    </>
  )
}

Utility

matchMutate

The matchMutate utility allows you to invalidate query keys that match a tRPC route. Create your typed useMutateMutate function:

// trpc.ts
import { createSWRHooks, getUseMatchMutate } from 'trpc-swr'
// `import type` ensures this import is fully erased at runtime
import type { AppRouter } from './router'

export const trpc = createSWRHooks<AppRouter>()
export const useMatchMutate = getUseMatchMutate<AppRouter>()

Now use it in a component:

import { trpc, useMatchMutate } from './trpc'

// profiles.tsx
const Profiles = () => {
  const userBobData = trpc.useSWR([
    'user.get',
    {
      name: 'Bob',
    },
  ])

  const userAvaData = trpc.useSWR([
    'user.get',
    {
      name: 'Ava',
    },
  ])

  const matchMutate = useMatchMutate()

  return (
    <div>
      {[userBobData, userAvaData].map(({ data: user, isValidating }) => (
        <div>
          Name: {!data && isValidating
            ? 'loading...'
            : data
            ? data.name
            : 'User does not exist'}
        </div>
      ))}
      <button onClick={() => matchMutate('user.get')}>
        Revalidate all tRPC `user.get` queries
      </button>
    </div>
  )
}
Comments
Releases(v0.1.5)
  • v0.1.5(Aug 9, 2022)

  • v0.1.3(Aug 1, 2022)

  • v0.1.2(Jul 31, 2022)

    What's Changed

    • use mutate from SWRConfiguration context by @briancoit in https://github.com/sachinraja/trpc-swr/pull/8

    New Contributors

    • @briancoit made their first contribution in https://github.com/sachinraja/trpc-swr/pull/8

    Full Changelog: https://github.com/sachinraja/trpc-swr/compare/v0.1.1...v0.1.2

    Source code(tar.gz)
    Source code(zip)
  • v0.1.1(Jul 9, 2022)

  • v0.1.0(May 17, 2022)

    Features

    • useSWR wrapper
    • useSWRInfinite wrapper
    • global mutate wrapper
    • matchMutate utility

    Full Changelog: https://github.com/sachinraja/trpc-swr/commits/v0.1.0

    Source code(tar.gz)
    Source code(zip)
Owner
Sachin Raja
full-stack, fast ⚡
Sachin Raja
React-query devtools for swr

React-query devtools for swr

Erfan Khadivar 12 Aug 14, 2022
End-to-end typesafe APIs with tRPC.io in SvelteKit applications

✨ tRPC-SvelteKit End-to-end typesafe APIs with tRPC.io in SvelteKit applications. No code generation, run-time bloat, or build pipeline. ❤️ ???? See b

Ionut-Cristian Florescu 307 Dec 29, 2022
Prisma 2+ generator to emit fully implemented tRPC routers

Prisma tRPC Generator Automatically generate fully implemented tRPC routers from your Prisma Schema. This includes routers, app router and of course a

Omar Dulaimi 370 Jan 3, 2023
Prisma +2 generator to emit a tRPC shield from your Prisma schema

Prisma tRPC Shield Generator Automatically generate a tRPC Shield from your Prisma Schema. Updates every time npx prisma generate runs. Table of Conte

Omar Dulaimi 27 Dec 24, 2022
Proof of concept: support immutable trpc servers using lambdas to ensure client/server compatibility

auto-versioned-trpc-aws-lambda Proof of concept to support an automatically versioned AWS Lambda running tRPC to ensure a somewhat graceful and automa

Kenneth Skovhus 5 Aug 30, 2022
tRPC test & precursor to a billion dollar social cat network

CatMash What is this? Have you ever wanted to rank 11,000 cat pictures by cuteness? Of course you have. That's what we're doing here. This is an app b

Christopher Ehrlich 6 Dec 18, 2022
OpenAPI support for tRPC 🧩

trpc-openapi OpenAPI support for tRPC ?? Easy REST endpoints for your tRPC procedures. Perfect for incremental adoption. OpenAPI version 3.0.3. Usage

James Berry 841 Jan 9, 2023
Scaffold a full-stack SvelteKit application with tRPC and WindiCSS out of the box

create-sweet-app Interactive CLI to quickly set up an opinionated, full-stack, typesafe SvelteKit project. Inspired by the T3 Stack and create-t3-app

David Hrabě 10 Dec 16, 2022
NX monorepo showing the TMDB Watchlist mobile app with Expo, tRPC, Next, and Prisma

tmdb-watchlist-prisma This app uses TMDB to retrieve a list of Now Playing movies. You can add/remove movies to track which ones you've watched. There

Mat Warger 65 Dec 28, 2022
Qwik City adapter for trpc.io

tRPC ?? Qwik City End-to-end typesafe APIs made easy with tRPC.io in Qwik City applications. Build & consume fully typesafe APIs, without schemas or c

Giorgio Boa 29 Oct 11, 2022
A fullstack TikTok clone with Nextjs, Prisma, trpc

TikTok Clone A fullstack TikTok clone with Nextjs, Prisma, trpc Live demo Official website: https://toptop-clone.vercel.app/ Main technology used The

Phong Nguyen 115 Dec 19, 2022
Kaol Stack - Prisma, Expo, Next, TRPC, Solito, Tailwind - A monorepo template for a truly universal app

Kaol Stack ?? About A monorepo with Prisma, Next.js, Expo, tRPC, Authentication and Solito setup and configured to work together. With this setup you

Matheus Vicente 187 Dec 21, 2022
Next.js + tRPC + Blitz.js Auth + Prisma. Fullstack app example

This is a Next.js project bootstrapped with create-next-app. Getting Started First, run the development server: npm run dev # or yarn dev Open http://

Daiki Okumura 6 Oct 12, 2022
A super simple minimal tRPC for next-js

Typed Routes A way to have fully typed routes in next, without all the complexity of tRPC. This is more for super minimal use cases, where you don't n

Rahul Tarak 4 Dec 28, 2022
tRPC + Next.js

next-trpc A drop-in version of tRPC + Next.js. If you haven't seen tRPC before, it's an excellent Next.js-native solution vs a traditional REST or Gra

ianh.eth 5 Dec 21, 2022
Tesodev-search-app - Personal Search App with React-Hooks

Tesodev-search-app Personal Search App with React-Hooks View on Heroku : [https://tesodev-staff-search-app.herokuapp.com/] Instructions Clone this rep

Rahmi Köse 1 Nov 10, 2022
Finance-Tracker - A basic finance-tracker application built using Next.js, React Hooks and Context API

Next.js + Tailwind CSS Example This example shows how to use Tailwind CSS (v3.0) with Next.js. It follows the steps outlined in the official Tailwind

Osemwengie Benjamin 1 Jan 2, 2022
Minimal Typescript / NextJS dApp template bootstrapped with wagmi Ethereum react hooks library.

Welcome to the NextJS wagmi starter template ?? Looking to get up and running with a Typescript / NextJS dApp as quickly as possible? You're in the ri

Seth 78 Jan 4, 2023
Atomico a micro-library for creating webcomponents using only functions, hooks and virtual-dom.

Atomico simplifies learning, workflow and maintenance when creating webcomponents. Scalable and reusable interfaces: with Atomico the code is simpler

Atomico 898 Dec 31, 2022