Hex Insights Hex Insights

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.

  1. Meta Tags
  2. Models
  3. Edges

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 edge keyword
  • 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.