Skip to content

Commit

Permalink
feat: custom trpc transformer
Browse files Browse the repository at this point in the history
  • Loading branch information
tsumo authored and awinogradov committed Oct 12, 2023
1 parent eaffa2a commit 0ccdd75
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 8 deletions.
1 change: 0 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@
"remark-emoji": "4.0.0",
"remark-gfm": "3.0.1",
"styled-components": "5.3.11",
"superjson": "1.12.3",
"throttle-debounce": "5.0.0",
"tinykeys": "1.4.0",
"zod": "3.22.3"
Expand Down
5 changes: 3 additions & 2 deletions src/utils/declareSsrProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ import { GetServerSidePropsContext } from 'next';
import { Session } from 'next-auth';
import { getSession } from 'next-auth/react';
import { createServerSideHelpers, DecoratedProcedureSSGRecord } from '@trpc/react-query/server';
import superjson from 'superjson';

import { routes } from '../hooks/router';
import { trpcRouter } from '../../trpc/router';
import type { TrpcRouter } from '../../trpc/router';

import { transformer } from './transformer';

export interface SSRProps<P = { [key: string]: string }> {
user: Session['user'];
req: GetServerSidePropsContext['req'];
Expand Down Expand Up @@ -45,7 +46,7 @@ export function declareSsrProps<T = ExternalPageProps>(
session,
headers: req.headers,
},
transformer: superjson,
transformer,
});

const ssrTime = Date.now();
Expand Down
30 changes: 30 additions & 0 deletions src/utils/transformer.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { expect } from '@jest/globals';

import { deserialize, serialize } from './transformer';

const dateString = '_serializedDate_1970-01-01T00:00:00.000Z';
const undefinedString = '_undefined_';

describe('Transformer', () => {
it('should serialize and deserialize', async () => {
const values = [
{ from: 1, to: 1 },
{ from: 'a', to: 'a' },
{ from: new Date(0), to: dateString },
{ from: undefined, to: undefinedString },
{ from: [], to: [] },
{ from: ['a', 1, new Date(0), [new Date(0)]], to: ['a', 1, dateString, [dateString]] },
{ from: ['a', [new Date(0)]], to: ['a', [dateString]] },
{ from: [undefined, [[undefined]]], to: [undefinedString, [[undefinedString]]] },
{ from: {}, to: {} },
{ from: { a: { b: new Date(0), c: {} } }, to: { a: { b: dateString, c: {} } } },
{ from: { a: { b: null } }, to: { a: { b: null } } },
];
for (const { from, to } of values) {
const serialized = serialize(from);
expect(serialized).toStrictEqual(to);
const deserialized = deserialize(to);
expect(deserialized).toStrictEqual(from);
}
});
});
36 changes: 36 additions & 0 deletions src/utils/transformer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { CombinedDataTransformer } from '@trpc/server';

const datePrefix = '_serializedDate_';
const undefinedPlaceholder = '_undefined_';

export const serialize = (data: any): any => {
if (typeof data === 'function') throw new Error('Cannot serialize function');
if (typeof data === 'symbol') throw new Error('Cannot serialize symbol');
if (typeof data === 'bigint') throw new Error('Cannot serialize bigint');
if (typeof data === 'undefined') return undefinedPlaceholder;
if (typeof data === 'object') {
if (data === null) return data;
if (data instanceof Date) return `${datePrefix}${data.toISOString()}`;
if (Array.isArray(data)) return data.map(serialize);
return Object.fromEntries(Object.entries(data).map(([k, v]) => [k, serialize(v)]));
}
return data;
};

export const deserialize = (data: any): any => {
if (typeof data === 'string') {
if (data.startsWith(datePrefix)) return new Date(data.slice(datePrefix.length));
if (data === undefinedPlaceholder) return undefined;
}
if (typeof data === 'object') {
if (data === null) return data;
if (Array.isArray(data)) return data.map(deserialize);
return Object.fromEntries(Object.entries(data).map(([k, v]) => [k, deserialize(v)]));
}
return data;
};

export const transformer: CombinedDataTransformer = {
input: { serialize, deserialize },
output: { serialize, deserialize },
};
5 changes: 3 additions & 2 deletions src/utils/trpcClient.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { httpBatchLink } from '@trpc/client';
import { createTRPCNext } from '@trpc/next';
import superjson from 'superjson';

import type { TrpcRouter } from '../../trpc/router';

import { transformer } from './transformer';

function getBaseUrl() {
if (typeof window !== 'undefined') {
// browser should use relative path
Expand All @@ -15,7 +16,7 @@ function getBaseUrl() {
export const trpc = createTRPCNext<TrpcRouter>({
config: ({ ctx }) => {
return {
transformer: superjson,
transformer,

queryClientConfig: {
defaultOptions: {
Expand Down
5 changes: 3 additions & 2 deletions trpc/trpcBackend.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { TRPCError, initTRPC } from '@trpc/server';
import superjson from 'superjson';

import { transformer } from '../src/utils/transformer';

import type { TrpcContext } from './context';

const t = initTRPC.context<TrpcContext>().create({
transformer: superjson,
transformer,
});

const sessionCheck = t.middleware(({ next, ctx }) => {
Expand Down

0 comments on commit 0ccdd75

Please sign in to comment.