Skip to main content

Configuring models

What you'll learn
  • Configuring a model through its factory or with a custom factory
  • Learning each available configuration option goal and usage

How to configure a model​

You may configure your model when creating them through your factory makeModel or when defining a custom factory such as described in the model composition guide.

When using inside a model creation (makeModel), the configuration will be dedicated to this model. Configuration is the first argument and definition is the second one:

post.ts
import { makeModel } from '@foscia/core';

makeModel({
type: 'posts',
/* ...configuration */
}, {
/* ...definition */
});

When using inside a model factory creation (makeModelFactory), the configuration will be shared between all models created through this factory. Configuration is the first argument and definition is the second one:

makeModel.ts
import { attr, makeModelFactory, toDateTime } from '@foscia/core';

export default makeModelFactory({
/* ...common configuration */
}, {
/* ...common definition */
});

Configuration options​

info

All the following examples do not declare any model definition for readability purpose.

Common​

type​

When using the model factory makeModel, you have probably seen that the first argument of the function is a string. This is the type of the current model.

It may be used for different purpose depending on the context:

  • Concatenate in a URL to target an API specific resource
  • Identify a record from an API/data source serialized data
  • Guess a table name for a SQL database implementation
  • Etc.

To define it, you should follow your data source convention. As an example, in a JSON:API the resource types are defined in plural kebab case, such as blog-posts or comments.

You may define the type as the only configuration of the model or as a configuration property (if you want to define other properties):

post.ts
import { makeModel } from '@foscia/core';

makeModel('posts');
// OR
makeModel({ type: 'posts' });

path​

Default: The model type.

The path is used to query the model. It defaults to the model's type.

In an HTTP API, it is used as the endpoint. In a SQL database, it would be the table.

post.ts
import { makeModel } from '@foscia/core';

makeModel({
type: 'posts',
path: 'blog-posts',
});

guessPath​

Default: no transformation.

Recommandation: use this configuration option inside a custom model factory.

guessPath transform a model's type to guess its path.

Here is an example of a path guesser using hypothetical toKebabCase function. If your JSON:API record types are using camel cased types but your endpoint are kebab cased:

post.ts
import { makeModel, isManyRelationDef } from '@foscia/core';

makeModel({
type: 'blogPosts',
guessPath: (type: string) => toKebabCase(type),
});

guessIdPath​

Default: no transformation.

Recommandation: use this configuration option inside a custom model factory.

guessIdPath transform a model's id to guess its path when querying. This can be useful when you want to query records by given their endpoint as their ID.

post.ts
import { makeModel, isManyRelationDef } from '@foscia/core';

// If `/api/posts/1` is given when querying Post, only `1` will be used
// as ID in requested endpoint.
makeModel({
type: 'posts',
guessIdPath: (id) => String(id).split('/').pop()!,
});

guessAlias​

Default: no transformation.

Recommandation: use this configuration option inside a custom model factory.

guessAlias transform a model's property key to guess its alias.

Here is an example of a path guesser using hypothetical toKebabCase function. If your JSON:API record properties are using kebab cased keys but your models are camel cased:

post.ts
import { makeModel, isManyRelationDef } from '@foscia/core';

makeModel({
type: 'BlogPosts',
guessAlias: (key: string) => toKebabCase(key),
});

guessRelationType​

Default: pluralize key for "to one" relation.

Recommandation: use this configuration option inside a custom model factory.

To avoid defining types on all your relations even when necessary (for example in some cases with REST implementation), you may configure a type guesser on your models.

Here is an example of a type guesser using hypothetical toKebabCase and pluralize functions. For example, if a Comment model has a blogPost relation, this would guess the type to blog-posts;

post.ts
import { makeModel, isPluralRelationDef, ModelRelation } from '@foscia/core';

makeModel({
type: 'posts',
guessRelationType: (def: ModelRelation) => (
isPluralRelationDef(def) ? def.key : pluralize(def.key)
),
});

guessRelationPath​

Default: no transformation.

Recommandation: use this configuration option inside a custom model factory.

guessRelationPath transform a model's relation to its "path". This is only needed in specific implementation context (such as JSON:API when querying related instances of a base model instance).

Here is an example of a type guesser using hypothetical toKebabCase function. If your JSON:API record properties are using kebab cased keys but your models are camel cased:

post.ts
import {
makeModel,
isManyRelationDef,
ModelClass,
ModelRelation,
} from '@foscia/core';

makeModel({
type: 'posts',
guessRelationPath: (def: ModelRelation) => toKebabCase(def.key),
});

compareValue and cloneValue​

Default: compare will check for strict equality and clone will return the base value.

Recommandation: use this configuration option inside a custom model factory.

You may have noticed that Foscia provide some model history features. Those allow you to know which parts of a model instance changed since its retrieval from the data source or interact with those changes, through some utilities functions: changed, reset, and syncOriginal.

Currently, Foscia won't clone any value when syncing the instance values (on save, etc.) and will do a strict equal comparison to known if the value changed.

The following model configuration is equivalent to the default behavior of Foscia:

post.ts
import { makeModel } from '@foscia/core';

makeModel({
type: 'posts',
compareValue: (newValue, prevValue) => nextValue === prevValue,
cloneValue: (value) => value,
});

You may change those two functions to really clone values when syncing the instance state. Keep in mind that:

  • Values might be any value your instance could contain, including complex object and even other model instance
  • Cloned values might be restored through reset utility
  • Making a real clone of a value without updating the comparator will break the history because of its default behavior

strict​

Default: undefined.

Recommandation: use this configuration option inside a custom model factory.

Globally enable all strict policies on model:

info

If a specific strict policies is enabled/disabled, it supersedes the global strict settings.

strictProperties​

Default: false.

Recommandation: use this configuration option inside a custom model factory.

When enabled, getting a model's instance property value will throw an error if the value was not retrieved from the store or if the relation is not loaded.

strictReadOnly​

Default: true.

Recommandation: use this configuration option inside a custom model factory.

When enabled, setting a model's instance readonly property value will throw an error.

HTTP​

The following configuration options are specific to HTTP models (when interacting with JSON:API, JSON REST, etc.).

baseURL​

You may define a baseURL configuration option on your models. It will replace the default base URL define on the adapter.

post.ts
import { makeModel } from '@foscia/core';

makeModel({
type: 'posts',
baseURL: 'https://example.com/api/v2',
});