Check out v4 of the Fauna CLI

v4 of the Fauna CLI is now in beta.

The new version introduces enhancements to the developer experience, including an improved authentication workflow. To get started, check out the CLI v4 quick start.

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 create and manage schema using any of the following:

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 query, including the write to the document, fails.

  • The predicate returns a non-boolean non-null value. The query, 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 query, 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 query 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 query fails a check constraint, the Core HTTP API’s Query endpoint rejects the query and returns an error with the constraint_failure error code.

The error includes a constraint_failures array. The array contains information about the constraints 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 immediately after a write is registered in the query, before the write is committed. All check constraints for the collection are automatically evaluated on the new document state.

If any check constraint fails, the entire query transaction is aborted — no part of the query after the write is evaluated, and no changes from before the write take effect.

Check constraints evaluate against the pending document state. For example, if a check constraint includes an expression such as Collection.byField(doc.foo).count(), the query includes the document being written in its results.

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("111")!.update({ balance: -50 })
    constraint_failure: Failed to update document with id 111 in collection `Customer`.
    
    error: Failed to update document with id 111 in collection `Customer`.
    constraint failures:
      Document failed check constraint `hasFunds`
    at *query*:1:29
      |
    1 | Customer.byId("111")!.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!