/

Core Taxi Language

Get familiar with the taxi syntax


Taxi is a language for documenting data schemas - such as data models, and the contracts of APIs. Taxi describes data semantically, which allows powerful tooling to discover and map data based on it's meaning, rather than the name of a field.

Taxi provides a lightweight way to describe the meaning of data in a consistent, business friendly language.

Overview

namespace taxi.demo  // Namespaces are optional, but recommended.

@SomeAnnotation
model Person {
  @Id // Annotations are supported at both class and field level
  id : PersonId inherits Int // Inline inheritance allow succinct semantic types

  /** Comments can be defined in either
   * block style, or // inline style
  */
  firstName : FirstName inherits String
  lastName : String // You don't have to use semantic types - but it is recommended

  friends : Person[] // Lists are supported

  spouse : Person? // '?' indicates nullable types
}

Key Concepts

Syntax and Files

Taxi files are simple text files, saved with a .taxi suffix. Taxi is it's own language, and doesn't use things like JSON or YAML. This means that we can specs that are expressive, and much easier to understand than the JSON / YAML counterparts.

Namespaces

Like in most languages, things in Taxi (types, services, operations, etc) need to have a unique name.

To help prevent name clashes, Taxi uses namespaces - simply a way of disambiguating between two things with the same name.

namespace customers

type Name inherits String // The fully qualified name is customers.Name

Namespaces in Taxi are analogous to namespaces in C#, or package in Java / Kotlin.

When namespaces are in use, the following rules apply:

  • Type references within the same namespace need not qualify their references
  • Type references within a different namespace must use a fully qualified reference
It's not mandatory to use namespaces, but it's recommended. It's useful to avoid name collisions and improves the output from generators, which typically are targeting languages that **do** use packages / namespaces

Namespaces can be declared in two ways.

Single namespace files

If everything in a single file belongs to the same namespace, simply add namespace xxx at the top of the file. Everything that is declared in the file becomes part of the namespace.

namespace customers

type Name inherits String // The fully qualified name is customers.Name

Multi-namespace files

If you have multiple namespaces within the same file, you can define namespaces using curly brace syntax:

namespace people {
    // fullyQualifiedName is people.Name
    type Name inherits String
}
namespace pets {
    // fullyQualifiedName is pets.Name
    type Name inherits String
}

Imports

Imports are a way of including a type declared in another file. When a type or model is imported, it can be referenced using it's name, rather than requiring the fully qualified name.

Imports come before a namespace declaration.

import people.Name

namespace customers

// The same as:
// type customers.CustomerFirstName inherits people.Name
type CustomerFirstName inherits Name
Edit on Github