Templefile Specification

Temple determines what to generate by taking in a Templefile. This is a text file containing a specification of the system to build. It consists of a project block and one or more service blocks.

Syntax

Templefiles are not whitespace-sensitive (except to terminate single-line comments), and so indentation, spaces and line breaks are not required and may be inserted at any point between keywords, literals, punctuation, and identifiers. Every declaration except for block declarations must end in a semicolon.

Comments

Comments are a way of providing information to the human reader, and have no effect on the compiled version. Any valid UTF-8 characters may occur within the comment, except for the character sequence marking the end of the comment. Comments may be used at any point where whitespace is valid.

  • Line comments start with //, and end with a line break.
  • Block comments start with /* and end with */. Note that these may not be nested, e.g. /* outer /* inner */ end */.
// line comment
/* block comment */
/*
block comment
*/
/* block
* comment
*/

Blocks

Blocks are a way of grouping declarations. There are three block types: project, service and struct. A block is specified with a block name, a colon, a block type and then curly braces around the content of the block. The block name is an alphanumeric string starting with a capital letter (e.g. Booking, OrderItem or ABC2Schema).

The block can then contain attributes, metadata and more blocks, depending on the block type.

// This project is called Example
Example: project {
// metadata goes here
}

Project Block

The project block is used for configuring an entire project. It includes global configuration with metadata, some of which may be overwritten by the services and structs. By convention this is the first entry in the file. Its name is the name of the project.

Example: project {
#provider(kube);
}

Service Blocks

Service blocks represent entire microservices. They may contain attributes, structs (other database tables to be handled by the same service), and metadata. By convention they are named in the singular, to identify a single entry, e.g. Home.

Example: project {
#provider(kube);
#authMethod(email);
}
Home: service {
address: string;
#auth;
}

Struct Blocks

Struct blocks represent tables stored in the same database as the main service. Every instance of a struct has an implicit reference to its parent service, forming a many-to-one relationship. They may contain attributes, and metadata.

Example: project {
#authMethod(email);
#provider(kube);
}
Home: service {
address: string;
#auth;
Room: struct {
name: string;
#enumerable;
};
}

Attributes

Services and structs can contain attributes, which are pieces of information that will be stored about every entity of this type.

Attribute names are alphanumeric strings starting with a lower-case letter, for example name, bookingID or what3WordsLocation. An attribute is specified by its name, a colon, its type and then any annotations, terminated by a semicolon.

Every attribute will have a type, which will either be a primitive type or a reference to another service or neighbor struct. Primitive types may be parameterized. All parameters are optional, and may be given in order or by name. For example, a string can be specified in the following ways:

  • string if no length constraints are needed
  • string(10) or string(maxLength: 10) if only an upper bound is required.
  • string(10, 5), string(maxLength: 10, minLength: 5) or
    string(minLength: 5, maxLength: 10) if both bounds are needed.

Annotations are given with an @ followed by the name of the annotation, for example @server or @unique. They may be used in combination without any separators.

ExampleService: service {
firstName: string;
lastName: string(20);
handle: string @serverSet @unique;
score: int(min: 0) @server;
}

Foreign attributes

Attributes may also be references to other services. Additionally, you may reference neighbor structs from inside a struct: this means structs inside the same service as the current struct. Simply use the name of the service as the type.

Package: service {
width: int;
height: int;
}
Delivery: service {
box: Package @unique;
destination: string;
}

Note that cycles and self-references are not legal, as it is impossible to initialize such a structure.

Metadata

Blocks may contain metadata, identified by a leading #. These are used to configure the block. Some metadata takes parameters, with the same syntax as with attribute type parameters. The argument may be named (#readable(by: this)), or not (#readable(this)). No block may include the same metadata item twice.

The parameters may be single words (e.g. go), or lists (e.g. [delete, update]). There is a short-hand syntax for lists: #omit[delete, update] is the same as #omit([delete, update]).

Types of metadata

#provider(provider: prov)

Blocks: Project

Specify the provider to use for orchestration code generated. If this is not given, no orchestration code is generated. See the Orchestration guide.

Possible values of provider:

#metrics(metrics: m)

Blocks: Project

Specify the provider to use for recording metrics on the project. If this is not given, no measurement code is generated. See the Metrics guide.

Possible values of m:

  • prometheus: Use Prometheus.
  • More providers are coming soon.

#authMethod(method: auth)

Blocks: Project

Enable authentication for this project, using the method provided. See the Authentication guide.

Possible values of auth:

  • email: Use email and password for login.
  • More methods are coming soon.

#language(language: lang)

Blocks: Service and Project

Specify the language to generate the service server in. If it is not specified, it will fall back to the value given in the project block (which also specifies the language to use for the auth service, if applicable).

Possible values of lang:

  • go: Use Go as the language for the server.
  • More languages are coming soon.

#database(database: db)

Blocks: Service and Project

Specify the database engine to use for the database backing the service. If it is not specified, it will fall back to the value given in the project block (which also specifies the database engine to use for the auth service database, if applicable).

Possible values of db:

  • postgres: Use PostgreSQL as the database for the server.
  • More databases are coming soon.

#enumerable

Blocks: Service and Struct

Generate a list endpoint for this service/struct. See the Enumeration guide.

#auth

Blocks: Service

Mark this service as one tied to an account, so there is a 1:1 mapping between a login account and an entry in this service.

#omit(endpoints: [endpoint])

Blocks: Service and Struct

Do not generate the specified endpoints. See the Omitting Endpoints guide.

Possible values of endpoint:

  • create
  • read
  • update
  • delete

#readable(by: scope), #writable(by: scope)

Blocks: Project and Service

Specify who is allowed to perform read/write operations on this service. The fallback value can be given in the project block, otherwise it will be this for projects with auth, or all for projects without. See the Access Control guide.

Possible values of scope:

  • this Only the creator of this service entry may read/edit it.
  • all Anyone may read/edit it.