Cross-Language Serialization
Fory JavaScript serializes to the same binary format as the Java, Python, Go, Rust, Swift, and C++ Fory runtimes. You can write a message in JavaScript and read it in Java — or any other direction — without any conversion layer.
Things to keep in mind:
- The Fory JavaScript runtime reads and writes cross-language payloads only (it does not support any language-native format).
- Out-of-band mode is not currently supported.
Requirements for a Successful Round Trip
For a message to survive a round trip between JavaScript and another runtime:
- Same type identity on both sides — same numeric ID, or same
namespace + typeName. - Compatible field types — a
Type.int32()field in JavaScript matches Javaint, Goint32, C#int. - Same nullability — if one side marks a field nullable, the other should too.
- Same
compatiblemode if using schema evolution. - Same reference tracking config if your data has shared or circular references.
Step-by-Step: JavaScript to Another Runtime
- Define the JavaScript schema with the same type name or numeric ID used by the other runtime.
- Register the schema in both runtimes.
- Match field types, nullability, and
compatiblesettings. - Test a real payload end-to-end before shipping.
JavaScript side:
import Fory, { Type } from "@apache-fory/core";
const messageType = Type.struct(
{ typeName: "example.message" },
{
id: Type.int64(),
content: Type.string(),
},
);
const fory = new Fory();
const { serialize } = fory.register(messageType);
const bytes = serialize({
id: 1n,
content: "hello from JavaScript",
});
On the other side, register the same example.message type (same name or same numeric ID) using the peer runtime's API:
Field Naming
Fory matches fields by name. When models are defined in multiple languages, keep field names consistent — or at minimum use a naming scheme that maps unambiguously across languages (e.g. snake_case everywhere).
When using compatible: true for schema evolution, field order differences are tolerated, but the names themselves must still match.
Numeric Types
JavaScript number is a 64-bit float, which does not map cleanly to every integer type in other languages. Use explicit schema types:
Type.int32()for 32-bit integers (Javaint, Goint32, C#int)Type.int64()withbigintvalues for 64-bit integers (Javalong, Goint64)Type.float32()orType.float64()for floating-point values
Date and Time
Type.timestamp()— a point in time; round-trips as a JavaScriptDateType.date()— a date without time; deserializes asDateType.duration()— exposed as a numeric millisecond value in JavaScript
Polymorphic Fields
Type.any() lets a field hold different types at runtime, but it is harder to keep in sync across languages. Prefer explicit field schemas whenever possible.
const wrapperType = Type.struct(
{ typeId: 3001 },
{
payload: Type.any(),
},
);
Enums
Enum member order must match across languages. Fory encodes enums by ordinal position, not by value.
const Color = { Red: 1, Green: 2, Blue: 3 };
const fory = new Fory();
fory.register(Type.enum({ typeId: 210 }, Color));
Use the same type ID or type name in every peer runtime.
Safety Limits
The maxDepth, maxBinarySize, and maxCollectionSize options protect the JavaScript runtime from overly large payloads. They do not change the binary format — they only control what the local runtime is willing to accept.