SuperJson
This guide explains how to replace the default oRPC RPC serializer with SuperJson.
INFO
While the default oRPC serializer is faster and more efficient, SuperJson is widely adopted and may be preferred for compatibility.
SuperJson Serializer
WARNING
The SuperJsonSerializer
supports only the data types that SuperJson handles, plus AsyncIteratorObject
at the root level for Event Iterator. It does not support all RPC supported types.
ts
import { createORPCErrorFromJson, ErrorEvent, isORPCErrorJson, mapEventIterator, toORPCError } from '@orpc/client'
import type { StandardRPCSerializer } from '@orpc/client/standard'
import { isAsyncIteratorObject } from '@orpc/shared'
import SuperJSON from 'superjson'
export class SuperJSONSerializer implements Pick<StandardRPCSerializer, keyof StandardRPCSerializer> {
serialize(data: unknown): object {
if (isAsyncIteratorObject(data)) {
return mapEventIterator(data, {
value: async (value: unknown) => SuperJSON.serialize(value),
error: async (e) => {
return new ErrorEvent({
data: SuperJSON.serialize(toORPCError(e).toJSON()),
cause: e,
})
},
})
}
return SuperJSON.serialize(data)
}
deserialize(data: any): unknown {
if (isAsyncIteratorObject(data)) {
return mapEventIterator(data, {
value: async value => SuperJSON.deserialize(value),
error: async (e) => {
if (!(e instanceof ErrorEvent))
return e
const deserialized = SuperJSON.deserialize(e.data as any)
if (isORPCErrorJson(deserialized)) {
return createORPCErrorFromJson(deserialized, { cause: e })
}
return new ErrorEvent({
data: deserialized,
cause: e,
})
},
})
}
return SuperJSON.deserialize(data)
}
}
SuperJson Handler
ts
import type { StandardRPCSerializer } from '@orpc/client/standard'
import type { Context, Router } from '@orpc/server'
import type { FetchHandlerOptions } from '@orpc/server/fetch'
import { FetchHandler } from '@orpc/server/fetch'
import { StrictGetMethodPlugin } from '@orpc/server/plugins'
import type { StandardHandlerOptions } from '@orpc/server/standard'
import { StandardHandler, StandardRPCCodec, StandardRPCMatcher } from '@orpc/server/standard'
export interface SuperJSONHandlerOptions<T extends Context> extends StandardHandlerOptions<T> {
/**
* Enable or disable the StrictGetMethodPlugin.
*
* @default true
*/
strictGetMethodPluginEnabled?: boolean
}
export class SuperJSONHandler<T extends Context> extends FetchHandler<T> {
constructor(router: Router<any, T>, options: NoInfer<FetchHandlerOptions<T> & SuperJSONHandlerOptions<T>> = {}) {
options.plugins ??= []
const strictGetMethodPluginEnabled = options.strictGetMethodPluginEnabled ?? true
if (strictGetMethodPluginEnabled) {
options.plugins.push(new StrictGetMethodPlugin())
}
const serializer = new SuperJSONSerializer()
const matcher = new StandardRPCMatcher()
const codec = new StandardRPCCodec(serializer as any)
super(new StandardHandler(router, matcher, codec, options), options)
}
}
SuperJson Link
ts
import type { ClientContext } from '@orpc/client'
import { StandardLink, StandardRPCLinkCodec } from '@orpc/client/standard'
import type { StandardLinkOptions, StandardRPCLinkCodecOptions, StandardRPCSerializer } from '@orpc/client/standard'
import type { LinkFetchClientOptions } from '@orpc/client/fetch'
import { LinkFetchClient } from '@orpc/client/fetch'
export interface SuperJSONLinkOptions<T extends ClientContext>
extends StandardLinkOptions<T>, StandardRPCLinkCodecOptions<T>, LinkFetchClientOptions<T> { }
export class SuperJSONLink<T extends ClientContext> extends StandardLink<T> {
constructor(options: SuperJSONLinkOptions<T>) {
const linkClient = new LinkFetchClient(options)
const serializer = new SuperJSONSerializer()
const linkCodec = new StandardRPCLinkCodec(serializer as any, options)
super(linkCodec, linkClient, options)
}
}