Skip to content

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:

go
type Category struct {
  Name  string `json:"name"`
  Slug  string `json:"slug"`
}
json
{
  "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

go
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:

go
type Category struct {
  _     any    `json:"-" fs:"name=cat;namespace=cats;label_field=slug"`
  Name  string `json:"name"`
  Slug  string `json:"slug"`
}
json
{
  "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.

go
`fs.db:"{'indexes':[{'name':'idx_name','columns':['name'],'unique':true}]}"`
json
{
  "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.

go
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 name
  • Namespace: Custom the schema namespace
  • LabelField: Custom the label field
  • Fields: 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:

go
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",
      },
    },
  }
}
json
{
  "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 type file 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:

go
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"`
}
json
{
  "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:

go
type Category struct {
  Status string `json:"status" fs:"type=enum" fs.enums:"[{'value':'active','label':'Active'},{'value':'inactive','label':'Inactive'}]"`
}
hjson
[
  {
    'value': 'active',
    'label': 'Active'
  },
  {
    'value': 'inactive',
    'label': 'Inactive'
  }
]
json
{
  "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:

go
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'}"`
}
hjson
// tag.blogs
{
  'type': 'm2m',
  'schema': 'blog',
  'field': 'tags',
  'owner': true
}

// blog.tags
{
  'type': 'm2m',
  'schema': 'tag',
  'field': 'blogs'
}
json
// 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:

go
type Blog struct {
  Name    string `json:"name"`
  Content string `json:"content" fs.renderer:"{'class':'editor','settings':{'height':500}}"`
}
hjson
{
  'class': 'editor',
  'settings': {
    'height': 500
  }
}
json
{
  "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:

go
type Blog struct {
  Name    string `json:"name" fs.db:"{'collation':'utf8mb4_unicode_ci','key':'PRIMARY'}"`
}
hjson
{
  'collation': 'utf8mb4_unicode_ci',
  'key': 'PRIMARY'
}
json
{
  "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

Released under the MIT License.