OpenAPI Serializer
OpenAPI Serializers handle one-way serialization to JSON-friendly formats. They let you partially support complex data types beyond plain JSON, such as Date, BigInt, Set, and even custom classes.
Supported Data Types
OpenAPISerializer supports the following types by default:
| Type | Handler key | Serialized | Notes |
|---|---|---|---|
| string | |||
| number | |||
| NaN | nan |
null |
|
| boolean | |||
| null | |||
| undefined | undefined |
null |
Ignore undefined properties |
| Date | date |
ISO String, null |
|
| BigInt | bigint |
string | |
| RegExp | regexp |
string | |
| URL | url |
string | |
| Record (object) | toJSON methods are ignored |
||
| Array | |||
| Set | set |
array | |
| Map | map |
array | |
| Blob | Unsupported in AsyncIteratorObject |
||
| File | Unsupported in AsyncIteratorObject |
||
| AsyncIteratorObject | Only at the root level | ||
ReadableStream<Uint8Array> |
Only at the root level |
Limitations
OpenAPI Serializers are designed for one-way serialization to JSON-friendly formats. For example, a Date is serialized to an ISO string and remains a string after deserialization unless you add custom logic or plugins.
In complex cases like mixed files with other data or nested structures in query strings, OpenAPI Serializer might use bracket notation to represent nested data, which has its own limitations. See Bracket Notation Limitations for details.
Custom Serializers
Add custom handlers with unique keys to support additional types, or reuse a built-in key to override the default behavior.
import { class OpenAPISerializerHandles one-way serialization of oRPC payloads into JSON-friendly formats,
partially supporting complex data types beyond plain JSON such as `Date`, `BigInt`, and `Set`.OpenAPISerializer } from '@orpc/openapi'
const const serializer: OpenAPISerializerserializer = new new OpenAPISerializer({ bracketNotation, serialize, ...options }?: OpenAPISerializerOptions): OpenAPISerializerHandles one-way serialization of oRPC payloads into JSON-friendly formats,
partially supporting complex data types beyond plain JSON such as `Date`, `BigInt`, and `Set`.OpenAPISerializer({
OpenAPIJsonSerializerOptions.handlers?: Record<string, OpenAPIJsonSerializerHandler | undefined> | undefinedExtend or override the built-in type handlers used during serialization.
Each key is a unique type identifier (e.g. `"date"`, `"bigint"`) and maps to a handler
that defines how to detect and serialize values of that type.
**Extending:** Add new keys to support custom types:
```ts
handlers: {
buffer: {
condition: (v) => v instanceof Buffer,
serialize: (v: Buffer) => v.toString('base64'),
isTerminal: true,
}
}
```
**Overriding:** Use an existing key to replace a built-in handler:
```ts
handlers: {
date: {
condition: (v) => v instanceof Date,
serialize: (v: Date) => v.getTime(),
isTerminal: true,
}
}
```
**Disabling:** Set a key to `undefined` to remove a built-in handler:
```ts
handlers: { regexp: undefined }
```
Built-in type keys: `undefined`, `bigint`, `date`, `nan`, `url`, `regexp`, `set`, `map`.handlers: {
person: {
condition: (v: unknown) => v is Person;
serialize: (v: Person) => {
name: string;
age: number;
};
}
person: { // <- add support for Person
OpenAPIJsonSerializerHandler.condition(value: unknown): booleancondition: v: unknownv => v: unknownv instanceof class PersonPerson,
OpenAPIJsonSerializerHandler.serialize(value: any): unknownserialize: (v: Personv: class PersonPerson) => ({ name: stringname: v: Personv.Person.name: stringname, age: numberage: v: Personv.Person.age: numberage }),
},
date: {
condition: (v: unknown) => v is Date;
serialize: (v: Date) => number;
}
date: { // <- replace the default Date handler
OpenAPIJsonSerializerHandler.condition(value: unknown): booleancondition: v: unknownv => v: unknownv instanceof var Date: DateConstructorEnables basic storage and retrieval of dates and times.Date,
OpenAPIJsonSerializerHandler.serialize(value: any): unknownserialize: (v: Datev: Date) => v: Datev.Date.getTime(): numberReturns the stored time value in milliseconds since midnight, January 1, 1970 UTC.getTime(),
},
},
})
Serialization Format
In most cases, serialized data is JSON-serializable.
{
"name": "John",
"age": 30,
"createdAt": "2024-01-01T00:00:00.000Z"
}
With Files
If the data includes nested Blob or File, the serializer returns a FormData object using Bracket Notation. Non-file values are converted to strings, and null or undefined fields are omitted.
const form = new FormData()
form.append('name', 'Earth')
form.append('thumbnail', new Blob([''], { type: 'image/png' }))
form.append('images[0]', new Blob([''], { type: 'image/png' }))
form.append('createdAt', '2022-01-01T00:00:00.000Z')
Direct File
If the entire data is a single Blob or File, it can be sent as-is without wrapping in FormData.
HTTP/1.1 200 OK
Content-Type: image/png
Content-Disposition: attachment; filename="earth.png"
Content-Length: 12345
Standard-Server: file
<binary data>
AsyncIteratorObject
When the output is an AsyncIteratorObject, it is sent as a Server-Sent Events stream. Each event contains one serialized chunk of data.
HTTP/1.1 200 OK
Content-Type: text/event-stream
event: message
data: {"name":"John","createdAt":"2024-01-01T00:00:00.000Z"}
event: message
data: {"name":"Jane","createdAt":"2024-01-02T00:00:00.000Z"}
ReadableStream<Uint8Array>
A ReadableStream<Uint8Array> is passed through as-is and streamed as binary data.
HTTP/1.1 200 OK
Content-Type: application/octet-stream
Standard-Server: octet-stream
<binary chunk 1>
<binary chunk 2>
Learn More
The serializer is a small, self-contained module, making it easy to understand. To explore its behavior in detail, see the source code.