fatline

tRPC Integration

Schema-aware transformer with fingerprinting for tRPC

fatline provides a transformer for tRPC that preserves your types and enables schema-aware serialization.

Setup

Server

import { transformer } from 'fatline/trpc'

const t = initTRPC.create({ transformer })

// Dates, Sets, Maps work automatically
export const appRouter = t.router({
  getUser: t.procedure.query(() => ({
    id: '1',
    createdAt: new Date(),
    roles: new Set(['admin']),
  })),
})

Client

import { transformer } from 'fatline/trpc'

const trpc = createTRPCClient<AppRouter>({
  transformer,
  links: [
    httpBatchLink({ url: '/api/trpc' }),
  ],
})

Schema Registry

For type-safe procedures with shared schemas, use a registry:

// packages/schemas/index.ts
import { createRegistry } from 'fatline'
import { z } from 'zod'

export const registry = createRegistry()

export const UserSchema = z.object({
  id: z.string(),
  createdAt: z.date(),
  tags: z.set(z.string()),
})

export const UserCodec = registry.register('user.get', UserSchema)

Each registered schema gets a content-based hash:

UserCodec.hash  // "a3f2c1b8"

Same schema = same hash, regardless of when or where it was compiled. This enables schema verification between client and server.

// Verify schemas match
registry.verify('user.get', 'a3f2c1b8')  // true

// Export manifest for distribution
const manifest = registry.export()

Custom Types

Register custom types that work across all your tRPC procedures:

import { registerType } from 'fatline'
import Decimal from 'decimal.js'

// Register before creating tRPC context
registerType({
  name: 'Decimal',
  test: (v): v is Decimal => v instanceof Decimal,
  encode: (v) => v.toString(),
  decode: (v) => new Decimal(v),
})

Now any procedure can return Decimal values and they'll serialize correctly.

oRPC

Similar API for oRPC:

import { FatlineSerializer } from 'fatline/orpc'

const handler = new RPCHandler(router, {
  serializer: new FatlineSerializer(),
})

Migrating from SuperJSON

- import superjson from 'superjson'
+ import { transformer } from 'fatline/trpc'

const t = initTRPC.create({
-   transformer: superjson,
+   transformer,
})

Your existing procedures work unchanged.

Next Steps

On this page