FSL collection schema: Check constraint definitions

Learn: Check constraints

A check constraint ensures a document field’s values meet a pre-defined rule.

You write the rule as a read-only FQL predicate. Fauna only allows document writes if the predicate evaluates to true. For example, you can check that field values are in an allowed range.

You define check constraints as part of an FSL collection schema:

collection Product {
  ...
  // Check constraint. `Product` document writes are
  // only allowed if the `stock` field value is zero or greater.
  check stockIsValid (product => product.stock >= 0)
  ...
}

You can use the Fauna Dashboard or the Fauna CLI to push schema changes to Fauna.

Fauna stores each collection schema as an FQL document in the Collection system collection. The Collection document’s constraints field contains FQL versions of the collection’s check constraint definitions.

FSL syntax

check <constraintName> (predicateBody: (<doc>: Document) => Boolean | Null)

Properties

Parameter Type Required Description

constraintName

String

Yes

Check constraint predicate name, which must be unique for the collection.

The constraintName identifies the constraint in error messages.

predicateBody

Function

Yes

An anonymous, read-only FQL function that evaluates to true, false, or null. If false or null, Fauna rejects the write. Accepts shorthand syntax.

The function is passed one argument: an object containing the document to write.

The function runs using the built-in server role’s privileges.

Discussion

Check constraints can read, call UDFs, and use computed fields but can’t write. A constraint that tries to write results in an evaluation error.

Multiple check constraints can be defined for the collection and a check constraint can evaluate multiple document fields, or multiple values or ranges for a field..

When a document is created, updated, or replaced, all defined check constraints are evaluated by applying the check constraint, which can have four possible outcomes:

  • The predicate returns true. The write to the document is allowed.

  • The predicate returns false or null. The transaction, including the write to the document, fails.

  • The predicate returns a non-boolean non-null value. The transaction, including the write to the document, fails.

  • The predicate fails to evaluate, possibly caused by divide-by-zero, stack overflow, timeout errors, or abort() is called. The transaction, including the write to the document, fails. If a check constraint is failed by calling abort, the argument to abort is returned as if the transaction were failed by calling abort in the query.

Check constraints aren’t applied retroactively to existing data in the database.

Check constraints should be defined defensively to handle the conditions that the field might not exist or might have a different type.

The check constraint is evaluated on the document version when the document is written, not what currently exists. When a check constraint runs on an update operation, it operates on the document with the update applied. When a check constraint runs on a replace operation, it operates on the version that is replacing the original.

Check constraints aren’t evaluated on delete queries.

Check constraint errors

When a transaction fails a check constraints, the Core HTTP API’s Query endpoint rejects the transaction and returns an error with the constraint_failure error code and a 400 HTTP status code.

The error includes a constraint_failures object containing information about the constraint that triggered the error:

{
  "error": {
    "code": "constraint_failure",
    "message": "Failed to create document in collection `Product`.",
    "constraint_failures": [
      {
        "paths": [],
        "message": "Document failed check constraint `stockIsValid`"
      }
    ]
  },
  ...
}

Fauna’s client drivers include classes for constraint errors:

Check constraint behavior

Check constraints are evaluated instantly after the write is registered in the transaction. The following query:

CC.create({ a: 0, b: 1 }).a

Effectively, evaluates like:

let doc = CC.create({ a: 0, b: 1 })
if (!x(doc)) abort()
if (!noTripleAs(doc)) abort()
doc.a

where x(doc) means the check constraint x is evaluated on doc. If a check constraint fails, no part of the query after the write is evaluated, and no part before the write takes effect because it is a transaction. Another implication is that check constraints see the document write. For example, this means that CC.byA(doc.a).count() in the noTripleAs check evaluates to 1 because the check constraint sees the write of the document to the CC collection.

Examples

Basic example

  1. Given a hasFunds check constraint for the Customer collection, which validates that a customer balance is greater than or equal to zero, and a customer who currently has a positive balance:

    collection Customer {
      ...
      check hasFunds((doc) => doc.balance >= 0)
      ...
    }

    And given the following Customer collection document:

    {
      id: "388093102421704737",
      coll: Customer,
      ts: Time("2099-06-25T12:14:29.440Z"),
      name: "Alice Appleseed",
      email: "alice.appleseed@example.com",
      ...,
      balance: 21
  2. Verify check constraint validation by trying to set the balance to less than zero:

    Customer.byId("388093102421704737")!.update({ balance: -50 })
    constraint_failure
    
    error: Failed to update document with id 388093102421704737 in collection `Customer`.
    constraint failures:
      Document failed check constraint `hasFunds`
    at *query*:1:44
      |
    1 | Customer.byId("388093102421704737")!.update({balance: -50})
      |                                            ^^^^^^^^^^^^^^^^
      |

    The hasFunds check constraint catches the constraint violation. Notice that the error message includes the name of the check constraint predicate.

Shorthand syntax

You can use shorthand syntax in a check constraint’s predicate function. For example:

collection Customer {
  ...
  check isAdult(.age >= 18)
  ...
}

Or:

collection Customer {
  ...
  check isAdult(doc => doc.age >= 18)
  ...
}

Is equivalent to:

collection Customer {
  ...
  check isAdult((doc) => doc.age >= 18)
  ...
}

Is this article helpful? 

Tell Fauna how the article can be improved:
Visit Fauna's forums or email docs@fauna.com

Thank you for your feedback!