FQL v4 will be decommissioned on June 30, 2025. Ensure that you complete your migration from FQL v4 to FQL v10 by that date.

For more details, see the v4 EOL announcement and migration guide. Contact support@fauna.com with any questions.

Migrate to FQL v10

See also: v10 migration FAQ

FQL v10 is the latest version of FQL that simplifies the data model and provides a more powerful and flexible query language. FQL v10 is backward-compatible with FQL v4. You don’t have to change or migrate your data to use v10. You can incrementally transition v4 applications to v10.

This guide covers:

  • Notable changes between FQL v4 and v10

  • How to transition v4 applications to v10

Document improvements

FQL v10 includes the following improvements to documents:

Data field

FQL v10 improves developer experience by automatically projecting user-defined fields as top-level fields of the documents. In v4, user-defined fields are always nested in a document’s data field.

{
  "ref": Ref(Collection("Customer"),
    "399433749825060941"),
  "ts": 1717188558387000,
  "data": {
    "firstName": "Alice",
    "lastName": "Appleseed",
    "address": {
      "street": "87856 Mendota Court",
      "city": "Washington",
      "state": "DC",
      "zipCode": "20220"
    }
  }
}
{
  coll: Customer,
  id: "399433749825060941",
  ts: Time("2024-05-31T20:49:18.387Z"),
  firstName: "Alice",
  lastName: "Appleseed",
  address: {
    street: "87856 Mendota Court",
    city: "Washington",
    state: "DC",
    zipCode: "20220"
  }
}
 
 

This top-level projection of user-defined fields doesn’t change how the underlying document is structured or stored. The new document format is only presented when using the v10 interface. Your existing v4-based applications can continue to work with no changes.

FQL v10 and v4-based applications work on the same documents concurrently without breaking each other. Your FQL v4 queries continue to reference and write to fields nested in the data field.

Metadata fields

In v10, all documents contain the following top-level metadata fields:

  • coll: The name of the document’s collection

  • id: A string-encoded 64-bit integer identifier that’s unique to the document in the collection

  • ts: A timestamp of the latest write to the document

v10 documents can also include an optional ttl timestamp. A document is permanently deleted on its ttl.

Avoid conflicts with reserved fields

In v10, data and metadata field names are reserved.

To avoid conflicts, you can use the following methods to create and modify documents that may contain reserved field names:

These methods safely nest values for fields with reserved names in the data field. For example:

Test.createData({
  data: {
    foo: "bar"
  }
})

Result:

{
  id: "398154525228138529"
  coll: Test,
  ts: Time("2024-05-17T17:56:34.670Z"),
  data: {
    data: {
      foo: "bar"
    }
  }
}

User-defined functions

You may want to migrate your v4 code incrementally by mixing v4 queries with v10 queries. You can use user-defined functions (UDFs) for this purpose.

You can call v4 UDFs from v10 FQL queries and vice versa. This lets you mix FQL v10 queries into a v4 application by encapsulating the v10 query in a UDF and calling that UDF in your v4 queries.

Calling v4 from v10 (or the other way around) is convenient for incremental migration. It is not recommended for long-term use. We recommend you migrate your UDFs to v10.

The ability to call v4 UDFs in v10 is a temporary convenience provided to users as a means to incrementally migrate. When v4 reaches its end of life (EOL), you must migrate your UDFs to v10.

Call a v4 UDF in a v10 query

To create a v4 UDF:

CreateFunction({
  name: "Increment",
  body: Query(Lambda("number", Add(1, Var("number"))))
})

To call the v4 UDF in a v10 query:

// Calls the v4 `Increment` UDF with the argument `1`
let num = Increment(1)
num

Call a v10 UDF in a v4 query

In v10, you can define UDFs using Fauna Schema Language (FSL):

function getOrdersByCustomer(name, status) {
  Order.where(.customer.name == name && .status == status)
}

To call the v10 UDF in a v4 query:

// Calls the v10 `getOrdersByCustomer` UDF
Call('getOrdersByCustomer', 'Gregor Samsa')

Additional resources

For more information on using UDFs in FQL v10, see:

Indexes

In v4, indexes were a top-level schema object, separate from collections. In v10, indexes are part of a collection’s schema.

To use a v4 index in FQL v10, wrap the indexed query in a v4 UDF, and call the UDF in FQL v10.

