Skip to main content
A link type is an ordinary table whose rows associate two records. You declare which two fields are the link’s endpoints and how many links each record may take part in, and Bijection enforces those bounds on every transaction.
bijection/schema.ts
Each customer_invoices document is one link between a customer and an invoice. A customer can have any number of invoices, and an invoice belongs to at most one customer. Call .link on a table definition with an object that has exactly two entries. Each key is an endpoint field and each value is its cardinality, [minimum, maximum]:
  • The endpoint fields must be required, top-level v.id(...) fields of the table. System tables can’t be endpoints.
  • The other fields of the table, such as note above, are ordinary data about the link. An ID field that isn’t named in .link isn’t an endpoint.
  • A table can have one .link declaration.
.link can’t be used on a view, on a table filled by an integration, or on a table with a staged validator.

Cardinality

An endpoint’s bounds count the live links that each document of the endpoint’s table takes part in. In the example, invoice_id: [0, 1] means every invoice is in zero or one customer_invoices link. "many" means no upper bound. Bounds are non-negative integers with the minimum no greater than the maximum. Put a bound on the endpoint it constrains. “An invoice has at most one customer” is a bound on invoice_id, not on customer_id.

Minimums

A minimum applies to every document in the endpoint’s table, including documents that have no links yet. With invoice_id: [1, 1], an invoice can’t exist without its link, so a mutation that inserts an invoice must also insert its link:
bijection/invoices.ts
Use a minimum of 0 when records can legitimately exist before they’re associated, and track unassigned records with an ordinary query. Links are written with ordinary mutations:
  • Create a link with db.insert.
  • Change its data with db.patch or db.replace, as long as both endpoint fields keep their values.
  • End a link with db.delete.
Endpoints can’t be changed in place. To move an invoice to another customer, end the old link and create the new one in the same mutation:
bijection/invoices.ts
A pair of records has at most one live link. If you end a link and later insert the same pair again, the restored link gets back its original _id and _creationTime.

Checked on the final state

Bijection checks links against the state the whole transaction leaves behind, not after each write. That’s why reassign can briefly leave the invoice without a link, and why a mutation can delete a customer together with all its links. It also covers writes that don’t touch the link table: deleting a customer that still has live links fails, even though no link was written. A transaction that breaks a link rule fails with one of these errors, and none of its writes are committed: Declaring a link type, or making its bounds stricter, also checks the documents already in your tables when you deploy.

Indexes

To check links efficiently, a link type needs two indexes. Taking the endpoint fields in alphabetical order, one index begins with both fields and the other begins with the second field. Bijection adds these as by_link_pair and by_link_endpoint_2 unless you’ve declared indexes that begin with the same fields, in which case it reuses yours. Declare the indexes you query yourself, so that they have meaningful names and appear in your generated types. In the example at the top of this page, by_customer_invoice begins with both endpoints and by_invoice begins with invoice_id, so Bijection adds no indexes of its own. A link type is a table, so you read links with ordinary indexed queries, in either direction:
bijection/invoices.ts
.unique() is safe in customerOf because invoice_id has a maximum of 1. You can also traverse links inside a view. Because an invoice has at most one link, a view keyed by invoices can join it with the default cardinality: "one":
bijection/schema.ts
In the other direction, group the links by customer to count each customer’s invoices:
bijection/schema.ts
Reads of links, directly or through views, follow the ordinary consistency and reactivity rules: a query that follows a link updates when the link is created, ended or reassigned.
Links in the console schema view are in beta.
The Schema page of the console draws each declared link as edges from the link table to its two endpoint tables. Selecting a table lists the links it takes part in, the table that declares each one, and each endpoint’s bounds, written like 0..1, 1 or 0..*. For a table that declares a link, the diagram shows the declared endpoints instead of guessing edges from its ID fields.