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 Event Iterator | ||
| File | Unsupported in Event Iterator | ||
| Event Iterator (AsyncIteratorObject) | Only at the root level | ||
| ReadableStream<Uint8Array> | Only at the root level |
WARNING
To better support Blob, File, and ReadableStream<Uint8Array> at the root level in cross-origin scenarios, extend your CORS allowlist to allow clients to send and receive the Content-Disposition and Standard-Server headers. Learn more in the Standard Server documentation. If you use the CORS Plugin, include them in allowHeaders and exposeHeaders:
const cors = new CORSHandlerPlugin({
allowHeaders: ['Content-Disposition', 'Standard-Server'],
exposeHeaders: ['Content-Disposition', 'Standard-Server'],
})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.
TIP
If you use OpenAPI Link to connect your client and server, follow Expanding Type Support for OpenAPI Link to restore native types on the client.
Custom Serializers
Add custom handlers with unique keys to support additional types, or reuse a built-in key to override the default behavior.
import { OpenAPISerializer } from '@orpc/openapi'
const serializer = new OpenAPISerializer({
handlers: {
person: { // <- add support for Person
condition: v => v instanceof Person,
serialize: (v: Person) => ({ name: v.name, age: v.age }),
},
date: { // <- replace the default Date handler
condition: v => v instanceof Date,
serialize: (v: Date) => v.getTime(),
},
},
})Use a custom serializer with OpenAPIHandler and OpenAPILink
const handler = new OpenAPIHandler(router, {
serializer,
})
const link = new OpenAPILink(contract, {
serializer,
})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')INFO
images[0] means the first item in images array.
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>INFO
If the receiver mistakenly handles this payload as a regular (non-file) body, set the standard-server header to help the receiver detect the actual data type and handle it correctly. Learn more about this header in the Standard Server Documentation.
Event Iterator (AsyncIteratorObject)
When the output is an event iterator (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>INFO
If the receiver mistakenly handles this payload as a regular (non-stream) body, set the standard-server header to help the receiver detect the actual data type and handle it correctly. Learn more about this header in the Standard Server Documentation.
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.

