Skip to content

RPC JSON Serializer

This serializer handles JSON payloads for the RPC Protocol and supports native data types.

Extending Native Data Types

Extend native types by creating your own StandardRPCCustomJsonSerializer and adding it to the customJsonSerializers option.

  1. Define Your Custom Serializer

    ts
    import type { StandardRPCCustomJsonSerializer } from '@orpc/client/standard'
    import * as 
    z
    from 'zod'
    export class
    User
    {
    constructor( public readonly
    id
    : string,
    public readonly
    name
    : string,
    public readonly
    email
    : string,
    public readonly
    age
    : number,
    ) {}
    toJSON
    () {
    return {
    id
    : this.
    id
    ,
    name
    : this.
    name
    ,
    email
    : this.
    email
    ,
    age
    : this.
    age
    ,
    } } } const
    UserSchema
    =
    z
    .
    object
    ({
    id
    :
    z
    .
    string
    (),
    name
    :
    z
    .
    string
    (),
    email
    :
    z
    .
    string
    (),
    age
    :
    z
    .
    number
    (),
    }) export const
    userSerializer
    : StandardRPCCustomJsonSerializer = {
    type
    : 21,
    condition
    :
    data
    =>
    data
    instanceof
    User
    ,
    serialize
    :
    data
    =>
    data
    .toJSON(),
    deserialize
    : (
    data
    ) => {
    const {
    id
    ,
    name
    ,
    email
    ,
    age
    } =
    UserSchema
    .
    parse
    (
    data
    )
    return new
    User
    (
    id
    ,
    name
    ,
    email
    ,
    age
    )
    }, }

    WARNING

    Ensure the type is unique and greater than 20 to avoid conflicts with built-in types in the future.

    WARNING

    deserialize receives untrusted input. A malicious client controls meta and can point any type at any JSON value, so never assume deserialize receives what serialize produced. Validate the value and throw when it does not match. Built-in types do the same, and RPC Handler turns these errors into a BAD_REQUEST response.

  2. Use Your Custom Serializer

    ts
    const 
    handler
    = new
    RPCHandler
    (
    router
    , {
    customJsonSerializers
    : [
    userSerializer
    ],
    }) const
    link
    = new
    RPCLink
    ({
    url
    : 'https://example.com/rpc',
    customJsonSerializers
    : [
    userSerializer
    ],
    })

Overriding Built-in Types

You can override built-in types by matching their type with the built-in types.

For example, oRPC represents undefined only in array items and ignores it in objects. To override this behavior:

ts
import { StandardRPCCustomJsonSerializer } from '@orpc/client/standard'

export const 
undefinedSerializer
: StandardRPCCustomJsonSerializer = {
type
: 3, // Match the built-in undefined type.
condition
:
data
=>
data
===
undefined
,
serialize
:
data
=> null, // JSON cannot represent undefined, so use null.
deserialize
:
data
=>
undefined
,
}

A custom serializer that matches a built-in type fully replaces it: the built-in deserialize no longer runs on that value, so your deserialize must validate the input itself.

Released under the MIT License.