Silgi
Protocols

devalue

Rich type serialization — Date, Map, Set, BigInt, RegExp, undefined, and circular references survive the round-trip.

If your API returns JavaScript types that JSON.stringify can't handle, devalue is the answer. It's a text-based format (like JSON) that preserves rich types through a round-trip.

The problem with JSON

JSON.stringify silently breaks several JavaScript types:

.(new ('2026-01-01')) // "2026-01-01T00:00:00.000Z" (string, not Date)
.(new ([['a', 1]])) // "{}" (empty object)
.(new ([1, 2, 3])) // "{}" (empty object)
.(42n) // throws TypeError
.({ :  }) // "{}" (key is dropped)

If your procedure returns any of these types, the client receives broken data with plain JSON.

How devalue fixes it

devalue (by Rich Harris, the creator of Svelte) serializes values into a JSON string with embedded type markers. When decoded, the original types are reconstructed:

import { ,  } from 'silgi/devalue'

const  = {
  : new ([['alice', { : 1 }]]),
  : new (['admin', 'user']),
  : 9007199254740993n,
  : new ('2026-01-01'),
  : /test/gi,
  : ,
}

const  = () // JSON string with type info
const  = ()

.users // Map { "alice" => { id: 1 } }  -- not an empty object
.tags // Set { "admin", "user" }        -- not an empty object
.count // 9007199254740993n              -- BigInt preserved
.created // Date 2026-01-01T00:00:00.000Z -- Date object, not string
.pattern // /test/gi                       -- RegExp preserved
.optional // undefined                      -- not missing

Supported types

TypeJSONdevalue
String, Number, Boolean, nullYesYes
Object, ArrayYesYes
DateBecomes stringPreserved
MapBecomes {}Preserved
SetBecomes {}Preserved
BigIntThrowsPreserved
RegExpBecomes {}Preserved
undefinedDroppedPreserved
Circular referencesThrowsPreserved

Server integration

devalue works through content negotiation, just like MessagePack. The client sends an Accept header and the server responds in devalue format:

Accept: application/x-devalue+json  -->  response encoded with devalue

Request bodies with Content-Type: application/x-devalue+json are decoded automatically on the server.

Both serve() and handler() support this out of the box. No configuration needed.

When to use devalue vs MessagePack

ScenarioRecommended format
Standard APIs (strings, numbers, arrays)JSON (default)
Smaller payloads, mobile, bandwidthMessagePack
Returning Date, Map, Set, BigIntdevalue
Circular references in datadevalue
Need binary + Date supportMessagePack

If you only need Date support and smaller payloads, MessagePack is a better choice. If you need Map, Set, BigInt, or circular references, devalue is the way to go.

devalue is text-based (the output is a JSON string), so it doesn't save bandwidth like MessagePack. Its advantage is type fidelity, not size.

Direct API

If you need to use devalue outside of the client/server flow:

import { ,  } from 'silgi/devalue'

const  = (myData) // returns a string
const  = () // returns the original value with types intact

What's next?

  • MessagePack — binary protocol for smaller payloads
  • WebSocket — persistent connections for real-time features
  • Client — the full client setup guide

On this page