Simple Relational Schema Language
Version: 0.4.1
Editor: editor.srsl.docs.hexinsights.dev
Basic Syntax
Every .srsl (pronounced "sersel") file is composed of
(up to) three sections.
Only the model declarations are required.
Models
A model declaration begins with the model keyword followed by a
PascalCased name.
model ModelName
This is a minimal valid .srsl file!
A model may also have named attributes and fields. These are both optional, but must appear in that order. We will see what named attributes look like later. For now, let's focus on fields.
A field is indicated by a snake_cased name followed by a colon and a PascalCased type.
model ModelName [ field_name: Type ]
A type may receive "type values". Type values follow a type name, and are enclosed by parentheses. For example, defining an Enum type looks like this:
model ModelName [ status: Enum ( first_value second_value ) ]
Notice that a list of values is not comma separated.
A field may also have associated properties. We will encounter these in more detail in the properties section, but these are declared by flat brackets.
model ModelName [ nickname: String [ Optional ] ]
Edges
Edges are specified by model names, aliases, and an arrow indicating the relation type.
| Relation | Arrow |
|---|---|
| O2O | <-> |
| O2M | <-< |
| M2M | >-< |
There is no syntax for a M2O relation, since this is just a reversed O2M relation. However, the schema editor allows you to select such a relation for convenience.
So an example of these relations in action looks like this:
model MorningStar model EveningStar model Book model Author model Task model CPUCore edge MorningStar "morning_star" <-> EveningStar "evening_star" edge Author "author" <-< Book "books" edge Task "tasks" >-< CPUCore "cpu_cores"
So an edge is declared with:
- The
edgekeyword - PascalCased name for the left model
- snake_cased alias in quotes
- arrow
- PascalCased name for the right model
- snake_cased alias in quotes
Like models, edges may also take named attributes, which will be covered in the following section.
Named Attributes
Both models and edges accept named attributes. A named attribute may either specify a string or a list of properties.
The name of a named attribute is specified with the "@"
(at) sign followed by a snake_cased name.
For example, we can add a description to a model or an edge.
model ModelName [ @description = "a description of my model" ] edge ModelName "alias" <-> ModelName "alias" [ @description = `a "description" of my edge` ]
String literals may be written with double quotes or back ticks.
More commonly, named attributes will be used with property lists. They look like this:
model ModelName [ @properties = [ APIPrivate ] ] edge ModelName "alias" <-> ModelName "alias" [ @properties = [ APIPrivate ] @left_properties = [ Required ] ]
The details of properties are discussed in the following section.
Properties
Properties can appear in three different places.
- properties on a field in a model,
- properties on a model, and
- properties on an edge
In general, a property is just a PascalCased name. However, properties can also accept "property values". Like type values on a field, these are values following a property name, enclosed by parentheses.
Unlike type values, however, there are 4 possible kinds of property values.
- "plain" values,
- strings,
- code blocks, and
- numbers
A plain property value is just a snake_cased value. Let's see an example with an Enum type on a model field.
model ModelName [ status: Enum ( not_started in_progress cancelled complete ) [ Default ( not_started ) ] ]
Here the field status has a single property
Default which has the plain property value
not_started.
A property may accept multiple property values. For example, here is a field with a restriction on the values it can take on. In this case, the property values are numbers.
model ModelName [ rating: Int [ Range ( 0 100 ) ] ]
A property value may be a string literal, for example the default value of a field.
model ModelName [ name: String [ Default ( "default value" ) ] ]
Finally, the most general possible value which can be passed is a code block.
This is useful for defining custom values. For example custom validation.
model ModelName [
color: String [
Match (
'''regexp.MustCompile("^#[0-9a-f]{3,6}$")'''
)
]
very_custom_thing: String [
Validate (
'''func(s string) error {
if strings.ToLower(s) == s {
return errors.New("very_custom_thing must begin with uppercase")
}
return nil
}'''
)
]
]
At the moment, model and edge named property lists do not generally take property values. But the syntax is the same, a list of PascalCased values.
model ModelName [ @properties = [ APIUpdateOnly ] ] edge ModelName "alias" <-> OtherModel "alias" [ @left_properties = [ APIReadOnly ] ]
Meta Tags
Meta tags are a simple way to specify metadata about your schema. They come at
the beginning of a .srsl file and are specified with a
"#+" followed by a PascalCased tag name, followed by a
":". The rest of the line is treated as the value for the metatag.
For example, you might specify the title of your schema or project:
#+Title: The title of the project or schema model Models
Full Example
#+Title: The title of the project or schema
model ModelName [
@description = "a description of my model"
@properties = [
APIUpdateOnly
]
rating: Int [
Range (
0
100
)
]
status: Enum (
not_started
in_progress
cancelled
complete
) [
Default (
not_started
)
APIReadOnly
]
]
model OtherModelName [
color: String [
Match (
'''regexp.MustCompile("^#[0-9a-f]{3,6}$")'''
)
]
very_custom_thing: String [
Validate (
'''func(s string) error {
if strings.ToLower(s) == s {
return errors.New("very_custom_thing must begin with uppercase")
}
return nil
}'''
)
]
]
edge ModelName "alias" <-> OtherModelName "alias" [
@description = `a "description" of my edge`
@left_properties = [
APIReadOnly
]
]
Supported Values
Models
Named Attributes
| Attribute | Description |
|---|---|
@description |
Description of the model. |
@properties |
A list of property values for the model. |
@table_name |
Custom SQL table name for the model. |
Properties
| Property | Description |
|---|---|
APIImmutable |
Indicates that instances of the model cannot be changed via the API, only created or deleted. |
APIPrivate |
Indicates that the model should not be included in the API. |
APIReadOnly |
Indicates that instances of the model can only be read via the API, not created, updated, or deleted. |
APIUpdateOnly |
Indicates that instance of the model can only be updated (and read) via the API, not created or deleted. |
NoGlobalSearch |
Indicates that the model should not appear in global search results. |
NoHistory |
Indicates that history tracking should not be enabled for the model. |
NoPermissions |
Indicates that permissions should not be generated for the model. |
NoSearch |
Indicates that the model should not support model-level search. Implies
NoGlobalSearch.
|
Fields
Types
| Type | Description |
|---|---|
Bool |
Boolean |
Bytes |
Bytes |
Date |
Date |
Enum |
Enum (must specify values) |
File |
File |
Float |
Float |
ID |
ID (for GraphQL) |
Int |
Int |
Other |
Other type |
String |
String |
Time |
Time |
UUID |
UUID |
Properties
| Property | Description |
|---|---|
AllowsEmpty |
Allows empty strings. |
Annotations |
Sets custom annotations on the field's ent schema definition. |
APIComputed |
Indicates the field's value is computed in the API layer of the server. Useful for properties that depend on the viewer (user). For general computed fields, see the Computed option. |
APIPrivate |
Hides the field from the API. |
APIReadOnly |
Disallows setting or updating the field through the API. |
APIUpdateOnly |
Disallows setting a value for the field when creating an instance of the model through the API. In other words, the initial value is set automatically by the server. |
Computed |
Indicates the field's value is computed on the server rather than stored in the database. |
Default |
The default value for the field. Can be a value or a function that returns a value |
DefaultOrderField |
Sets the default order of the model to this field. Only necessary if customization is desired. Note that if multiple fields have this property, the first field will be used. |
Immutable |
Prevents updates to the field after creation. |
Match |
Sets a custom pattern that values of the field must match. A Go code snippet that evaulates to a regular expression. |
Max |
Sets a maximum allowed value for the field. |
MaxLen |
Sets a maximum length. |
Min |
Sets a minimum allowed value for the field. |
MinLen |
Sets a minimum length. |
Negative |
Requires values of the field be negative. |
Nillable |
Allows nil/null values for the field. |
NoHistory |
Excludes the field from history tracking. |
NonNegative |
Requires values of the field be non-negative. |
NoSearch |
Excludes the field from the default searchable-fields. Only applies to String fields. |
Optional |
Removes requirement to give a value for the field when creating a new instance of the model. |
OrderField |
Allows ordering instances of the model on the field through the API. |
Positive |
Requires values of the field be positive. |
Range |
Sets minimum and maximum allowed values for the field. |
Sensitive |
Prevents the field from being printed or encoded. |
StructTag |
Sets a custom struct tag for the field. |
Unique |
Requires each instance of the model to have a distinct value for the field. |
Validate |
Sets a custom validation function for the field. |
Edges
Named Attributes
| Attribute | Description |
|---|---|
@left_properties |
Properties of the edge on the left side. |
@properties |
General properties of the edge. |
@right_properties |
Properties of the edge on the right side. |
Properties
| Property | Description |
|---|---|
APIPrivate |
Excludes the edge from the API. |
APIReadOnly |
Makes the edge read-only in the API—disallows mutations on the edge. |
Immutable |
Disallows update actions after the edge value has been set. |
OnDelete |
Defines behavior upon deleting the instance that holds the foreign key
(sets ON DELETE in SQL). Allowed values:
Cascade, NoAction, Restrict,
SetDefault, SetNull.
|
Sided Properties
Properties that are only available in the @left_properties and
@right_properties attributes.
| Property | Description |
|---|---|
APIUpdateOnly |
Makes the edge update-only in the API—the value may not be set at create time. |
CreateFormFieldType |
Defines how the frontend should generate create form fields for this
edge. Note that this defines the field for the model on the side it is
defined, for example, if set in @left_properties then this
defines the field that shows up in the left model's form. Allowed
values: Omit, Select, Nested.
|
DetailFormFieldType |
Defines how the frontend should generate detail form fields for this
edge. See CreateFormFieldType for details. Allowed values:
Omit, Select.
|
Required |
Indicates that the edge may not be null/blank/empty. |
Searchable |
Causes model-level search to traverse the edge. |