vishalbalaji / trpc-svelte-query-adapter

A simple adapter to use `@tanstack/svelte-query` with trpc, similar to `@trpc/react-query`.
71 stars 6 forks source link

trpc client error #24

Closed Schmell closed 6 months ago

Schmell commented 6 months ago

I get Unexpected token '<', "<!DOCTYPE "... is not valid JSON when running the example. i figure its the httpBatchLink that is the culprit here, but i can't figure out what this url is supposed to point to. I read the tRPC docs and cannot seem to wrap my mind around this. I thought maybe you could help

vishalbalaji commented 6 months ago

Hi @Schmell, could you please provide a small snippet of how you're setting up your tRPC client or preferably a reproduction of this issue that I can use as a reference while I look into this?

Schmell commented 6 months ago

I cannot get a working example on stackblitz. (what a surprise) Any way I am really just using the getting started files provided, by icflorescu (which works) and then modify the files according to your docs.

After playing for a bit I discovered that I do in fact need an endpoint at the url that is added in the httpBatchLink , but then i get a transform error. I am just confused about why i need another endpoint. I thought that the function in the routes (i.e.: greeting) is the endpoint. And if I just need and endpoint what is supposed to be there (because a GET endpoint does not work)

I think it might be helpful if you could provide a working example, but any help would be appreciated.

Anyway. Here are the files I am using.

lib/trpc/router.ts

// lib/trpc/router.ts
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;

lib/trpc/context.ts

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

hooks.server.ts

// This is the only file that is different from the docs
// hooks.server.ts
import { auth } from '$lib/server/lucia'
import type { Handle } from '@sveltejs/kit'
import { sequence } from '@sveltejs/kit/hooks'

import { createContext } from '$lib/trpc/context'
import { router } from '$lib/trpc/router'
import { createTRPCHandle } from 'trpc-sveltekit'

export const trpc: Handle = createTRPCHandle({ router, createContext })

export const luciaHandle: Handle = async ({ event, resolve }) => {
    // we can pass `event` because we used the SvelteKit middleware
    event.locals.auth = auth.handleRequest(event)
    return await resolve(event)
}

export const themeHandle: Handle = async ({ resolve, event }) => {
    let theme: string | null = null

    const newTheme = event.url.searchParams.get('theme')
    const cookieTheme = event.cookies.get('colorTheme')

    if (cookieTheme) {
        theme = cookieTheme
    } else if (newTheme) {
        theme = newTheme
    }

    if (theme) {
        return await resolve(event, {
            transformPageChunk: ({ html }) => html.replace('data-theme=""', `data-theme="${theme}"`)
        })
    }

    return resolve(event)
}

export const handle: Handle = sequence(trpc, luciaHandle, themeHandle)

lib/trpc/client.ts

//  lib/trpc/client.ts
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
}

lib/trpc/trpc.ts

// lib/trpc/trpc.ts
import type { QueryClient } from '@tanstack/svelte-query';

const client = createTRPCProxyClient<Router>({
  links: [
    httpBatchLink({
      // Replace this URL with that of your tRPC server
      // this currently points to a blank sveltekit GET endpoint
      url: 'http://localhost:5000/api/greeting',
    }),
  ],
});

export function trpc(queryClient?: QueryClient) {
  return svelteQueryWrapper<Router>({
    client,
    queryClient
  });
};

route/+page.svelte

<!-- routes/+page.ts -->
<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>

Thanks again

vishalbalaji commented 6 months ago

Hi, it seems to me like you are already using trpc-sveltekit, so I don't see the point of having the lib/trpc/trpc.ts file. That example is for client-only Svelte.

I think that there's been some slight misunderstanding here. If you're using trpc-sveltekit, you need to set it up first using this guide first: https://icflorescu.github.io/trpc-sveltekit/getting-started

Then modify the trpc function in lib/trpc/client.ts as directed in the README. The snippet for the client file in the README does not show the full contents of that file, just what needs to be modified. You shouldn't really have to worry about setting up httpBatchLinks this way.

Schmell commented 6 months ago

Ah. I see.
It is working, however i did have to pass the $page store to the trpc function

const client = trpc($page)