Skip to content

Commit

Permalink
feat: require transformers on frontend & backend when present (#3289)
Browse files Browse the repository at this point in the history
  • Loading branch information
QuiiBz committed Dec 23, 2022
1 parent 29e4d0e commit db69b00
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 30 deletions.
2 changes: 1 addition & 1 deletion packages/client/src/createTRPCClient.ts
Expand Up @@ -18,7 +18,7 @@ export function createTRPCClient<TRouter extends AnyRouter>(
return [httpBatchLink(opts)];
};
const client = new Client<TRouter>({
transformer: opts.transformer,
...opts,
links: getLinks(),
});
return client;
Expand Down
62 changes: 48 additions & 14 deletions packages/client/src/internals/TRPCClient.ts
@@ -1,7 +1,10 @@
import {
AnyRouter,
ClientDataTransformerOptions,
CombinedDataTransformer,
DataTransformer,
DataTransformerOptions,
DefaultDataTransformer,
inferProcedureInput,
inferProcedureOutput,
inferSubscriptionOutput,
Expand All @@ -22,13 +25,38 @@ import {
TRPCLink,
} from '../links/types';

interface CreateTRPCClientBaseOptions {
/**
* Data transformer
* @link https://trpc.io/docs/data-transformers
**/
transformer?: ClientDataTransformerOptions;
}
type CreateTRPCClientBaseOptions<TRouter extends AnyRouter> =
TRouter['_def']['_config']['transformer'] extends DefaultDataTransformer
? {
/**
* Data transformer
*
* You must use the same transformer on the backend and frontend
* @link https://trpc.io/docs/data-transformers
**/
transformer?: 'You must set a transformer on the backend router';
}
: TRouter['_def']['_config']['transformer'] extends DataTransformerOptions
? {
/**
* Data transformer
*
* You must use the same transformer on the backend and frontend
* @link https://trpc.io/docs/data-transformers
**/
transformer: TRouter['_def']['_config']['transformer'] extends CombinedDataTransformer
? DataTransformerOptions
: TRouter['_def']['_config']['transformer'];
}
: {
/**
* Data transformer
*
* You must use the same transformer on the backend and frontend
* @link https://trpc.io/docs/data-transformers
**/
transformer?: ClientDataTransformerOptions;
};

type TRPCType = 'subscription' | 'query' | 'mutation';
export interface TRPCRequestOptions {
Expand All @@ -49,7 +77,7 @@ export interface TRPCSubscriptionObserver<TValue, TError> {

/** @internal */
export type CreateTRPCClientOptions<TRouter extends AnyRouter> =
| CreateTRPCClientBaseOptions & {
| CreateTRPCClientBaseOptions<TRouter> & {
links: TRPCLink<TRouter>[];
};

Expand All @@ -62,16 +90,22 @@ export class TRPCClient<TRouter extends AnyRouter> {
this.requestId = 0;

function getTransformer(): DataTransformer {
if (!opts.transformer)
if (!opts.transformer || typeof opts.transformer === 'string')
return {
serialize: (data) => data,
deserialize: (data) => data,
};
if ('input' in opts.transformer)
return {
serialize: opts.transformer.input.serialize,
deserialize: opts.transformer.output.deserialize,
};
// Type guard for `opts.transformer` because it can be `any`
function isTransformer(obj: any): obj is ClientDataTransformerOptions {
return true;
}
if (isTransformer(opts.transformer)) {
if ('input' in opts.transformer)
return {
serialize: opts.transformer.input.serialize,
deserialize: opts.transformer.output.deserialize,
};
}
return opts.transformer;
}

Expand Down
5 changes: 1 addition & 4 deletions packages/server/src/core/initTRPC.ts
Expand Up @@ -7,7 +7,6 @@ import {
} from '../error/formatter';
import { createFlatProxy } from '../shared';
import {
CombinedDataTransformer,
DataTransformerOptions,
DefaultDataTransformer,
defaultTransformer,
Expand Down Expand Up @@ -86,9 +85,7 @@ function createTRPCInner<TParams extends PartialRootConfigTypes>() {
ErrorFormatter<$Context, DefaultErrorShape>
>;
type $Transformer = TOptions['transformer'] extends DataTransformerOptions
? TOptions['transformer'] extends DataTransformerOptions
? CombinedDataTransformer
: DefaultDataTransformer
? TOptions['transformer']
: DefaultDataTransformer;
type $ErrorShape = ErrorFormatterShape<$Formatter>;

Expand Down
4 changes: 2 additions & 2 deletions packages/tests/server/___testHelpers.ts
Expand Up @@ -70,14 +70,14 @@ export function routerToServerAndClientNew<TRouter extends AnyNewRouter>(
url: wssUrl,
...opts?.wsClient,
});
const trpcClientOptions: WithTRPCConfig<typeof router> = {
const trpcClientOptions = {
links: [httpBatchLink({ url: httpUrl })],
...(opts?.client
? typeof opts.client === 'function'
? opts.client({ httpUrl, wssUrl, wsClient })
: opts.client
: {}),
};
} as WithTRPCConfig<typeof router>;

const client = createTRPCClient<typeof router>(trpcClientOptions);
const proxy = createTRPCClientProxy<typeof router>(client);
Expand Down
3 changes: 1 addition & 2 deletions packages/tests/server/initTRPC.test.ts
@@ -1,5 +1,4 @@
import {
CombinedDataTransformer,
DataTransformerOptions,
DefaultDataTransformer,
initTRPC,
Expand Down Expand Up @@ -34,7 +33,7 @@ test('custom transformer', () => {
const router = t.router({});
expectTypeOf(
router._def._config.transformer,
).toMatchTypeOf<CombinedDataTransformer>();
).toMatchTypeOf<DataTransformerOptions>();
expectTypeOf(
router._def._config.transformer,
).not.toMatchTypeOf<DefaultDataTransformer>();
Expand Down
11 changes: 4 additions & 7 deletions packages/tests/server/interop/__legacyRouterToServerAndClient.ts
@@ -1,9 +1,6 @@
import { routerToServerAndClientNew } from '../___testHelpers';
import {
CreateTRPCClientOptions,
TRPCWebSocketClient,
WebSocketClientOptions,
} from '@trpc/client/src';
import { TRPCWebSocketClient, WebSocketClientOptions } from '@trpc/client/src';
import { WithTRPCConfig } from '@trpc/next';
import { CreateHTTPHandlerOptions } from '@trpc/server/src/adapters/standalone';
import { WSSHandlerOptions } from '@trpc/server/src/adapters/ws';
import { MigrateOldRouter } from '@trpc/server/src/deprecated/interop';
Expand All @@ -20,12 +17,12 @@ export function legacyRouterToServerAndClient<TOldRouter extends OldRouter>(
wssServer?: Partial<WSSHandlerOptions<MigrateOldRouter<TOldRouter>>>;
wsClient?: Partial<WebSocketClientOptions>;
client?:
| Partial<CreateTRPCClientOptions<MigrateOldRouter<TOldRouter>>>
| Partial<WithTRPCConfig<MigrateOldRouter<TOldRouter>>>
| ((opts: {
httpUrl: string;
wssUrl: string;
wsClient: TRPCWebSocketClient;
}) => Partial<CreateTRPCClientOptions<MigrateOldRouter<TOldRouter>>>);
}) => Partial<WithTRPCConfig<MigrateOldRouter<TOldRouter>>>);
},
) {
const router = _router.interop() as MigrateOldRouter<TOldRouter>;
Expand Down
50 changes: 50 additions & 0 deletions packages/tests/server/transformer.test.ts
Expand Up @@ -2,6 +2,7 @@
import { routerToServerAndClientNew, waitError } from './___testHelpers';
import {
TRPCClientError,
createTRPCProxyClient,
createWSClient,
httpBatchLink,
httpLink,
Expand Down Expand Up @@ -495,3 +496,52 @@ Object {

close();
});

describe('required tranformers', () => {
test('works without transformer', () => {
const t = initTRPC.create({});
const router = t.router({});

createTRPCProxyClient<typeof router>({
links: [httpBatchLink({ url: '' })],
});
});

test('works with transformer', () => {
const transformer = superjson;
const t = initTRPC.create({
transformer,
});
const router = t.router({});

createTRPCProxyClient<typeof router>({
links: [httpBatchLink({ url: '' })],
transformer,
});
});

test('errors with transformer set on backend but not on frontend', () => {
const transformer = superjson;
const t = initTRPC.create({
transformer,
});
const router = t.router({});

// @ts-expect-error missing transformer on frontend
createTRPCProxyClient<typeof router>({
links: [httpBatchLink({ url: '' })],
});
});

test('errors with transformer set on frontend but not on backend', () => {
const transformer = superjson;
const t = initTRPC.create({});
const router = t.router({});

createTRPCProxyClient<typeof router>({
links: [httpBatchLink({ url: '' })],
// @ts-expect-error missing transformer on backend
transformer,
});
});
});

3 comments on commit db69b00

@vercel
Copy link

@vercel vercel bot commented on db69b00 Dec 23, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

www – ./www

www-trpc.vercel.app
www-git-main-trpc.vercel.app
www.trpc.io
beta.trpc.io
alpha.trpc.io
trpc.io

@vercel
Copy link

@vercel vercel bot commented on db69b00 Dec 23, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

og-image – ./www/og-image

og-image-three-neon.vercel.app
og-image-git-main-trpc.vercel.app
og-image.trpc.io
og-image-trpc.vercel.app

@vercel
Copy link

@vercel vercel bot commented on db69b00 Dec 23, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

next-prisma-starter – ./examples/next-prisma-starter

next-prisma-starter-trpc.vercel.app
next-prisma-starter-git-main-trpc.vercel.app
nextjs.trpc.io

Please sign in to comment.