FSL collection schema: Field definitions

Learn: Field definitions

Field definitions define fields for a collection’s documents.

You define field definitions as part of an FSL collection schema. Field definitions are part of a collection’s document type definition.

collection Order {
  // Field definitions.
  // Define the structure of the collection's documents.
  description: String? // Equivalent to `status: String | Null`
  customer: Ref<Customer>
  status: "cart" | "processing" | "shipped" | "delivered"
  createdAt: Time = Time.now()
  ...
}

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 fields field contains FQL versions of the collection’s field definitions.

FSL syntax

<fieldName>: <types>[ = <defaultValue>]

Name

fieldName String Required

Document field name. A top-level field name must be a valid identifier.

Use a top-level wildcard (*) constraint to specify accepted data types for ad hoc fields. See Wildcard constraints.

Properties

Property Type Required Description

<types>

String

Yes

Accepted data types for the field. Separate multiple types by |.

Types must be persistable. Use Any to accept any persistable value.

Use Ref<CollectionName> to accept a document reference. References to named system collection documents are not supported.

Use the Union type to enumerate accepted field values. See Enumerated field values.

The ? (nullable) type annotation indicates the field accepts Null values. ? is equivalent to | Null.

Fields that accept Null are not guaranteed to exist in every document of the collection.

<defaultValue>

String

Default field value. Used when the field is missing during document creation or replacement.

Can be an FQL expression. The expression can have no effect other than to:

Fauna evaluates the expression at write time.

You can use a document reference as a default value. References to named system collection documents are not supported. See Default to a document reference.

You can’t use another field’s value as a default value.

The default value is not applied if an explicit null value is provided or during document updates.

Wildcard constraints

When you add field definitions to a collection, the collection’s documents can only contain the defined fields. To accept arbitrary ad hoc fields, add a wildcard (*) constraint:

collection Order {
  status: String?

  // Wildcard constraint.
  // Allows arbitrary ad hoc fields.
  *: Any
}

A wildcard constraint is part of a collection’s document type definition. You can’t specify a default value for a wildcard constraint.

A collection can only have one top-level wildcard constraint. If present, the top-level wildcard constraint’s definition must be *: Any.

Schemaless by default

If a collection has no field definitions, it implicitly accepts ad hoc fields of any type. This is equivalent to:

collection Order {
  // Contains no field definitions
  *: Any
}

This means the collection is schemaless. The collection’s documents can contain any field.

Permissive document type

If a collection has both field definitions and a wildcard constraint, it has a permissive document type. The collection’s documents can contain ad hoc fields. However, fields must conform to the structure of their definitions.

collection Order {
  // Contains field definitions and a wildcard constraint
  status: String?
  *: Any
}

Strict document type

If a collection has field definitions but doesn’t have a wildcard constraint, it does not accept documents with ad hoc fields. This is called a strict document type.

collection Order {
  // Contains no wildcard constraint
  status: String?
}

Nested wildcard constraints

You can include a wildcard constraint in an object field definition to accept ad hoc fields in the object:

collection Product {
  metadata: {
    name: String?
    // The `metadata` object can contain ad hoc fields
    // of any type.
    *: Any
  }
}

You can restrict the accepted data types of ad hoc fields in the object:

collection Product {
  metadata: {
    name: String?
    // Only accept ad hoc fields with `String` or `Int` values.
    *: String | Int
  }
}

Nested field migrations with wildcard constraints

You can’t run migrations on a nested field that has a neighboring wildcard constraint:

collection Product {
  metadata: {
    // The `metadata` object contains a wildcard constraint.
    // You can't run migrations on `name` or other
    // neighboring fields in `metadata`.
    name: String?
    *: Any
  }
}

The add_wilcard, remove_wildcard, and move_wildcard migration statements only support top-level wildcard constraints, not nested wildcard constraints. These statements let you safely handle conflicts between ad hoc and defined fields.

Document TTLs default

The document_ttls field to controls whether you can write to the ttl field for collection documents.

