# Models
# Concept
# Content Type's models
Models are a representation of the database's structure. They are split into two separate files. A JavaScript file that contains the model options (e.g: lifecycle hooks), and a JSON one that represents the data structure stored in the database.
Path โ ./api/restaurant/models/Restaurant.js
.
module.exports = {
lifecycles: {
// Called before an entry is created
beforeCreate(data) {},
// Called after an entry is created
afterCreate(result) {},
},
};
Path โ ./api/restaurant/models/Restaurant.settings.json
.
{
"kind": "collectionType",
"connection": "default",
"info": {
"name": "restaurant",
"description": "This represents the Restaurant Model"
},
"attributes": {
"cover": {
"collection": "file",
"via": "related",
"plugin": "upload"
},
"name": {
"default": "",
"type": "string"
},
"description": {
"default": "",
"type": "text"
}
}
}
In this example, there is a Restaurant
model which contains the attributes cover
, name
and description
.
# Component's models
It also exist another type of models named components
. A component is a data structure that can be used in one or many other API's model. There is no lifecycle related, only a JSON file definition
Path โ ./components/default/simple.json
{
"connection": "default",
"collectionName": "components_default_simples",
"info": {
"name": "simple",
"icon": "arrow-circle-right"
},
"options": {},
"attributes": {
"name": {
"type": "string"
}
}
}
In this example, there is a Simple
component which contains the attributes name
. And the component is in the category default
.
# Where are the models defined?
For Content Types, models are defined in each ./api/**/models/
folder. Every JavaScript or JSON file in these folders will be loaded as a model. They are also available through the strapi.models
and strapi.api.**.models
global variables. Usable everywhere in the project, they contain the ORM model object that they refer to. By convention, a model's name should be written in lowercase.
For Components, models are defined in ./components
folder. Every components has to be under a subfolder (the category name of the component).
# How to create a model?
TIP
If you are just starting out it is very convenient to generate some models with the Content Type Builder, directly in the admin interface. You can then review the generated model mappings on the code level. The UI takes over a lot of validation tasks and gives you a feeling for available features.
# For Content Types models
Use the CLI, and run the following command strapi generate:model restaurant name:string description:text
.
Read the CLI documentation for more information.
This will create two files located at ./api/restaurant/models
:
Restaurant.settings.json
: contains the list of attributes and settings. The JSON format makes the file easily editable.Restaurant.js
: importsRestaurant.settings.json
and extends it with additional settings and life cycle callbacks.
TIP
When you create a new API using the CLI (strapi generate:api <name>
), a model is automatically created.
# For Components models
To create a component you will have to use the Content Type Builder from the Admin panel, there is no generator for components.
Or you can create your component manually by following the file path described previously and by following the file structure described below.
# Model settings
Additional settings can be set on models:
kind
(string) - Define if the model is a Collection Type (collectionType
) of a Single Type (singleType
) - only for Content Typesconnection
(string) - Connection name which must be used. Default value:default
.collectionName
(string) - Collection name (or table name) in which the data should be stored.globalId
(string) - Global variable name for this model (case-sensitive) - only for Content Typesattributes
(object) - Define the data structure of your model. Find available options below.
Path โ Restaurant.settings.json
.
{
"kind": "collectionType",
"connection": "mongo",
"collectionName": "Restaurants_v1",
"globalId": "Restaurants",
"attributes": {}
}
In this example, the model Restaurant
will be accessible through the Restaurants
global variable. The data will be stored in the Restaurants_v1
collection or table and the model will use the mongo
connection defined in ./config/database.js
WARNING
If not set manually in the JSON file, Strapi will adopt the filename as globalId
.
The globalId
serves as a reference to your model within relations and Strapi APIs. If you chose to rename it (either by renaming your file or by changing the value of the globalId
), you'd have to migrate your tables manually and update the references.
Please note that you should not alter Strapi's models globalId
(plugins and core ones) since it is used directly within Strapi APIs and other models' relations.
TIP
The connection
value can be changed whenever you want, but you should be aware that there is no automatic data migration process. Also if the new connection doesn't use the same ORM you will have to rewrite your queries.
# Model information
The info key on the model-json states information about the model. This information is used in the admin interface, when showing the model.
name
: The name of the model, as shown in admin interface.description
: The description of the model.icon
: The fontawesome V5 name - only for Components
Path โ Restaurant.settings.json
.
{
"info": {
"name": "restaurant",
"description": ""
}
}
# Model options
The options key on the model-json states.
timestamps
: This tells the model which attributes to use for timestamps. Accepts eitherboolean
orArray
of strings where first element is create date and second element is update date. Default value when set totrue
for Bookshelf is["created_at", "updated_at"]
and for MongoDB is["createdAt", "updatedAt"]
.privateAttributes
: This configuration allows to treat a set of attributes as private, even if they're not actually defined as attributes in the model. Accepts anArray
of strings. It could be used to remove from API responses timestamps or_v
when using MongoDB. The set ofprivateAttributes
defined in the model are merged with theprivateAttributes
defined in the global Strapi configuration.
Path โ User.settings.json
.
{
"options": {
"timestamps": true,
"privateAttributes": ["id", "created_at"]
}
}
# Define the attributes
The following types are currently available:
string
text
richtext
email
password
integer
biginteger
float
decimal
date
time
datetime
boolean
enumeration
json
uid
# Validations
You can apply basic validations to the attributes. The following supported validations are only supported by MongoDB connection. If you're using SQL databases, you should use the native SQL constraints to apply them.
required
(boolean) โ If true, adds a required validator for this property.unique
(boolean) โ Whether to define a unique index on this property.index
(boolean) โ Adds an index on this property, this will create a single field index that will run in the background. Only supported by MongoDB.max
(integer) โ Checks if the value is greater than or equal to the given maximum.min
(integer) โ Checks if the value is less than or equal to the given minimum.
Security validations
To improve the Developer Experience when developing or using the administration panel, the framework enhances the attributes with these "security validations":
private
(boolean) โ If true, the attribute will be removed from the server response (it's useful to hide sensitive data).configurable
(boolean) - if false, the attribute isn't configurable from the Content Type Builder plugin.
# Exceptions
uid
targetField
(string) โ The value is the name of an attribute that hasstring
oftext
type.options
(string) โ The value is a set of options passed to the underlyinguid
generator. A caveat is that the resultinguid
must abide to the following RegEx/^[A-Za-z0-9-_.~]*$
.
# Example
Path โ Restaurant.settings.json
.
{
...
"attributes": {
"title": {
"type": "string",
"min": 3,
"max": 99,
"unique": true
},
"description": {
"default": "My description",
"type": "text",
"required": true
},
"slug": {
"type": "uid",
"targetField": "title"
}
...
}
}
# Relations
Relations let you create links (relations) between your Content Types.
# Components
Component field let your create a relation between your Content Type and a Component structure.
# Example
Lets say we created an openinghours
component in restaurant
category.
Path โ ./api/restaurant/models/Restaurant.settings.json
.
{
"attributes": {
"openinghours": {
"type": "component",
"repeatable": true,
"component": "restaurant.openinghours"
}
}
}
repeatable
(boolean): Could betrue
orfalse
that let you create a list of data.component
(string): It follows this format<category>.<componentName>
.
# Dynamic Zone
Dynamic Zone fields let you create a flexible space, in which to compose content, based on a mixed list of components.
# Example
Lets say we created an slider
and content
component in article
category.
Path โ ./api/article/models/Article.settings.json
.
{
"attributes": {
"body": {
"type": "dynamiczone",
"components": ["article.slider", "article.content"]
}
}
}
components
(array): Array of components, that follows this format<category>.<componentName>
.
# Lifecycle hooks
The lifecycle hooks are functions that get triggered when the Strapi queries
are called. They will get triggered automatically when you manage your content in the Admin Panel or when you develop custom code using queries
ยท
To configure a ContentType
lifecycle hooks you can set a lifecycles
key in the {modelName}.js
file located at ./api/{apiName}/models/{modelName}.js
folder.
# Available Lifecycle hooks
# Example
Path โ ./api/user/models/User.js
.
module.exports = {
/**
* Triggered before user creation.
*/
lifecycles: {
async beforeCreate(data) {
const passwordHashed = await strapi.api.user.services.user.hashPassword(data.password);
data.password = passwordHashed;
},
},
};
TIP
You can mutate one of the parameters to change its properties. Make sure not to reassign the parameter as it will have no effect:
This will Work
module.exports = {
lifecycles: {
beforeCreate(data) {
data.name = 'Some fixed name';
},
},
};
This will NOT Work
module.exports = {
lifecycles: {
beforeCreate(data) {
data = {
...data,
name: 'Some fixed name',
};
},
},
};
# Custom use
When you are building custom ORM specific queries the lifecycles will not be triggered. You can however call a lifecycle function directly if you wish.
Bookshelf example
Path - ./api/{apiName}/services/{serviceName}.js
module.exports = {
async createCustomEntry() {
const ORMModel = strapi.query(modelName).model;
const newCustomEntry = await ORMModel.forge().save();
// trigger manually
ORMModel.lifecycles.afterCreate(newCustomEntry.toJSON());
},
};
TIP
When calling a lifecycle function directly, you will need to make sure you call it with the expected parameters.
โ Middlewares Plugins โ