System Schema
Userschema
A UserSchema is a schema created by users through the Admin UI. It is stored in JSON format, can be used to generate database table and is't represented by a Go struct.
For more information, see Schema.
From now on, we will refer to the UserSchema
as Schema
.
SystemSchema
A System Schema is a Schema generated from a Go struct, designed to store system-level data such as users, roles, and permissions.
A System Schema can also be used to define any Schema from within the Go code.
System Schemas are created exclusively through Go code and cannot be deleted. However, they can be extended by users via the Admin UI.
Example:
type Category struct {
Name string `json:"name"`
Slug string `json:"slug"`
}
{
"name": "category",
"namespace": "categories",
"label_field": "name",
"fields": [
{
"type": "string",
"name": "name",
"label": "Name"
},
{
"type": "string",
"name": "slug",
"label": "Slug"
}
]
}
Schema generation
A System Schema
itself is just a Go Struct, and is not a valid schema.Schema
object. FastSchema will generate an actual Schema object from the Go struct, following the rules below.
name
: Created using the snake case of the struct name. For example,Category
->category
.namespace
: Created using the plural form of the schema name. For example,category
->categories
.label_field
: Using the first string field in the struct.fields
: An array of fields in the struct.type
: Field type is created based on the Go type of the field.name
: Field name is created using the snake case of the field name.label
: Field label is created using the field name.
WARNING
The label_field
is required for all system schemas. If the struct does not have a string field, an error will be thrown.
Only the exported fields of the struct are considered for the schema fields.
Field that has a tag json:"-"
will be ignored.
Customize schema
A Go struct contains very limited information for the schema generation. To provide more information about the schema, you can use one of the following methods:
Customize schema using an unexported field with tag fs
type Category struct {
_ any `json:"-" fs:"label_field=slug"`
Name string `json:"name"`
Slug string `json:"slug"`
}
fs
tag is a string that contains a list of properties separated by a semicolon. Each property is in the format key=value
.
Using the fs
tag, you can customize the schema with the following properties:
name
: Custom the schema name.namespace
: Custom the schema namespace.label_field
: Custom the label field.disable_timestamp
: Disable the timestamp fields.db
: Custom the DB index and unique constraint for the table.
Example:
type Category struct {
_ any `json:"-" fs:"name=cat;namespace=cats;label_field=slug"`
Name string `json:"name"`
Slug string `json:"slug"`
}
{
"name": "cat",
"namespace": "cats",
"label_field": "slug",
"fields": [
{
"type": "string",
"name": "name",
"label": "Name"
},
{
"type": "string",
"name": "slug",
"label": "Slug"
}
]
}
Additionally, you can use the fs.db
tag to customize the DB index and unique constraint for the table.
WARNING
The value of fs.db
tag must be a valid HJSON string and satisfy the schema.SchemaDB
type.
`fs.db:"{'indexes':[{'name':'idx_name','columns':['name'],'unique':true}]}"`
{
"db": {
"indexes": [
{
"name": "idx_name",
"columns": ["name"],
"unique": true
}
]
}
}
Customize schema using the Schema()
method
A struct that implements the CustomizableSchema
interface can return a custom schema information. These information will be merged with the generated schema.
type CustomizableSchema interface {
Schema() *Schema
}
Using the Schema()
method brings more flexibility to customize the schema. You can customize any part of the schema, such as the schema name, namespace, label field, DB, etc.
Name
: Custom the schema nameNamespace
: Custom the schema namespaceLabelField
: Custom the label fieldFields
: Custom the fields
Customize Fields using Schema method
The fields in the schema method will only be used to override the existing fields in the struct.
The updated fields in the schema method will be matched with the fields in the struct based on the field name.
If there are any fields returned by the schema method that are not in the struct, they will be ignored.
If you want to add new fields, you need to include them in the struct.
Example:
type Category struct {
Name string `json:"name"`
Slug string `json:"slug"`
}
func (c Category) Schema() *Schema {
return &Schema{
Name: "cat",
Namespace: "cats",
LabelField: "slug",
Fields: []*Field{
{
Type: "string",
Name: "name",
Label: "Category Name",
},
{
Type: "string",
Name: "slug",
Label: "Category slug",
},
},
}
}
{
"name": "cat",
"namespace": "cats",
"label_field": "slug",
"fields": [
{
"type": "string",
"name": "name",
"label": "Category Name"
},
{
"type": "string",
"name": "slug",
"label": "Category Slug"
}
]
}
IMPORTANT
A System Schema can be customized using both the fs
tag and the Schema()
method. If both are used, the Schema()
method will override the fs
tag.
Customize field
A Go struct property includes only the field name and type, which is quite limited.
In contrast, a Schema Field contains additional properties that allow for greater customization (see Field).
FastSchema enables you to provide more detailed information about the field using struct tags
for customization.
Customize with tag json
json
tag is used to marshal and unmarshal the field value to/from JSON string.
FastSchema uses the json
tag to customize the field name
.
Customize with tag fs
fs
tag is a string that contains a list of properties separated by a semicolon. Each property is in the format key=value
.
Using the fs
tag, you can customize the field with the following properties:
label_field
: Select the field as the schema label field.type
: Custom the field type, refer to Field for the list of available types.label
: Custom the field label.size
: Custom the field size.multiple
: Custom the field to accept multiple values. Only field with typefile
supports this property.unique
: Flag the field as unique.optional
: Allow the field to be optional.sortable
: Allow the field to be sortable through the API.filterable
: Allow the field to be filterable through the API.default
: Set the default value for the field.
Example:
type Category struct {
Slug string `json:"slug" fs:"unique;size=255;label=Url"`
Name string `json:"name" fs:"label_field;filterable`
Views int `json:"views" fs:"type=uint64;optional;sortable"`
Note string `json:"note" fs:"optional;default=Empty Note"`
Image any `json:"image" fs:"type=file;multiple;optional"`
}
{
"name": "category",
"namespace": "categories",
"label_field": "name",
"fields": [
{
"type": "string",
"name": "slug",
"label": "Url",
"size": 255,
"unique": true
},
{
"type": "string",
"name": "name",
"label": "Name",
"filterable": true
},
{
"type": "uint64",
"name": "views",
"optional": true,
"sortable": true
},
{
"type": "string",
"name": "note",
"optional": true,
"default": "Empty Note"
},
{
"type": "file",
"name": "image",
"multiple": true,
"optional": true
}
]
}
Customize with tag fs.enums
fs.enums
tag is used to define the list of enum values for the field if the field type is enum
.
The value of fs.enums
tag must meet the following criteria:
- A valid HJSON string.
- Represent the
[]*schema.FieldEnum
type.
Example:
type Category struct {
Status string `json:"status" fs:"type=enum" fs.enums:"[{'value':'active','label':'Active'},{'value':'inactive','label':'Inactive'}]"`
}
[
{
'value': 'active',
'label': 'Active'
},
{
'value': 'inactive',
'label': 'Inactive'
}
]
{
"name": "category",
"namespace": "categories",
"label_field": "name",
"fields": [
{
"type": "enum",
"name": "status",
"enums": [
{
"value": "active",
"label": "Active"
},
{
"value": "inactive",
"label": "Inactive"
}
]
}
]
}
Custom with tag fs.relation
fs.relation
tag is used to define the relation between the field and another schema.
The value of fs.relation
tag must meet the following criteria:
- A valid HJSON string.
- Represent the
*schema.Relation
type.
Example:
type Tag struct {
Name string `json:"name"`
Blogs []*Blog `json:"blogs" fs.relation:"{'type':'m2m','schema':'blog','field':'tags','owner':true}"`
}
type Blog struct {
Name string `json:"name"`
Tags []*Tag `json:"tags" fs.relation:"{'type':'m2m','schema':'tag','field':'blogs'}"`
}
// tag.blogs
{
'type': 'm2m',
'schema': 'blog',
'field': 'tags',
'owner': true
}
// blog.tags
{
'type': 'm2m',
'schema': 'tag',
'field': 'blogs'
}
// data/schemas/tag.json
{
"name": "tag",
"namespace": "tags",
"label_field": "name",
"fields": [
{
"type": "string",
"name": "name"
},
{
"type": "m2m",
"name": "blogs",
"relation": {
"type": "m2m",
"schema": "blog",
"field": "tags",
"owner": true
}
}
]
}
// data/schemas/blog.json
{
"name": "blog",
"namespace": "blogs",
"label_field": "name",
"fields": [
{
"type": "string",
"name": "name"
},
{
"type": "m2m",
"name": "tags",
"relation": {
"type": "m2m",
"schema": "tag",
"field": "blogs"
}
}
]
}
Customize with tag fs.renderer
fs.renderer
tag is used to define the frontend renderer for the field.
The value of fs.renderer
tag must meet the following criteria:
- A valid HJSON string.
- Represent the
*schema.FieldRenderer
type.
Example:
type Blog struct {
Name string `json:"name"`
Content string `json:"content" fs.renderer:"{'class':'editor','settings':{'height':500}}"`
}
{
'class': 'editor',
'settings': {
'height': 500
}
}
{
"name": "blog",
"namespace": "blogs",
"label_field": "name",
"fields": [
{
"type": "string",
"name": "name"
},
{
"type": "string",
"name": "content",
"renderer": {
"class": "editor",
"settings": {
"height": 500
}
}
}
]
}
Customize with tag fs.db
fs.db
tag is used to define the database properties for the field.
The value of fs.db
tag must meet the following criteria:
- A valid HJSON string.
- Represent the
*schema.FieldDB
type.
Example:
type Blog struct {
Name string `json:"name" fs.db:"{'collation':'utf8mb4_unicode_ci','key':'PRIMARY'}"`
}
{
'collation': 'utf8mb4_unicode_ci',
'key': 'PRIMARY'
}
{
"name": "blog",
"namespace": "blogs",
"label_field": "name",
"fields": [
{
"type": "string",
"name": "name",
"db": {
"collation": "utf8mb4_unicode_ci",
"key": "PRIMARY"
}
}
]
}
TIP
A field can also be customized using Customize schema using a struct method