wleistra
wleistraā€¢9mo ago

shared useTRPCClient hook conversion to v11

In our mono repo, we have 30 or so MFEs which all use the same TRPC client setup. So, we abstracted the inner workings of getting the TRPC client set up away in a different package as a hook. Now, I'm converting the code to v11, and I'm running into deprecation warnings and typescript errors. Our hook looks like this:
import { useState } from 'react';

import { createWSClient, getFetch, httpLink, splitLink, wsLink } from '@trpc/client';
import type { TRPCClient, TRPCLink } from '@trpc/client'; // [1] TRPCClient no longer exported

import type { AnyRouter } from '@trpc/server'; // [2] AnyRouter deprecated

type TRPC<TRouter extends AnyRouter> = {
createClient: (opts: { links: TRPCLink<TRouter>[] }) => TRPCClient<TRouter>;
};

export const useTRPCClient = <TRouter extends AnyRouter>(trpc: TRPC<TRouter>, url: string, wsUrl?: string) => {
const [trpcClient] = useState(() => {
const http = httpLink({
url,
fetch: async (input, init) => {
const fetch = getFetch();
return fetch(input, { ...init, credentials: 'include' });
},
headers: () => {
const originURL = window.location.href;
return {
'origin-url': originURL,
};
},
});

const wsClient = wsUrl ? createWSClient({ url: wsUrl }) : null;
const ws = wsClient ? wsLink({ client: wsClient }) : null;

return trpc.createClient({
links: ws
? [
splitLink({
condition: (op) => op.type === 'subscription',
true: ws,
false: http,
}),
]
: [http],
});
});

return trpcClient;
};
import { useState } from 'react';

import { createWSClient, getFetch, httpLink, splitLink, wsLink } from '@trpc/client';
import type { TRPCClient, TRPCLink } from '@trpc/client'; // [1] TRPCClient no longer exported

import type { AnyRouter } from '@trpc/server'; // [2] AnyRouter deprecated

type TRPC<TRouter extends AnyRouter> = {
createClient: (opts: { links: TRPCLink<TRouter>[] }) => TRPCClient<TRouter>;
};

export const useTRPCClient = <TRouter extends AnyRouter>(trpc: TRPC<TRouter>, url: string, wsUrl?: string) => {
const [trpcClient] = useState(() => {
const http = httpLink({
url,
fetch: async (input, init) => {
const fetch = getFetch();
return fetch(input, { ...init, credentials: 'include' });
},
headers: () => {
const originURL = window.location.href;
return {
'origin-url': originURL,
};
},
});

const wsClient = wsUrl ? createWSClient({ url: wsUrl }) : null;
const ws = wsClient ? wsLink({ client: wsClient }) : null;

return trpc.createClient({
links: ws
? [
splitLink({
condition: (op) => op.type === 'subscription',
true: ws,
false: http,
}),
]
: [http],
});
});

return trpcClient;
};
Issue 1: TRPCClient type is no longer exported from @trpc/client. I assume we can replace it with createClient: (opts: { links: TRPCLink<TRouter>[] }) => ReturnType<typeof createTRPCClient>; correct? Issue 2: the AnyRouter type deprecation, how can that be resolved?
6 Replies
BeBoRE
BeBoREā€¢9mo ago
Why not use @trpc/react?
wleistra
wleistraOPā€¢9mo ago
We do! The prop trpc is coming from createReactRouter<AppRouter> like this
import type { AppRouter } from 'mfe-middleware';

import type { inferReactQueryProcedureOptions } from '@trpc/react-query';
import { createTRPCReact } from '@trpc/react-query';
import type { inferRouterInputs, inferRouterOutputs } from '@trpc/server';

/* eslint-disable import/no-unused-modules */
export type ReactQueryOptions = inferReactQueryProcedureOptions<AppRouter>;
export type RouterInputs = inferRouterInputs<AppRouter>;
export type RouterOutputs = inferRouterOutputs<AppRouter>;
/* eslint-enable import/no-unused-modules */

export const trpc = createTRPCReact<AppRouter>();
import type { AppRouter } from 'mfe-middleware';

import type { inferReactQueryProcedureOptions } from '@trpc/react-query';
import { createTRPCReact } from '@trpc/react-query';
import type { inferRouterInputs, inferRouterOutputs } from '@trpc/server';

/* eslint-disable import/no-unused-modules */
export type ReactQueryOptions = inferReactQueryProcedureOptions<AppRouter>;
export type RouterInputs = inferRouterInputs<AppRouter>;
export type RouterOutputs = inferRouterOutputs<AppRouter>;
/* eslint-enable import/no-unused-modules */

export const trpc = createTRPCReact<AppRouter>();
And this is how we put it all together
import type { ReactNode } from 'react';
import React from 'react';

import { createQueryClient, useTRPCClient } from '@company/utils';
import { QueryClientProvider } from '@tanstack/react-query';

import { trpc } from './trpc';

const queryClient = createQueryClient();

interface MFEQueryProviderProps {
children: ReactNode;
}

export const MFEQueryProvider = ({ children }: MFEQueryProviderProps) => {
const trpcClient = useTRPCClient(trpc, process.env.API_URL ?? '');

return (
<trpc.Provider client={trpcClient} queryClient={queryClient}>
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
</trpc.Provider>
);
};
import type { ReactNode } from 'react';
import React from 'react';

import { createQueryClient, useTRPCClient } from '@company/utils';
import { QueryClientProvider } from '@tanstack/react-query';

import { trpc } from './trpc';

const queryClient = createQueryClient();

interface MFEQueryProviderProps {
children: ReactNode;
}

export const MFEQueryProvider = ({ children }: MFEQueryProviderProps) => {
const trpcClient = useTRPCClient(trpc, process.env.API_URL ?? '');

return (
<trpc.Provider client={trpcClient} queryClient={queryClient}>
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
</trpc.Provider>
);
};
If there is a more efficient/better practice to achieve the sharing of the same client setup across all our Frontends without duplicating the code I'm all ears too... So far we successfully used this way with trpc 10
Alex / KATT šŸ±
there should be comments on all deprecations feel free to make a pr on things that we might've removed we have started being explicit about what we import + added smurfing(https://blog.codinghorror.com/new-programming-jargon/#21) on most variables/types if you need consulting for the migration email you can email sales@trpc.io
wleistra
wleistraOPā€¢9mo ago
This is the information vscode gives about the deprecation:
'AnyRouter' is deprecated.ts(6385)
index.d.ts(12, 4): The declaration was marked as deprecated here.
āš  Error(TS6385) |
AnyRouter is deprecated.
(alias) type AnyRouter = Router<any, any>
import AnyRouter
'AnyRouter' is deprecated.ts(6385)
index.d.ts(12, 4): The declaration was marked as deprecated here.
āš  Error(TS6385) |
AnyRouter is deprecated.
(alias) type AnyRouter = Router<any, any>
import AnyRouter
Maybe a comment was missed along the way? When trying to use Router<any, any> directly it says Router is not exported from @trpc/server I worked around the issue by not returning the createClient but by returning the links instead. and have the createClient in the MFE's own useState.
wleistra
wleistraOPā€¢9mo ago
Thanks @Alex / KATT šŸ± for the extra details on the comments.