Data Transformers
On this page
You are able to serialize the response data & input args. The transformers need to be added both to the server and the client.
Using superjson
SuperJSON allows us to transparently use, e.g., standard Date/Map/Sets over the wire between the server and client. That is, you can return any of these types from your API-resolver and use them in the client without having to recreate the objects from JSON.
How to
1. Install
yarn add superjson2. Add to your initTRPC
import { const initTRPC: TRPCBuilder<object, object>initTRPC } from '@trpc/server';
import class superjsonsuperjson from 'superjson';
export const const t: TRPCRootObject<object, object, {
transformer: typeof superjson;
}, {
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: true;
}>
t = const initTRPC: TRPCBuilder<object, object>initTRPC.TRPCBuilder<object, object>.create<{
transformer: typeof superjson;
}>(opts?: {
transformer: typeof superjson;
} | undefined): TRPCRootObject<object, object, {
transformer: typeof superjson;
}, {
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: true;
}>
create({
transformer: typeof superjsontransformer: class superjsonsuperjson,
});3. Add to httpLink(), wsLink(), etc
TypeScript will guide you to where you need to add
transformeras soon as you've added it on theinitTRPC-object
createTRPCClient():
import { function createTRPCClient<TRouter extends AnyRouter>(opts: CreateTRPCClientOptions<TRouter>): TRPCClient<TRouter>createTRPCClient, function httpLink<TRouter extends AnyRouter = AnyRouter>(opts: HTTPLinkOptions<TRouter["_def"]["_config"]["$types"]>): TRPCLink<TRouter>httpLink } from '@trpc/client';
import type { type AppRouter = Router<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: true;
}, DecorateCreateRouterOptions<{}>> & DecorateCreateRouterOptions<{}>
AppRouter } from './server';
import class superjsonsuperjson from 'superjson';
export const const client: TRPCClient<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: true;
}, DecorateCreateRouterOptions<{}>>>
client = createTRPCClient<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: true;
}, DecorateCreateRouterOptions<{}>>>(opts: CreateTRPCClientOptions<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: true;
}, DecorateCreateRouterOptions<{}>>>): TRPCClient<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: true;
}, DecorateCreateRouterOptions<{}>>>
createTRPCClient<type AppRouter = Router<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: true;
}, DecorateCreateRouterOptions<{}>> & DecorateCreateRouterOptions<{}>
AppRouter>({
links: TRPCLink<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: true;
}, DecorateCreateRouterOptions<{}>>>[]
links: [
httpLink<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: true;
}, DecorateCreateRouterOptions<{}>>>(opts: HTTPLinkOptions<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: true;
}>): TRPCLink<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: true;
}, DecorateCreateRouterOptions<{}>>>
httpLink({
url: string | URLurl: 'http://localhost:3000',
transformer: DataTransformerOptionstransformer: class superjsonsuperjson,
}),
],
});Using devalue
Devalue works like superjson, focusing on performance and compact payloads, but at the cost of a less human-readable body.
How to
1. Install
yarn add devalue2. Add to utils/trpc.ts
Here we use parse and stringify as they mitigate XSS.
import { function parse(str: string): any (+1 overload)parse, function stringify(value: any): string (+1 overload)stringify } from 'devalue';
// [...]
export const const transformer: {
deserialize: (object: any) => any;
serialize: (object: any) => string;
}
transformer = {
deserialize: (object: any) => anydeserialize: (object: anyobject: any) => function parse(serialized: string, revivers?: Record<string, (value: any) => any>, options?: ParseOptions_1): any (+1 overload)parse(object: anyobject),
serialize: (object: any) => stringserialize: (object: anyobject: any) => function stringify(value: any, reducers?: Record<string, (value: any) => any>, options?: StringifyOptions_1): string (+1 overload)stringify(object: anyobject),
};3. Add to your initTRPC
import { const initTRPC: TRPCBuilder<object, object>initTRPC } from '@trpc/server';
import { const transformer: {
deserialize: (object: any) => any;
serialize: (object: any) => any;
}
transformer } from '../../utils/trpc';
export const const t: TRPCRootObject<object, object, {
transformer: {
deserialize: (object: any) => any;
serialize: (object: any) => any;
};
}, {
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: true;
}>
t = const initTRPC: TRPCBuilder<object, object>initTRPC.TRPCBuilder<object, object>.create<{
transformer: {
deserialize: (object: any) => any;
serialize: (object: any) => any;
};
}>(opts?: {
transformer: {
deserialize: (object: any) => any;
serialize: (object: any) => any;
};
} | undefined): TRPCRootObject<object, object, {
transformer: {
deserialize: (object: any) => any;
serialize: (object: any) => any;
};
}, {
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: true;
}>
create({
transformer: {
deserialize: (object: any) => any;
serialize: (object: any) => any;
}
transformer,
});4. Add to httpLink(), wsLink(), etc
TypeScript will guide you to where you need to add
transformeras soon as you've added it on theinitTRPC-object
createTRPCClient():
import { function createTRPCClient<TRouter extends AnyRouter>(opts: CreateTRPCClientOptions<TRouter>): TRPCClient<TRouter>createTRPCClient, function httpLink<TRouter extends AnyRouter = AnyRouter>(opts: HTTPLinkOptions<TRouter["_def"]["_config"]["$types"]>): TRPCLink<TRouter>httpLink } from '@trpc/client';
import type { type AppRouter = Router<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: true;
}, DecorateCreateRouterOptions<{}>> & DecorateCreateRouterOptions<{}>
AppRouter } from './server/routers/_app';
import { const transformer: {
deserialize: (object: any) => any;
serialize: (object: any) => any;
}
transformer } from './utils/trpc';
export const const client: TRPCClient<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: true;
}, DecorateCreateRouterOptions<{}>>>
client = createTRPCClient<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: true;
}, DecorateCreateRouterOptions<{}>>>(opts: CreateTRPCClientOptions<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: true;
}, DecorateCreateRouterOptions<{}>>>): TRPCClient<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: true;
}, DecorateCreateRouterOptions<{}>>>
createTRPCClient<type AppRouter = Router<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: true;
}, DecorateCreateRouterOptions<{}>> & DecorateCreateRouterOptions<{}>
AppRouter>({
links: TRPCLink<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: true;
}, DecorateCreateRouterOptions<{}>>>[]
links: [
httpLink<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: true;
}, DecorateCreateRouterOptions<{}>>>(opts: HTTPLinkOptions<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: true;
}>): TRPCLink<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: true;
}, DecorateCreateRouterOptions<{}>>>
httpLink({
url: string | URLurl: 'http://localhost:3000',
transformer: DataTransformerOptionstransformer,
}),
],
});Different transformers for upload and download
If a transformer should only be used for one direction or different transformers should be used for upload and download (e.g., for performance reasons), you can provide individual transformers for upload and download. Make sure you use the same combined transformer everywhere.
DataTransformer interface
export interface DataTransformer {
DataTransformer.serialize(object: any): anyserialize(object: anyobject: any): any;
DataTransformer.deserialize(object: any): anydeserialize(object: anyobject: any): any;
}
interface InputDataTransformer extends DataTransformer {
/**
* This function runs **on the client** before sending the data to the server.
*/
InputDataTransformer.serialize(object: any): anyserialize(object: anyobject: any): any;
/**
* This function runs **on the server** to transform the data before it is passed to the resolver
*/
InputDataTransformer.deserialize(object: any): anydeserialize(object: anyobject: any): any;
}
interface OutputDataTransformer extends DataTransformer {
/**
* This function runs **on the server** before sending the data to the client.
*/
OutputDataTransformer.serialize(object: any): anyserialize(object: anyobject: any): any;
/**
* This function runs **only on the client** to transform the data sent from the server.
*/
OutputDataTransformer.deserialize(object: any): anydeserialize(object: anyobject: any): any;
}
export interface CombinedDataTransformer {
/**
* Specify how the data sent from the client to the server should be transformed.
*/
CombinedDataTransformer.input: InputDataTransformerinput: InputDataTransformer;
/**
* Specify how the data sent from the server to the client should be transformed.
*/
CombinedDataTransformer.output: OutputDataTransformeroutput: OutputDataTransformer;
}