Skip to main content
Version: 0.17

Basic Serialization

This guide covers the core serialization APIs in Apache Fory JavaScript.

Create a Fory Instance

import Fory from "@apache-fory/core";

const fory = new Fory();

Create one instance, register your schemas, and reuse it. Fory caches the generated serializers after the first register call, so recreating it on every request wastes that work.

Define a Schema with Type.struct

The most common path is to define a schema and register it.

import Fory, { Type } from "@apache-fory/core";

const accountType = Type.struct(
{ typeName: "example.account" },
{
id: Type.int64(),
owner: Type.string(),
active: Type.bool(),
nickname: Type.string().setNullable(true),
},
);

const fory = new Fory();
const { serialize, deserialize } = fory.register(accountType);

Serialize and Deserialize

const bytes = serialize({
id: 42n,
owner: "Alice",
active: true,
nickname: null,
});

const value = deserialize(bytes);
console.log(value);
// { id: 42n, owner: 'Alice', active: true, nickname: null }

The returned bytes value is a Uint8Array/platform buffer and can be sent over the network or written to storage.

Root-Level Dynamic Serialization

Fory can also serialize dynamic root values without first binding a schema-specific serializer.

const fory = new Fory();

const bytes = fory.serialize(
new Map([
["name", "Alice"],
["age", 30],
]),
);

const value = fory.deserialize(bytes);

This is convenient for dynamic payloads, but explicit schemas are usually better for stable interfaces and cross-language contracts.

Primitive Values

const fory = new Fory();

fory.deserialize(fory.serialize(true));
// true

fory.deserialize(fory.serialize("hello"));
// 'hello'

fory.deserialize(fory.serialize(123));
// 123

fory.deserialize(fory.serialize(123n));
// 123n

fory.deserialize(fory.serialize(new Date("2021-10-20T09:13:00Z")));
// Date

Number and bigint

JavaScript number is a 64-bit float, which cannot exactly represent all 64-bit integers. For cross-language contracts or anywhere exact integer sizes matter, use explicit field types in your schema:

  • Type.int32() — 32-bit integer; use JavaScript number
  • Type.int64() — 64-bit integer; use JavaScript bigint
  • Type.float32() / Type.float64() — floating-point

Dynamic root serialization (calling fory.serialize(someNumber) without a schema) will infer a type, but the inferred type is not guaranteed by the API. Use a schema for any stable contract.

Arrays, Maps, and Sets

const inventoryType = Type.struct("example.inventory", {
tags: Type.array(Type.string()),
counts: Type.map(Type.string(), Type.int32()),
labels: Type.set(Type.string()),
});

const fory = new Fory({ ref: true });
const { serialize, deserialize } = fory.register(inventoryType);

const bytes = serialize({
tags: ["hot", "new"],
counts: new Map([
["apple", 3],
["pear", 8],
]),
labels: new Set(["featured", "seasonal"]),
});

const value = deserialize(bytes);

Nested Structs

const addressType = Type.struct("example.address", {
city: Type.string(),
country: Type.string(),
});

const userType = Type.struct("example.user", {
name: Type.string(),
address: Type.struct("example.address", {
city: Type.string(),
country: Type.string(),
}),
});

const fory = new Fory();
const { serialize, deserialize } = fory.register(userType);

const bytes = serialize({
name: "Alice",
address: { city: "Hangzhou", country: "CN" },
});

const user = deserialize(bytes);

If a nested value can be missing, mark it nullable:

const wrapperType = Type.struct("example.wrapper", {
child: Type.struct("example.child", {
name: Type.string(),
}).setNullable(true),
});

Decorator-Based Registration

TypeScript decorators are also supported.

import Fory, { Type } from "@apache-fory/core";

@Type.struct("example.user")
class User {
@Type.int64()
id!: bigint;

@Type.string()
name!: string;
}

const fory = new Fory();
const { serialize, deserialize } = fory.register(User);

const user = new User();
user.id = 1n;
user.name = "Alice";

const copy = deserialize(serialize(user));
console.log(copy instanceof User); // true

Nullability

Field nullability is explicit in schema-based structs.

const nullableType = Type.struct("example.optional_user", {
name: Type.string(),
email: Type.string().setNullable(true),
});

If a field is not marked nullable and you try to write null, serialization throws.

Debugging Generated Code

You can inspect generated serializer code with hooks.afterCodeGenerated.

const fory = new Fory({
hooks: {
afterCodeGenerated(code) {
console.log(code);
return code;
},
},
});

This is useful when debugging schema behavior, field ordering, or generated fast paths.