If a collection schema contains field definitions, the schema’s document_ttls field defaults to false. Otherwise, document_ttls defaults to true.

See Enable or disable ttl writes

Examples

Arrays

Use Array<…​> to accept Array values:

collection Product {
  categories: Array<String>
}

To accept an object Array:

collection Customer {
  addresses: Array<{
    street: String
    city: String
    state: String
    zipCode: String
  }>
}

You can’t run migrations on nested fields within an object Array. Otherwise, you define the object in the Array like an object field. See Objects.

Default to today’s date

Use Date.today() to set today’s date as a default value:

collection Product {
  creationDate: Date = Date.today()
}

Fauna evaluates the expression at write time.

Default to the current time

Use Time.now() to set the current time as a default value:

collection Product {
  creationTime: Time = Time.now()
}

Fauna evaluates the expression at write time.

Default to a unique ID

Use newId() to use a unique ID as the default value. You must cast the ID to a String using toString():

collection Product {
  productId: String = newId().toString()
}

If used, Fauna generates a unique ID value for each document.

Default to a document reference

You can use a document reference as a default value:

collection Product {
  // `category` defaults to a `Category` document reference
  // Replace `400684606016192545` with a `Category` document ID.
  category: Ref<Category> = Category("400684606016192545")
}

Fauna doesn’t guarantee the document exists. You can’t fetch the document using an FQL expression.

References to named system collection documents are not supported.

Document relationships

Use Ref<CollectionName> to accept a document reference as a data type:

collection Product {
  // Accepts a reference to a `Category` collection
  // document or `null`.
  category: Ref<Category>?
}

Documents may contain references to documents that don’t exist. These are called dangling references.

You can’t delete a collection that’s referenced by a field definition. See Drop a document reference field.

References for named system collection documents are not supported.

Enumerated field values

To accept enumerated values:

collection Customer {
  status: "silver" | "gold" | "platinum"?
}

Objects

To define a field containing an object:

collection Customer {
  // `address` is an object.
  address: {
    // `street` and the other fields are
    // nested fields in the `address` object.
    street: String
    city: String
    state: String
    postalCode: String
    country: String
  }
}

You can nest objects within other objects:

collection Customer {
  address: {
    street: String
    city: String
    state: String
    postalCode: String
    country: {
      // `countryCode` is nested in the
      // `country` field. The `country`
      // field is nested in the `address` field.
      countryCode:
    }
  }
}

Delimit nested fields

You can use commas to optionally delimit an object’s nested fields:

collection Customer {
  address: {
    street: String,
    city: String,
    state: String,
    postalCode: String,
    country: String
  }
}

Nested field names

A top-level field name must be a valid identifier. A nested field name can be any valid string, including an identifier:

collection Product {
  // `metadata` is a top-level field name and must be an identifier.
  metadata: {
    // `internal description` is a nested field name.
    // The name contains a space and is not a valid
    // identifier.
    "internal description": String
  }
}

You can access non-identifier field names using bracket notation:

// Create a `Product` document using the previous schema.
let product = Product.create({
  metadata: {
    "internal description": "Fresh key limes"
  }
})

// Access the document's nested `internal description`
// field using bracket notation.
product.metadata["internal description"]

Defaults for nested fields

You can provide default values for fields in an object:

collection Customer {
  address: {
    // If `address.street` is not provided,
    // use a default value of "unknown street".
    street: String = "unknown street"
    // If `address.city` is not provided,
    // use a default value of "unknown city".
    city: String = "unknown city"
    state: String
    postalCode: String
    country: String
  }
}

Default for an entire object

You can provide a default object for an object field. The default is used only if the entire object is omitted:

collection Customer {
  // If no `address` object is provided, use the default object.
  address: {
    street: String
    city: String
    state: String?
    postalCode: String?
    country: String?
  } = {
    // The default object:
    street: "unknown street",
    city: "unknown city"
  }
}

You can’t specify both a default object and nested field defaults.

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!