Skip to main content

Customizing

Conventions can be customized to suit the standards and idioms of your project. This makes it easier to enforce a consistent style and structure across your codebase, greatly reducing configuration boilerplate and duplication.

Customization can be achieved by modifying, replacing or removing the default conventions provided by the library, or by creating completely new conventions from scratch.

Customization Hook

By now you should be familiar with the Config object that is passed to the verse function on startup. This object has an optional conventions property that can be used to modify the conventions before they get added to the verse instance.

const db = verse({
config: {
conventions: (conventions: Convention[]) => {
// Modify the conventions here.
return conventions;
},
},
});

Customizing Existing Conventions

Some built-in conventions can be customized by modifying their properties. For example, you can change the default max length of a string column by modifying the maxLength property of the MaxLengthDefault convention.

Since conventions are often immutable, like in the case of the MaxLengthDefault convention, you can create a new instance of the convention with the desired properties, and then replace the existing convention with the new one.

const db = verse({
config: {
conventions: (conventions: Convention[]) => {
const index = conventions.findIndex(c => c instanceof MaxLengthDefault);

conventions[index] = new MaxLengthDefault(100);

return conventions;
},
},
});

Now all string columns will have a default max length of 100 characters.

Creating New Conventions

You can create a new convention by implementing the Convention interface or, more conveniently, by extending the AbstractConvention class.

Conventions work by visiting the metadata model, and making changes to it based on the rules defined in the convention.

For example, let's create a convention that adds a prefix to all table names.

class PrefixTables extends AbstractConvention {
constructor(readonly prefix = "tbl_") {
super();
}

override visitEntity(entity: EntityModel) {
return entity.withTable(this.prefix + entity.table);
}
}

Because we are extending the AbstractConvention class, we only need to implement the visitEntity method. This method will be called for every entity in the metadata model, and we can modify the entity as needed.

Now we can add this convention to the verse instance.

const db = verse({
config: {
conventions: (conventions: Convention[]) => {
conventions.push(new PrefixTables());

return conventions;
},
},
});

We add our new convention to the end of the list of conventions. This is important because the order of conventions matters. Conventions are applied in the order they are added, so later conventions can override or depend on changes made by earlier ones. In this case, we want to apply the prefix only after the table name has been initially set.