Introduction
Welcome to Taxi đź‘‹
Taxi is a language for documenting and connecting data and services across your entire ecosystem - from APIs and databases to message queues and beyond.
It’s designed to help you build more maintainable, flexible integrations by focusing on what your data means, not just how it’s structured.
​Example
Taxi allows you to be really expressive about what the meaning is of data returned from an API. By describing the meaning of our data, we can start to make data interchangeable.
For example:
type CustomerId inherits String
model Customer {
id : CustomerId
firstName : FirstName inherits String
lastName : LastName inherits String
}
model Order {
orderId : OrderId inherits Int
customerId : CustomerId
}
Now your systems can understand that Customer.id
and Order.customerId
represent the same concept, enabling automatic data discovery and integration.
TaxiQL - Taxi’s query language - lets you leverage these relationships to find and combine data across your ecosystem, based on meaning:
// Find all customers and their orders
find { Customer[] } as {
customerId : CustomerId
name : concat(FirstName, ' ', LastName)
orders: Order[]
totalSpent: Decimal = Order[].sum((OrderValue) -> OrderValue)
}[]
Try this example in Taxi Playground
​Getting started
​Taxi Playground
Taxi Playground is a playground for quickly writing Taxi, and generating diagrams. Read more about why we built it, and where it’s going, here
​Grab the source
Taxi is an open source (Apache 2) project, hosted on Github
​FAQ's
​Why do we need another schema language?
Go deeper...
Schema languages today focus inwards - they’re often great at describing what this API does. However, they’re not great at describing how this API relates to other APIs in the ecosystem - such as:
- How does the data returned relate to other data in the organisation?
- How do the inputs relate to other data available in the organisation.
Taxi aims to bridge this gap, by using Semantics to model how data relates between systems.
Taxi generally works with your other schema languages by polyfilling them with semantic metadata.
This difference boils down to Structural vs Semantic contracts.
​Structural contracts:
- Where is this API accessible (port, transport mechanism, path, HTTP verbs, etc)
- How are requests / responses encoded? (JSON / Protobuf, etc)
- What keys are present in the maps & arrays returned?
- What inputs are expected?
​Semantic contracts:
- What does the data mean?
- How do the inputs and output from this system relate to other systems?
- What are the side effects of calling this method?
The concept of Semantic vs Structural contracts are discussed in detail in this talk:
​How does Taxi relate to OpenAPI / RAML / AsyncApi etc.
Taxi has strong interoperability with OpenApi. While you could use Taxi to fully describe REST-ish APIs in the same way you can use OpenAPI, most people prefer to combine the two.
Typically, developers embed Taxi metadata inside existing OpenAPI specs, to get the best of both worlds. This lets you use OpenAPI to describe the capabilities of an API, and Taxi metadata describes how the inputs and data returned relate to other systems in your ecosystem.
In comparing Taxi and OpenAPI, there’s some areas where Taxi is stronger:
- Readability & Writability - you can easily sketch a Taxi spec by hand, because it’s a dedicated DSL, rather than YAML
- Taxi’s type system is much richer and more expressive. It has a full semantic type system, and compiler to validate that API contracts are correct.
- Taxi is better at describing how data relates between APIs - OpenAPI’s goal is to describe a single API, not the relationship between multiple APIs
- Taxi’s documentation goals are broader than just HTTP APIs, it includes Message queues, Serverless functions, Databases, etc.
But, in most other areas other OpenAPI is stronger:
- It’s the defacto standard - pretty much everyone understands what OpenAPI is.
- The tooling ecosystem is awesome.
- It’s more mature, having had the benefit of thousands of developers collaborating for years.
​Taxi vs Protobuf / Avro
Just like with OpenAPI, Taxi typically doesn’t replace Protobuf / Avro, but is used alongside it.
Importantly, Protobuf and Avro are also an encoding specification - they document how payloads are serialized to bytes. Taxi does not attempt to be a serialization protocol.
Learn more about how to embed Taxi metadata in your Protobuf and Avro specs.
​Taxi vs GraphQL
Taxi (and espeically the query language - TaxiQL) and GraphQL share similar goals - providing a single entrypoint for composing multiple APIs together.
However, there are subtle, but key differences in their approach:
​TaxiQL doesn't need resolvers
One of the goals of Taxi is to allow software to automate integration between systems, without engineers having to write glue code. It’s semantic types become the links between data sources.
In comparison, GraphQL relies on resolvers to code how to stitch together APIs. These resolvers need to be maintained, such that breaking changes in upstream systems need to be propagated into resolver code.
​TaxiQL is protocol agnostic
GraphQL federation requires “GraphQL everywhere”. Taxi aims to work with the API specs you already have, and can bridge between OpenAPI, RAML, JsonSchema and Protobuf without requiring exisitng schemas to be replaced.
Taxi can also bridge between Databases, but - like GraphQL - that does require a Taxi schema in addition to the DDL. (We haven’t figured out a neat way to embed taxi metadata in DDL scripts. If you have ideas, we’d love to hear them).
​TaxiQL allows consumers to define their data contracts
GraphQL has a single schema, that consumers can cherry-pick fields from. This single schema can become difficult to refactor it, as all the consumers also need to change.
TaxiQL is designed to allow consumers to define the contract of data they want, and it’s up to the query engine to satisfy this contract. This means that as publisher contracts change, consumers remain decoupled, keeping cost-of-change low.
​Language Goals
As a language, Taxi focuses on:
- Readability - A familiar syntax that should be easy to write, and easy to understand.
- Expressiveness - Taxi should be able to describe the semantic meaning of your data, and rich quirky contracts of our APIs
- Typesafe - A strongly typed, expressive language, purpose-built for describing API operations & types
- Tooling - Taxi is intended to allow next-generation tooling integration - the syntax allows rich expression of what services can do (rather than just where to find them).
- Extensibility - Taxi allows you to refine and compose API schemas, adding context, annotations, and improving type signatures.
Taxi is used heavily to power Orbital - and the projects have influenced each other & evolved together. The expressiveness of Taxi allows Orbital to automate integration between services.
However, Taxi is intended to be a standalone tool, and is not coupled to Orbital. There’s lots of amazing things you can do with Taxi on it’s own.
​New to Taxi or Orbital?
It’s easy to adopt Taxi incrementally, meaning you can set it up alongside an existing solution (such as Swagger or XML schemas) and migrate functionality at your convenience.
In fact, Taxi is designed to complement those tools, and you can happily use Swagger or XSDs are you base schema, and then overlay semantic data using Taxi.