When encoding or decoding data, you must specify a version number. This versioning system allows making changes to the data structure while maintaining compatibility with stored data and older clients.

AutoEncoder

AutoEncoder (Writing AutoEncoder classes) has an easy way to introduce new fields or make changes to fields using the @field decorator.

Let's say you want to change the ID field from a number to a string type, and add a new field called createdAt. Versioning makes this possible:

class MyClass extends AutoEncoder {
    @field({ decoder: NumberDecoder })
    @field({ 
		    decoder: StringDecoder, 
		    version: 2, 
		    upgrade: (old: number) => old.toString(), 
		    downgrade: (n: string) => parseInt(n) 
		 })
    id: number;

    @field({ decoder: StringDecoder }) 
    name: string;

    @field({ decoder: MyClass, nullable: true })
    other: MyClass | null = null;

    @field({ decoder: DateDecoder, version: 2, upgrade: () => new Date() })
    createdAt: Date;
}

For other classes you'll have to implement it manually in your encode/decode methods (Manually implementing encode and decode).

Real example with upgrade


The current version: Version constant

In Stamhoofd, we store the current version in Version (shared/structures/src/Version.ts).

Encoding

import { Version } from "@stamhoofd/structures";

const pablo = Dog.create({ id: "123", name: "Pablo", createdAt: new Date() });
const json = JSON.stringify(pablo.encode({ version: Version }));
// Do something with json (send it over the network, store it...)

Decoding

import { Version } from "@stamhoofd/structures";

const plainObject = JSON.parse(json);
const data = new ObjectData(plainObject, { version: Version });
const pablo = Dog.decode(data);

Increasing the version

We increase the version only during Stamhoofd releases to avoid conflicts when multiple people make changes simultaneously or switch between branches. For adding new properties to data structures during development, use the ...NextVersion helper.