Stamhoofd uses MySQL for data storage. The data in every table is mapped to a type safe Model object, which you can query and save to the database.

const dog = new Dog();
dog.name = "Pablo";
await dog.save();

console.log(dog.id); // "119df7e7-b619-4c02-b6e8-2ade3e7ca4d7"

If no fields have changed, save won’t do anything

const dog = new Dog();
dog.name = "Pablo";
await dog.save(); // does save

await dog.save(); // won't do anything

Defining models

A model looks like this:

export class Dog extends QueryableModel {
    static table = 'dogs';

    @column({
        primary: true, type: 'string', beforeSave(value) {
            return value ?? uuidv4();
        },
    })
    id!: string;

    @column({ type: 'string' })
    name: string;
    
    @column({ type: 'string', nullable: true })
    ownerName: string | null = null;

    @column({
        type: 'datetime', beforeSave(old?: any) {
            if (old !== undefined) {
                return old;
            }
            const date = new Date();
            date.setMilliseconds(0);
            return date;
        },
    })
    createdAt: Date;

    @column({
        type: 'datetime', beforeSave() {
            const date = new Date();
            date.setMilliseconds(0);
            return date;
        },
        skipUpdate: true,
    })
    updatedAt: Date;
}

After you’ve created a new model, you’ll need to add a migration: Migrations

You can use the beforeSave method to define default values for models. This avoids expensive operations in the constructor of the classes (e.g. generating an ID).

Setting skipUpdate to true make sure that if you call dog.save() it won’t actually send a query to the database if that is the only column that was changed.


Structures inside models

Encoding & decoding (structures)

Models also support storing Encodeable data / structures. This data is stored in JSON columns. This is often used in Stamhoofd to store data properties that are not queried often but are needed. It simplifies the database design and often makes it more performant.

export class Dog extends QueryableModel {
    // ...
    
		@column({ type: 'json', decoder: DogSettings })
    settings = DogSettings.create({}); // default is optional but recommended

    // ...
}