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 JavaScriptnumberType.int64()— 64-bit integer; use JavaScriptbigintType.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.