For example, if you have a v4 index named product_by_name, you can not call it directly in FQL v10. You must first wrap the v4 product_by_name index in a v4 UDF:

CreateFunction({
  name: "productByName",
  body: Query(
    Lambda(
      ["name"],
      Select(
        "data",
        Paginate(Match(Index("product_by_name"), Var("name")))
      )
    )
  )
})

Then call the v4 productByName UDF in a v10 query:

productByName("Apple Macbook Pro")

Similarly, FQL v10 indexes can’t be called directly from FQL v4 queries unless they are in a UDF.

Indexes in FQL v10 are full indexes whereas v4 indexes are sparse indexes. For example, if you declared a v4 index with a value field foo, in FQL v4 there is an entry in the index for every document that has a non-null foo value. In FQL v10, all documents are indexed, including those that have a null foo value.

In FQL v4, terms and values treat arrays as a multi-valued attribute (MVA) such that when a term targets a document field or index binding result that is an array, one index entry per array item is created. In FQL v10, you must explicitly state that a term or a value is an MVA. See index definitions.

FQL v10 computed fields are generally equivalent to FQL v4 index binding, and are declared inside the collection instead of in the index. See computed field definitions.

Reserved words and field names

As described previously, the document metadata id, coll, and ts keywords are reserved. See Reserved words. If your existing documents use these field names, they’re nested in the data field of your documents to avoid colliding with the FQL v10 reserved key names. For example, notice how the id field in the following FQL v4 document is nested in data, avoiding collision on the reserved key name:

{
  "ref": Ref(Collection("Customer"),
    "399433749825060941"),
  "ts": 1717188956715000,
  "data": {
    "firstName": "Alice",
    "lastName": "Appleseed",
    "address": {
      "street": "87856 Mendota Court",
      "city": "Washington",
      "state": "DC",
      "zipCode": "20220"
    },
    "id": "12345",
  }
}
{
  coll: Customer,
  id: "399433749825060941",
  ts: Time("2024-05-31T20:55:56.715Z"),
  firstName: "Alice",
  lastName: "Appleseed",
  address: {
    street: "87856 Mendota Court",
    city: "Washington",
    state: "DC",
    zipCode: "20220"
  },
  data: {
    id: "12345"
  }
}

Reserved collection names

To avoid naming conflicts with system collections and system entities, FQL v10 doesn’t allow you to name top-level entities, such as collections and UDFs, with a reserved name. Where a user-defined collection or function has a conflicting name, you can assign collections and UDFs an alias that makes that resource available in the FQL v10 environment. For example, when migrating from FQL v4 with a Collection named Collection, you must add an alias to the collection to use it in their FQL v10 environment.

// adding an alias to a Collection named Collection
Collection.byName("Collection")!.update(
  {
    alias: "Products"
  }
)

// using that collection in a subsequent query
Products.firstWhere(.name == "Hard Anodised 12 Kadhai")
Products.create(...)

If the original name is non-conflicting, the Collection/UDF is available as its name and as the aliased name. If it is conflicting, the Collection/UDF is available only under the aliased name.

Referenced collections

You can no longer delete a collection that is referenced by a Role or Key document. This is true of roles or keys created in v4 or v10. You must reconfigure or remove the Role or Key and then delete the collection.

Security-related definitions

You must convert definitions for access providers and user-defined roles to v10 before the v4 EOL.

In v10, you typically define access providers and roles using Fauna Schema Language (FSL) schema.

Access providers

To convert a v4 access provider definition to v10, open the definition as FSL in the Fauna Dashboard and save the result.

User-defined roles

You can authorize v10 transactions using v4 roles. This lets you incrementally migrate v4 role definitions to v10.

You can’t use FQL v4 methods to create v10 role definitions. The structure of role definitions has changed significantly from v4 to v10.

Major differences are outlined below. For more information, see You should review the Role FSL schema documentation.

Role name

In v10, the role name must be a valid identifier.

Resources for membership and privileges

In v4, the resource field was a reference reference. In v10 FSL, the <resource> is a string:

privileges: [
 {
    resource: Ref("Store"),
    actions: {
      read: true,
      write: false,
      create: false,
      delete: false
    }
  }
role manager {

  privileges Store {
    read
    write
    create
    delete
  }
}
 

Predicates

In v4, predicates were defined as Query structures. In v10, predicates are anonymous FQL functions.

Privilege actions

v10 does not support the history_write action for privileges.

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!