Skip to main content
Version: 0.17

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:

  1. Same type identity on both sides — same numeric ID, or same namespace + typeName.
  2. Compatible field types — a Type.int32() field in JavaScript matches Java int, Go int32, C# int.
  3. Same nullability — if one side marks a field nullable, the other should too.
  4. Same compatible mode if using schema evolution.
  5. Same reference tracking config if your data has shared or circular references.

Step-by-Step: JavaScript to Another Runtime

  1. Define the JavaScript schema with the same type name or numeric ID used by the other runtime.
  2. Register the schema in both runtimes.
  3. Match field types, nullability, and compatible settings.
  4. 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 (Java int, Go int32, C# int)
  • Type.int64() with bigint values for 64-bit integers (Java long, Go int64)
  • Type.float32() or Type.float64() for floating-point values

Date and Time

  • Type.timestamp() — a point in time; round-trips as a JavaScript Date
  • Type.date() — a date without time; deserializes as Date
  • Type.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.